1 package com.android.ddmlib.testrunner;
2
3 import java.io.IOException;
4 import java.util.AbstractMap;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collection;
8 import java.util.List;
9 import java.util.Properties;
10 import java.util.Map.Entry;
11
12 import org.apache.commons.lang3.StringUtils;
13
14 import com.android.ddmlib.AdbCommandRejectedException;
15 import com.android.ddmlib.IDevice;
16 import com.android.ddmlib.Log;
17 import com.android.ddmlib.ShellCommandUnresponsiveException;
18 import com.android.ddmlib.TimeoutException;
19
20
21
22
23 public class UIAutomatorRemoteAndroidTestRunner
24 {
25
26 private IDevice mRemoteDevice;
27
28 private int mMaxTimeToOutputResponse = 0;
29 private String mRunName = null;
30
31
32 private List< Entry< String, String >> mArgList;
33 private InstrumentationResultParser mParser;
34 private final String jarFile;
35 private boolean noHup;
36 private Object dumpFilePath;
37
38 private static final String LOG_TAG = "RemoteAndroidTest";
39
40
41 private static final String CLASS_ARG_NAME = "class";
42 private static final String DEBUG_ARG_NAME = "debug";
43
44 public UIAutomatorRemoteAndroidTestRunner( String jarFile, IDevice remoteDevice )
45 {
46 this.jarFile = jarFile;
47 mRemoteDevice = remoteDevice;
48 mArgList = new ArrayList< Entry< String, String >>();
49 }
50
51
52
53
54
55 public void setTestClassOrMethods( String[] testClassOrMethods )
56 {
57 for ( String testClassOrMethod : testClassOrMethods )
58 {
59 addInstrumentationArg( CLASS_ARG_NAME, testClassOrMethod );
60 }
61 }
62
63
64
65
66 public void addInstrumentationArg( String name, String value )
67 {
68 if ( name == null || value == null )
69 {
70 throw new IllegalArgumentException( "name or value arguments cannot be null" );
71 }
72 mArgList.add( new AbstractMap.SimpleImmutableEntry< String, String >( name, value ) );
73 }
74
75
76
77
78
79 public void addBooleanArg( String name, boolean value )
80 {
81 addInstrumentationArg( name, Boolean.toString( value ) );
82 }
83
84
85
86
87
88
89
90
91
92 public void setDebug( boolean debug )
93 {
94 addBooleanArg( DEBUG_ARG_NAME, debug );
95 }
96
97 public void setNoHup( boolean noHup )
98 {
99 this.noHup = noHup;
100 }
101
102 public void setDumpFilePath( String dumpFilePath )
103 {
104 this.dumpFilePath = dumpFilePath;
105 }
106
107
108
109
110
111 public void setMaxtimeToOutputResponse( int maxTimeToOutputResponse )
112 {
113 mMaxTimeToOutputResponse = maxTimeToOutputResponse;
114 }
115
116
117
118
119
120 public void setRunName( String runName )
121 {
122 mRunName = runName;
123 }
124
125
126
127
128
129 public void run( ITestRunListener... listeners ) throws TimeoutException, AdbCommandRejectedException,
130 ShellCommandUnresponsiveException, IOException
131 {
132 run( Arrays.asList( listeners ) );
133 }
134
135
136
137
138 public void run( Collection< ITestRunListener > listeners ) throws TimeoutException, AdbCommandRejectedException,
139 ShellCommandUnresponsiveException, IOException
140 {
141 final String runCaseCommandStr = String.format( "uiautomator runtest %1$s %2$s", jarFile, buildArgsCommand() );
142 Log.i( LOG_TAG, String.format( "Running %1$s on %2$s", runCaseCommandStr, mRemoteDevice.getSerialNumber() ) );
143 mParser = new InstrumentationResultParser( mRunName, listeners );
144
145 try
146 {
147 mRemoteDevice.executeShellCommand( runCaseCommandStr, mParser, mMaxTimeToOutputResponse );
148 }
149 catch ( IOException e )
150 {
151 Log.w( LOG_TAG, String.format( "IOException %1$s when running tests %2$s on %3$s", e.toString(), jarFile,
152 mRemoteDevice.getSerialNumber() ) );
153
154 mParser.handleTestRunFailed( e.toString() );
155 throw e;
156 }
157 catch ( ShellCommandUnresponsiveException e )
158 {
159 Log.w( LOG_TAG,
160 String.format( "ShellCommandUnresponsiveException %1$s when running tests %2$s on %3$s",
161 e.toString(), jarFile, mRemoteDevice.getSerialNumber() ) );
162 mParser.handleTestRunFailed( String.format( "Failed to receive adb shell test output within %1$d ms. "
163 + "Test may have timed out, or adb connection to device became unresponsive",
164 mMaxTimeToOutputResponse ) );
165 throw e;
166 }
167 catch ( TimeoutException e )
168 {
169 Log.w( LOG_TAG,
170 String.format( "TimeoutException when running tests %1$s on %2$s", jarFile,
171 mRemoteDevice.getSerialNumber() ) );
172 mParser.handleTestRunFailed( e.toString() );
173 throw e;
174 }
175 catch ( AdbCommandRejectedException e )
176 {
177 Log.w( LOG_TAG, String.format( "AdbCommandRejectedException %1$s when running tests %2$s on %3$s",
178 e.toString(), jarFile, mRemoteDevice.getSerialNumber() ) );
179 mParser.handleTestRunFailed( e.toString() );
180 throw e;
181 }
182 }
183
184
185
186
187 public void cancel()
188 {
189 if ( mParser != null )
190 {
191 mParser.cancel();
192 }
193 }
194
195
196
197
198
199 private String buildArgsCommand()
200 {
201 StringBuilder commandBuilder = new StringBuilder();
202 for ( Entry< String, String > argPair : mArgList )
203 {
204 final String argCmd = String.format( " -e %1$s %2$s", argPair.getKey(), argPair.getValue() );
205 commandBuilder.append( argCmd );
206 }
207
208 if ( noHup )
209 {
210 commandBuilder.append( " --nohup" );
211 }
212
213 if ( dumpFilePath != null )
214 {
215 commandBuilder.append( " dump " + dumpFilePath );
216 }
217 return commandBuilder.toString();
218 }
219
220
221
222
223
224
225
226 public void setUserProperties( Properties userProperties, String propertiesKeyPrefix )
227 {
228 if ( userProperties == null )
229 {
230 throw new IllegalArgumentException( "userProperties cannot be null" );
231 }
232
233 if ( StringUtils.isBlank( propertiesKeyPrefix ) )
234 {
235
236 return;
237 }
238
239 for ( Entry< Object, Object > property : userProperties.entrySet() )
240 {
241 String name = (String) property.getKey();
242
243
244 if ( StringUtils.startsWith( name, propertiesKeyPrefix ) )
245 {
246 String value = (String) property.getValue();
247
248
249 name = StringUtils.substring( name,
250 StringUtils.length( propertiesKeyPrefix ),
251 StringUtils.length( name ) );
252
253
254 if ( StringUtils.isNotBlank( name ) )
255 {
256
257 addInstrumentationArg( name, value );
258 }
259 }
260 }
261 }
262 }