This builder allows for configuration of the {@link Process}'s output and error streams as well as the
12 | * {@link Executor} to use when starting the shell process. The output and error streams may be either be redirected
13 | * (using {@link java.lang.ProcessBuilder.Redirect}) or given an explicit {@link OutputStream}. You may also combine
14 | * the output and error streams via {@link #redirectErrorStream(boolean) redirectErrorStream(true)}.
15 | *
16 | *
Use {@link #start()} to execute the command, and then use {@link Process#waitFor()} to wait for the command to
17 | * complete.
18 | *
19 | *
Warning: If stdout and stderr are both set to {@link java.lang.ProcessBuilder.Redirect#PIPE} (the default),
20 | * you must read from their InputStreams ({@link Process#getInputStream()} and {@link Process#getErrorStream()})
21 | * concurrently. This requires having two separate threads to read the input streams separately. Otherwise,
22 | * the process may deadlock. To avoid using threads, you can use {@link #redirectErrorStream(boolean)}, in which case
23 | * you must read all output from {@link Process#getInputStream()} before calling {@link Process#waitFor()}:
24 | *
25 | *
{@code
26 | * Process process = jadbDevice.shellProcessBuilder("command")
27 | * .redirectErrorStream(errorStream)
28 | * .start();
29 | * String stdoutAndStderr = new Scanner(process.getInputStream()).useDelimiter("\\A").next();
30 | * int exitCode = process.waitFor();
31 | * }
32 | *
33 | * You can also use one of the {@code redirectOutput} methods to have the output automatically redirected. For example,
34 | * to buffer all of stdout and stderr separately, you can use {@link java.io.ByteArrayOutputStream}:
35 | *
36 | *
For Lollipop and later see: {@link #execute(String, String...)}
87 | *
88 | * @param command main command to run. E.g. "ls"
89 | * @param args arguments to the command.
90 | * @return combined stdout/stderr stream.
91 | * @throws IOException
92 | * @throws JadbException
93 | */
94 | public InputStream executeShell(String command, String... args) throws IOException, JadbException {
95 | Transport transport = getTransport();
96 | StringBuilder shellLine = buildCmdLine(command, args);
97 | send(transport, "shell:" + shellLine.toString());
98 | return new AdbFilterInputStream(new BufferedInputStream(transport.getInputStream()));
99 | }
100 |
101 | /**
102 | *
103 | * @deprecated Use InputStream executeShell(String command, String... args) method instead. Together with
104 | * Stream.copy(in, out), it is possible to achieve the same effect.
105 | */
106 | @Deprecated
107 | public void executeShell(OutputStream output, String command, String... args) throws IOException, JadbException {
108 | try (Transport transport = getTransport()) {
109 | StringBuilder shellLine = buildCmdLine(command, args);
110 | send(transport, "shell:" + shellLine.toString());
111 | if (output == null)
112 | return;
113 |
114 | AdbFilterOutputStream out = new AdbFilterOutputStream(output);
115 | transport.readResponseTo(out);
116 | }
117 | }
118 |
119 | /**
Execute a shell command.
120 | *
121 | *
This method supports separate stdin, stdout, and stderr streams, as well as a return code. The shell command
122 | * is not executed until calling {@link ShellProcessBuilder#start()}, which returns a {@link Process}.
123 | *
124 | * @param command main command to run, e.g. "screencap"
125 | * @param args arguments to the command, e.g. "-p".
126 | * @return a {@link ShellProcessBuilder}
127 | */
128 | public ShellProcessBuilder shellProcessBuilder(String command, String... args) {
129 | return new ShellProcessBuilder(this, buildCmdLine(command, args).toString());
130 | }
131 |
132 | /**
Execute a command with raw binary output.
133 | *
134 | *
Support for this command was added in Lollipop (Android 5.0), and is the recommended way to transmit binary
135 | * data with that version or later. For earlier versions of Android, use
136 | * {@link #executeShell(String, String...)}.
137 | *
138 | * @param command main command to run, e.g. "screencap"
139 | * @param args arguments to the command, e.g. "-p".
140 | * @return combined stdout/stderr stream.
141 | * @throws IOException
142 | * @throws JadbException
143 | */
144 | public InputStream execute(String command, String... args) throws IOException, JadbException {
145 | Transport transport = getTransport();
146 | StringBuilder shellLine = buildCmdLine(command, args);
147 | send(transport, "exec:" + shellLine.toString());
148 | return new BufferedInputStream(transport.getInputStream());
149 | }
150 |
151 | /**
152 | * Builds a command line string from the command and its arguments.
153 | *
154 | * @param command the command.
155 | * @param args the list of arguments.
156 | * @return the command line.
157 | */
158 | private StringBuilder buildCmdLine(String command, String... args) {
159 | StringBuilder shellLine = new StringBuilder(command);
160 | for (String arg : args) {
161 | shellLine.append(" ");
162 | shellLine.append(Bash.quote(arg));
163 | }
164 | return shellLine;
165 | }
166 |
167 | /**
168 | * Enable tcpip on the default port (5555)
169 | *
170 | * @return success or failure
171 | */
172 | public void enableAdbOverTCP() throws IOException, JadbException {
173 | enableAdbOverTCP(DEFAULT_TCPIP_PORT);
174 | }
175 |
176 | /**
177 | * Enable tcpip on a specific port
178 | *
179 | * @param port for the device to bind on
180 | *
181 | * @return success or failure
182 | */
183 | public void enableAdbOverTCP(int port) throws IOException, JadbException {
184 | try (Transport transport = getTransport()) {
185 | send(transport, String.format("tcpip:%d", port));
186 | }
187 | }
188 |
189 | public List list(String remotePath) throws IOException, JadbException {
190 | try (Transport transport = getTransport()) {
191 | SyncTransport sync = transport.startSync();
192 | sync.send("LIST", remotePath);
193 |
194 | List result = new ArrayList<>();
195 | for (RemoteFileRecord dent = sync.readDirectoryEntry(); dent != RemoteFileRecord.DONE; dent = sync.readDirectoryEntry()) {
196 | result.add(dent);
197 | }
198 | return result;
199 | }
200 | }
201 |
202 | public void push(InputStream source, long lastModified, int mode, RemoteFile remote) throws IOException, JadbException {
203 | try (Transport transport = getTransport()) {
204 | SyncTransport sync = transport.startSync();
205 | sync.send("SEND", remote.getPath() + "," + mode);
206 |
207 | sync.sendStream(source);
208 |
209 | sync.sendStatus("DONE", (int) lastModified);
210 | sync.verifyStatus();
211 | }
212 | }
213 |
214 | public void push(File local, RemoteFile remote) throws IOException, JadbException {
215 | try (FileInputStream fileStream = new FileInputStream(local)) {
216 | push(fileStream, TimeUnit.MILLISECONDS.toSeconds(local.lastModified()), DEFAULT_MODE, remote);
217 | }
218 | }
219 |
220 | public void pull(RemoteFile remote, OutputStream destination) throws IOException, JadbException {
221 | try (Transport transport = getTransport()) {
222 | SyncTransport sync = transport.startSync();
223 | sync.send("RECV", remote.getPath());
224 |
225 | sync.readChunksTo(destination);
226 | }
227 | }
228 |
229 | public void pull(RemoteFile remote, File local) throws IOException, JadbException {
230 | try (FileOutputStream fileStream = new FileOutputStream(local)) {
231 | pull(remote, fileStream);
232 | }
233 | }
234 |
235 | private void send(Transport transport, String command) throws IOException, JadbException {
236 | transport.send(command);
237 | transport.verifyResponse();
238 | }
239 |
240 | @Override
241 | public String toString() {
242 | return "Android Device with serial " + serial;
243 | }
244 |
245 | @Override
246 | public int hashCode() {
247 | final int prime = 31;
248 | int result = 1;
249 | result = prime * result + ((serial == null) ? 0 : serial.hashCode());
250 | return result;
251 | }
252 |
253 | @Override
254 | public boolean equals(Object obj) {
255 | if (this == obj)
256 | return true;
257 | if (obj == null)
258 | return false;
259 | if (getClass() != obj.getClass())
260 | return false;
261 | JadbDevice other = (JadbDevice) obj;
262 | if (serial == null) {
263 | return other.serial == null;
264 | }
265 | return serial.equals(other.serial);
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/test/se/vidstige/jadb/test/fakes/FakeAdbServer.java:
--------------------------------------------------------------------------------
1 | package se.vidstige.jadb.test.fakes;
2 |
3 | import se.vidstige.jadb.JadbException;
4 | import se.vidstige.jadb.RemoteFile;
5 | import se.vidstige.jadb.server.AdbDeviceResponder;
6 | import se.vidstige.jadb.server.AdbResponder;
7 | import se.vidstige.jadb.server.AdbServer;
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.DataInput;
11 | import java.io.DataOutputStream;
12 | import java.io.IOException;
13 | import java.net.ProtocolException;
14 | import java.nio.charset.StandardCharsets;
15 | import java.util.ArrayList;
16 | import java.util.Collections;
17 | import java.util.List;
18 |
19 | /**
20 | * Created by vidstige on 2014-03-20.
21 | */
22 | public class FakeAdbServer implements AdbResponder {
23 | private final AdbServer server;
24 | private final List devices = new ArrayList<>();
25 |
26 | public FakeAdbServer(int port) {
27 | server = new AdbServer(this, port);
28 | }
29 |
30 | public void start() throws InterruptedException {
31 | System.out.println("Starting fake on port " + server.getPort());
32 | server.start();
33 | }
34 |
35 | public void stop() throws IOException, InterruptedException {
36 | System.out.println("Stopping fake on port " + server.getPort());
37 | server.stop();
38 | }
39 |
40 | @Override
41 | public void onCommand(String command) {
42 | System.out.println("command: " + command);
43 | }
44 |
45 | @Override
46 | public int getVersion() {
47 | return 31;
48 | }
49 |
50 | public void add(String serial) {
51 | devices.add(new DeviceResponder(serial, "device"));
52 | }
53 |
54 | public void add(String serial, String type) {
55 | devices.add(new DeviceResponder(serial, type));
56 | }
57 |
58 | public void verifyExpectations() {
59 | for (DeviceResponder d : devices)
60 | d.verifyExpectations();
61 | }
62 |
63 | public interface ExpectationBuilder {
64 | void failWith(String message);
65 |
66 | void withContent(byte[] content);
67 |
68 | void withContent(String content);
69 | }
70 |
71 | private DeviceResponder findBySerial(String serial) {
72 | for (DeviceResponder d : devices) {
73 | if (d.getSerial().equals(serial)) return d;
74 | }
75 | return null;
76 | }
77 |
78 | public ExpectationBuilder expectPush(String serial, RemoteFile path) {
79 | return findBySerial(serial).expectPush(path);
80 | }
81 |
82 | public ExpectationBuilder expectPull(String serial, RemoteFile path) {
83 | return findBySerial(serial).expectPull(path);
84 | }
85 |
86 | public DeviceResponder.ShellExpectation expectShell(String serial, String commands) {
87 | return findBySerial(serial).expectShell(commands);
88 | }
89 |
90 | public void expectTcpip(String serial, Integer port) {
91 | findBySerial(serial).expectTcpip(port);
92 | }
93 |
94 | public DeviceResponder.ListExpectation expectList(String serial, String remotePath) {
95 | return findBySerial(serial).expectList(remotePath);
96 | }
97 |
98 | @Override
99 | public List getDevices() {
100 | return new ArrayList(devices);
101 | }
102 |
103 | private static class DeviceResponder implements AdbDeviceResponder {
104 | private final String serial;
105 | private final String type;
106 | private List fileExpectations = new ArrayList<>();
107 | private List shellExpectations = new ArrayList<>();
108 | private List listExpectations = new ArrayList<>();
109 | private List tcpipExpectations = new ArrayList<>();
110 |
111 | private DeviceResponder(String serial, String type) {
112 | this.serial = serial;
113 | this.type = type;
114 | }
115 |
116 | @Override
117 | public String getSerial() {
118 | return serial;
119 | }
120 |
121 | @Override
122 | public String getType() {
123 | return type;
124 | }
125 |
126 | @Override
127 | public void filePushed(RemoteFile path, int mode, ByteArrayOutputStream buffer) throws JadbException {
128 | for (FileExpectation fe : fileExpectations) {
129 | if (fe.matches(path)) {
130 | fileExpectations.remove(fe);
131 | fe.throwIfFail();
132 | fe.verifyContent(buffer.toByteArray());
133 | return;
134 | }
135 | }
136 | throw new JadbException("Unexpected push to device " + serial + " at " + path);
137 | }
138 |
139 | @Override
140 | public void filePulled(RemoteFile path, ByteArrayOutputStream buffer) throws JadbException, IOException {
141 | for (FileExpectation fe : fileExpectations) {
142 | if (fe.matches(path)) {
143 | fileExpectations.remove(fe);
144 | fe.throwIfFail();
145 | fe.returnFile(buffer);
146 | return;
147 | }
148 | }
149 | throw new JadbException("Unexpected push to device " + serial + " at " + path);
150 | }
151 |
152 | @Override
153 | public void shell(String command, DataOutputStream stdout, DataInput stdin) throws IOException {
154 | for (ShellExpectation se : shellExpectations) {
155 | if (se.matches(command)) {
156 | shellExpectations.remove(se);
157 | se.writeOutputTo(stdout);
158 | return;
159 | }
160 | }
161 | throw new ProtocolException("Unexpected shell to device " + serial + ": " + command);
162 | }
163 |
164 | @Override
165 | public void enableIpCommand(String port, DataOutputStream outputStream) throws IOException {
166 | for (Integer expectation : tcpipExpectations) {
167 | if (expectation == Integer.parseInt(port)) {
168 | tcpipExpectations.remove(expectation);
169 | return;
170 | }
171 | }
172 |
173 | throw new ProtocolException("Unexpected tcpip to device " + serial + ": (port) " + port);
174 |
175 | }
176 |
177 | @Override
178 | public List list(String path) throws IOException {
179 | for (ListExpectation le : listExpectations) {
180 | if (le.matches(path)) {
181 | listExpectations.remove(le);
182 | return le.getFiles();
183 | }
184 | }
185 | throw new ProtocolException("Unexpected list of device " + serial + " in dir " + path);
186 | }
187 |
188 | public void verifyExpectations() {
189 | for (FileExpectation expectation : fileExpectations) {
190 | org.junit.Assert.fail(expectation.toString());
191 | }
192 | for (ShellExpectation expectation : shellExpectations) {
193 | org.junit.Assert.fail(expectation.toString());
194 | }
195 | for (ListExpectation expectation : listExpectations) {
196 | org.junit.Assert.fail(expectation.toString());
197 | }
198 | for (int expectation : tcpipExpectations) {
199 | org.junit.Assert.fail("Expected tcp/ip on" + expectation);
200 | }
201 | }
202 |
203 | private static class FileExpectation implements ExpectationBuilder {
204 | private final RemoteFile path;
205 | private byte[] content;
206 | private String failMessage;
207 |
208 | public FileExpectation(RemoteFile path) {
209 | this.path = path;
210 | content = null;
211 | failMessage = null;
212 | }
213 |
214 | @Override
215 | public void failWith(String message) {
216 | failMessage = message;
217 | }
218 |
219 | @Override
220 | public void withContent(byte[] content) {
221 | this.content = content;
222 | }
223 |
224 | @Override
225 | public void withContent(String content) {
226 | this.content = content.getBytes(StandardCharsets.UTF_8);
227 | }
228 |
229 | public boolean matches(RemoteFile path) {
230 | return this.path.equals(path);
231 | }
232 |
233 | public void throwIfFail() throws JadbException {
234 | if (failMessage != null) throw new JadbException(failMessage);
235 | }
236 |
237 | public void verifyContent(byte[] content) {
238 | org.junit.Assert.assertArrayEquals(this.content, content);
239 | }
240 |
241 | public void returnFile(ByteArrayOutputStream buffer) throws IOException {
242 | buffer.write(content);
243 | }
244 |
245 | @Override
246 | public String toString() {
247 | return "Expected file " + path;
248 | }
249 | }
250 |
251 | public static class ShellExpectation {
252 | private final String command;
253 | private byte[] stdout;
254 |
255 | public ShellExpectation(String command) {
256 | this.command = command;
257 | }
258 |
259 | public boolean matches(String command) {
260 | return command.equals(this.command);
261 | }
262 |
263 | public void returns(String stdout) {
264 | this.stdout = stdout.getBytes(StandardCharsets.UTF_8);
265 | }
266 |
267 | public void writeOutputTo(DataOutputStream stdout) throws IOException {
268 | stdout.write(this.stdout);
269 | }
270 |
271 | @Override
272 | public String toString() {
273 | return "Expected shell " + command;
274 | }
275 | }
276 |
277 | public static class ListExpectation {
278 |
279 | private final String remotePath;
280 | private final List files = new ArrayList<>();
281 |
282 | public ListExpectation(String remotePath) {
283 | this.remotePath = remotePath;
284 | }
285 |
286 | public boolean matches(String remotePath) {
287 | return remotePath.equals(this.remotePath);
288 | }
289 |
290 | public ListExpectation withFile(String path, int size, int modifyTime) {
291 | files.add(new MockFileEntry(path, size, modifyTime, false));
292 | return this;
293 | }
294 |
295 | public ListExpectation withDir(String path, int modifyTime) {
296 | files.add(new MockFileEntry(path, -1, modifyTime, true));
297 | return this;
298 | }
299 |
300 | public List getFiles() {
301 | return Collections.unmodifiableList(files);
302 | }
303 |
304 | @Override
305 | public String toString() {
306 | return "Expected file list " + remotePath;
307 | }
308 |
309 | private static class MockFileEntry extends RemoteFile {
310 |
311 | private final int size;
312 | private final int modifyTime;
313 | private final boolean dir;
314 |
315 | MockFileEntry(String path, int size, int modifyTime, boolean dir) {
316 | super(path);
317 | this.size = size;
318 | this.modifyTime = modifyTime;
319 | this.dir = dir;
320 | }
321 |
322 | public int getSize() {
323 | return size;
324 | }
325 |
326 | public int getLastModified() {
327 | return modifyTime;
328 | }
329 |
330 | public boolean isDirectory() {
331 | return dir;
332 | }
333 |
334 | }
335 |
336 | }
337 |
338 | public ExpectationBuilder expectPush(RemoteFile path) {
339 | FileExpectation expectation = new FileExpectation(path);
340 | fileExpectations.add(expectation);
341 | return expectation;
342 | }
343 |
344 | public ExpectationBuilder expectPull(RemoteFile path) {
345 | FileExpectation expectation = new FileExpectation(path);
346 | fileExpectations.add(expectation);
347 | return expectation;
348 | }
349 |
350 | public ShellExpectation expectShell(String command) {
351 | ShellExpectation expectation = new ShellExpectation(command);
352 | shellExpectations.add(expectation);
353 | return expectation;
354 | }
355 |
356 | public ListExpectation expectList(String remotePath) {
357 | ListExpectation expectation = new ListExpectation(remotePath);
358 | listExpectations.add(expectation);
359 | return expectation;
360 | }
361 |
362 | public void expectTcpip(int port) {
363 | tcpipExpectations.add(port);
364 | }
365 | }
366 | }
367 |
--------------------------------------------------------------------------------