• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      httprunPackage://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts;
18 
19 import com.android.cts.HostConfig.CaseRepository;
20 import com.android.cts.HostConfig.PlanRepository;
21 import com.android.ddmlib.AndroidDebugBridge;
22 
23 import org.xml.sax.SAXException;
24 
25 import java.io.BufferedWriter;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.FileReader;
29 import java.io.FileWriter;
30 import java.io.IOException;
31 import java.security.NoSuchAlgorithmException;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.HashMap;
35 
36 import javax.xml.parsers.ParserConfigurationException;
37 import javax.xml.transform.TransformerException;
38 import javax.xml.transform.TransformerFactoryConfigurationError;
39 
40 /**
41  * Act as the host for the device connections, also provides management of
42  * sessions.
43  */
44 public class TestHost extends XMLResourceHandler implements SessionObserver {
45     public static final String TEMP_PLAN_NAME = "tempPlan";
46 
47     enum ActionType {
48         RUN_SINGLE_TEST, RUN_SINGLE_JAVA_PACKAGE, START_NEW_SESSION, RESUME_SESSION
49     }
50     /**
51      * Definition of the modes the TestHost will run with.
52      * <ul>
53      *    <li> RUN: For this mode, the TestHost will run the plan or
54      *              package directly without starting the UI.
55      *    <li> CONSOLE: For this mode, the TestHost will start the UI
56      *                  and wait for input from user.
57      * </ul>
58      */
59     enum MODE {
60         UNINITIALIZED, RUN, CONSOLE
61     }
62 
63     static private ArrayList<TestSession> sSessions = new ArrayList<TestSession>();
64     static private DeviceManager sDeviceManager = new DeviceManager();
65     static private Object sTestSessionSync = new Object();
66 
67     static private ConsoleUi sConsoleUi;
68 
69     static private HostConfig sConfig;
70 
71     private static TestHost sInstance;
72     static MODE sMode = MODE.UNINITIALIZED;
73     private static boolean sQuick = false;
74 
main(final String[] mainArgs)75     public static void main(final String[] mainArgs) {
76         CUIOutputStream.println("Android CTS version " + Version.asString());
77 
78         if (HostLock.lock() == false) {
79             Log.e("Error: CTS is being used at the moment."
80                     + " No more than one CTS instance is allowed simultaneously", null);
81             exit();
82         }
83 
84         sDeviceManager.initAdb();
85 
86         sConsoleUi = new ConsoleUi(getInstance());
87         CommandParser cp = init(sConsoleUi, mainArgs);
88 
89         if (sMode == MODE.RUN) {
90             try {
91                 /* After booting up, the connection between
92                  * CTS host and device isn't ready. It's needed
93                  * to wait for 3 seconds for device ready to
94                  * start the the mode of no console UI.
95                  */
96                 Thread.sleep(3000);
97                 cp.removeKey(CTSCommand.OPTION_CFG);
98                 sConsoleUi.processCommand(cp);
99             } catch (InterruptedException e) {
100                 Log.e("Met InterruptedException", e);
101             } catch (Exception e) {
102                 Log.e("Met exception when processing command", e);
103             }
104         } else if (sMode == MODE.CONSOLE) {
105             sConsoleUi.startUi();
106         }
107 
108         exit();
109     }
110 
111     /**
112      * Release host lock and then exit.
113      */
exit()114     private static void exit() {
115         Log.closeLog();
116         HostLock.release();
117         System.exit(-1);
118     }
119 
120     /**
121      * Extract mode from the options used to activating CTS.
122      *
123      * @param cp Command container.
124      * @return The mode.
125      */
getMode(final CommandParser cp)126     static private MODE getMode(final CommandParser cp) {
127         String action = cp.getAction();
128         if ((action != null) && (action.equals(CTSCommand.START))) {
129             return MODE.RUN;
130         } else {
131             return MODE.CONSOLE;
132         }
133     }
134 
135     /**
136      * Start zipped package.
137      *
138      * @param pathName  The path name of the zipped package.
139      */
startZippedPackage(final String pathName)140     public void startZippedPackage(final String pathName)
141                 throws FileNotFoundException,
142                        IOException,
143                        ParserConfigurationException,
144                        TransformerFactoryConfigurationError,
145                        TransformerException,
146                        DeviceNotAvailableException,
147                        TestNotFoundException,
148                        SAXException,
149                        TestPlanNotFoundException,
150                        IllegalTestNameException,
151                        InterruptedException, DeviceDisconnectedException,
152                        NoSuchAlgorithmException, InvalidNameSpaceException,
153                        InvalidApkPathException {
154 
155         // step 1: add package
156         if (!addPackage(pathName)) {
157             return;
158         }
159 
160         // step 2: create plan
161         ArrayList<String> packages = new ArrayList<String>();
162         String pkgName = pathName.substring(pathName
163                 .lastIndexOf(File.separator) + 1, pathName.lastIndexOf("."));
164         packages.add(pkgName);
165         HashMap<String, ArrayList<String>> selectedResult =
166                        new HashMap<String, ArrayList<String>>();
167         selectedResult.put(pkgName, null);
168         TestSessionBuilder.getInstance().serialize(TEMP_PLAN_NAME, packages, selectedResult);
169 
170         // step 3: start the plan
171         TestSession ts = startSession(TEMP_PLAN_NAME, getFirstAvailableDevice().getSerialNumber(),
172                 null);
173 
174         // step 4: copy the resulting zip file
175         String resultName = pathName.substring(0, pathName.lastIndexOf("."))
176                 + ".zip";
177         TestSessionLog log = ts.getSessionLog();
178         copyFile(log.getResultPath() + ".zip", resultName);
179 
180         // step 5: clear the temporary working environment
181         removePlans(TEMP_PLAN_NAME);
182         //give the system some time to avoid asserting
183         Thread.sleep(1000);
184 
185         removePackages(pkgName);
186         //give the system some time to avoid asserting
187         Thread.sleep(1000);
188     }
189 
190     /**
191      * Copy the source file to the destination file.
192      *
193      * @param srcFileName The name of the source file.
194      * @param dstFileName The name of the destination file.
195      */
copyFile(final String srcFileName, final String dstFileName)196     private void copyFile(final String srcFileName, final String dstFileName) throws IOException {
197         FileReader input = new FileReader(new File(srcFileName));
198         BufferedWriter output = new BufferedWriter(new FileWriter(dstFileName));
199 
200         int c;
201         while ((c = input.read()) != -1) {
202             output.write(c);
203         }
204 
205         input.close();
206         output.flush();
207         output.close();
208     }
209 
210     /**
211      * Add a package by the path and package name.
212      *
213      * @param pathName The path name.
214      * @return If succeed in adding package, return true; else, return false.
215      */
addPackage(final String pathName)216     public boolean addPackage(final String pathName) throws FileNotFoundException,
217             IOException, NoSuchAlgorithmException {
218 
219         CaseRepository caseRepo = sConfig.getCaseRepository();
220         if (!HostUtils.isFileExist(pathName)) {
221             Log.e("Package error: package file " + pathName + " doesn't exist.", null);
222             return false;
223         }
224 
225         if (!caseRepo.isValidPackageName(pathName)) {
226             return false;
227         }
228 
229         caseRepo.addPackage(pathName);
230         return true;
231     }
232 
233     /**
234      * Remove plans from the plan repository according to the specific plan name.
235      *
236      * @param name The plan name.
237      */
removePlans(final String name)238     public void removePlans(final String name) {
239         if ((name == null) || (name.length() == 0)) {
240             CUIOutputStream.println("Please add plan name or all as parameter.");
241             return;
242         }
243 
244         PlanRepository planRepo = sConfig.getPlanRepository();
245         if (name.equals(HostConfig.ALL)) {
246             ArrayList<String> plans = planRepo.getAllPlanNames();
247             for (String plan : plans) {
248                 removePlan(plan, planRepo);
249             }
250         } else {
251             if (!planRepo.getAllPlanNames().contains(name)) {
252                 Log.e("No plan named " + name + " in repository!", null);
253                 return;
254             }
255             removePlan(name, planRepo);
256         }
257     }
258 
259     /**
260      * Remove a specified plan from the plan repository.
261      *
262      * @param planName The plan name.
263      * @param planRepo The plan repository.
264      */
removePlan(final String planName, final PlanRepository planRepo)265     private void removePlan(final String planName, final PlanRepository planRepo) {
266         File planFile = new File(planRepo.getPlanPath(planName));
267         if (!planFile.isFile() || !planFile.exists()) {
268             Log.e("Can't locate the file of the plan, please check your repository!", null);
269             return;
270         }
271 
272         if (!planFile.canWrite()) {
273             Log.e("Can't delete this plan, permission denied!", null);
274             return;
275         }
276 
277         if (!planFile.delete()) {
278             Log.e(planName + " plan file delete failed", null);
279         }
280     }
281 
282     /**
283      * Remove packages from the case repository..
284      *
285      * @param packageName The java package name to be removed from the case repository.
286      */
removePackages(final String packageName)287     public void removePackages(final String packageName)
288             throws IndexOutOfBoundsException {
289         CaseRepository caseRepo = sConfig.getCaseRepository();
290 
291         if ((packageName == null) || (packageName.length() == 0)) {
292             CUIOutputStream.println("Please add package name or all as parameter.");
293             return;
294         }
295 
296         caseRepo.removePackages(packageName);
297     }
298 
299     /**
300      * Initialize TestHost with the arguments passed in.
301      *
302      * @param mainArgs The arguments.
303      * @return CommandParser which contains the command and options.
304      */
init(final ConsoleUi cui, final String[] mainArgs)305     static CommandParser init(final ConsoleUi cui, final String[] mainArgs) {
306         CommandParser cp = null;
307         String cfgPath= null;
308 
309         if (mainArgs.length == 0) {
310             sMode = MODE.CONSOLE;
311             cfgPath = System.getProperty("HOST_CONFIG");
312             if ((cfgPath == null) || (cfgPath.length() == 0)) {
313                 Log.e("Please make sure environment variable CTS_HOST_CFG is "
314                        + "set as {cts install path}[/host_config.xml].", null);
315                 exit();
316             }
317         } else if (mainArgs.length == 1) {
318             sMode = MODE.CONSOLE;
319             cfgPath = mainArgs[0];
320         } else {
321             String cmdLine = "";
322             for (int i = 0; i < mainArgs.length; i ++) {
323                 cmdLine += mainArgs[i] + " ";
324             }
325 
326             try {
327                 cp = CommandParser.parse(cmdLine);
328                 if (!cui.validateCommandParams(cp)) {
329                     Log.e("Please type in arguments correctly to activate CTS.", null);
330                     exit();
331                 }
332             } catch (UnknownCommandException e1) {
333                 Log.e("Please type in arguments correctly to activate CTS.", null);
334                 exit();
335             } catch (CommandNotFoundException e1) {
336                 Log.e("Please type in arguments correctly to activate CTS.", null);
337                 exit();
338             }
339 
340             sMode = getMode(cp);
341             if (sMode == MODE.RUN) {
342                 if (cp.containsKey(CTSCommand.OPTION_CFG)) {
343                     cfgPath = cp.getValue(CTSCommand.OPTION_CFG);
344                 } else {
345                     cfgPath = System.getProperty("HOST_CONFIG");
346                     if ((cfgPath == null) || (cfgPath.length() == 0)) {
347                         Log.e("Please make sure environment variable CTS_HOST_CFG "
348                                + "is set as {cts install path}[/host_config.xml].", null);
349                         exit();
350                     }
351                 }
352             }
353 
354             if (cp.containsKey(CTSCommand.OPTION_QUICK)) {
355                 sQuick = true;
356             }
357         }
358 
359         if ((cfgPath == null) || (cfgPath.length() == 0)) {
360             Log.e("Please type in arguments correctly to activate CTS.", null);
361             exit();
362         }
363 
364         String filePath = getConfigFilePath(cfgPath);
365         try {
366             if (loadConfig(filePath) == false) {
367                 exit();
368             }
369             if (sQuick) {
370                 HostConfig.Ints.valueOf("postInstallWaitMs").setValue(1);
371             }
372 
373             Log.initLog(sConfig.getLogRoot());
374             sConfig.loadRepositories(sQuick);
375         } catch (Exception e) {
376             Log.e("Error while parsing cts config file", e);
377             exit();
378         }
379         return cp;
380     }
381 
382     /**
383      * Singleton generator.
384      *
385      * @return The TestHost.
386      */
getInstance()387     public static TestHost getInstance() {
388         if (sInstance == null) {
389             sInstance = new TestHost();
390         }
391 
392         return sInstance;
393     }
394 
395     /**
396      * Get configuration file from the arguments given.
397      *
398      * @param filePath The file path.
399      * @return The the path of the configuration file.
400      */
getConfigFilePath(final String filePath)401     static private String getConfigFilePath(final String filePath) {
402         if (filePath != null) {
403             if (!HostUtils.isFileExist(filePath)) {
404                 Log.e("Configuration file \"" + filePath + "\" doesn't exist.", null);
405                 exit();
406             }
407         } else {
408             Log.e("Configuration file doesn't exist.", null);
409             exit();
410         }
411 
412         return filePath;
413     }
414 
415     /**
416      * Load configuration from the given file.
417      *
418      * @param configPath The configuration path.
419      * @return If succeed, return true; else, return false.
420      */
loadConfig(final String configPath)421     static boolean loadConfig(final String configPath) throws SAXException,
422             IOException, ParserConfigurationException {
423         sConfig = HostConfig.getInstance();
424 
425         return sConfig.load(configPath);
426     }
427 
428     /**
429      * Get case repository.
430      *
431      * @return The case repository.
432      */
getCaseRepository()433     public HostConfig.CaseRepository getCaseRepository() {
434         return sConfig.getCaseRepository();
435     }
436 
437     /**
438      * Get plan repository.
439      *
440      * @return The plan repository.
441      */
getPlanRepository()442     public HostConfig.PlanRepository getPlanRepository() {
443         return sConfig.getPlanRepository();
444     }
445 
446     /**
447      * Run the specified {@link TestSession} on the specified {@link TestDevice}(s)
448      *
449      * @param ts the specified {@link TestSession}
450      * @param deviceId the ID of the specified {@link TestDevice}
451      * @param testFullName The full name of the test to be run.
452      * @param javaPkgName The specific java package name to be run.
453      * @param type The action type to activate the test session.
454      */
runTest(final TestSession ts, final String deviceId, final String testFullName, final String javaPkgName, ActionType type)455     static private void runTest(final TestSession ts, final String deviceId,
456             final String testFullName, final String javaPkgName, ActionType type)
457             throws DeviceNotAvailableException, TestNotFoundException, IllegalTestNameException,
458             DeviceDisconnectedException, InvalidNameSpaceException,
459             InvalidApkPathException {
460 
461         if (ts == null) {
462             return;
463         }
464 
465         ts.setObserver(getInstance());
466         TestDevice device = sDeviceManager.allocateFreeDeviceById(deviceId);
467         TestSessionLog sessionLog = ts.getSessionLog();
468         ts.setTestDevice(device);
469         ts.getDevice().installDeviceSetupApp();
470         if (!sQuick) {
471             sessionLog.setDeviceInfo(ts.getDevice().getDeviceInfo());
472         }
473 
474         boolean finish = false;
475         while (!finish) {
476             ts.getDevice().disableKeyguard();
477             try {
478                 switch (type) {
479                 case RUN_SINGLE_TEST:
480                     ts.start(testFullName);
481                     break;
482 
483                 case RUN_SINGLE_JAVA_PACKAGE:
484                     ts.start(javaPkgName);
485                     break;
486 
487                 case START_NEW_SESSION:
488                     ts.start();
489                     break;
490 
491                 case RESUME_SESSION:
492                     ts.resume();
493                     break;
494                 }
495 
496                 finish = true;
497             } catch (ADBServerNeedRestartException e) {
498                 Log.d(e.getMessage());
499                 Log.i("Max ADB operations reached. Restarting ADB...");
500 
501                 TestSession.setADBServerRestartedMode();
502                 sDeviceManager.restartADBServer(ts);
503 
504                 type = ActionType.RESUME_SESSION;
505             }
506         }
507 
508         TestSession.resetADBServerRestartedMode();
509         if (HostConfig.getMaxTestCount() > 0) {
510             sDeviceManager.resetTestDevice(ts.getDevice());
511         }
512 
513         ts.getDevice().uninstallDeviceSetupApp();
514     }
515 
516     /**
517      * Create {@link TestSession} according to the specified test plan.
518      *
519      * @param testPlanName the name of the specified test plan
520      * @return a {@link TestSession}
521      */
createSession(final String testPlanName)522     static public TestSession createSession(final String testPlanName)
523             throws IOException, TestNotFoundException, SAXException,
524             ParserConfigurationException, TestPlanNotFoundException, NoSuchAlgorithmException {
525 
526         String testPlanPath = sConfig.getPlanRepository().getPlanPath(testPlanName);
527         TestSession ts = TestSessionBuilder.getInstance().build(testPlanPath);
528         sSessions.add(ts);
529 
530         return ts;
531     }
532 
533     /** {@inheritDoc} */
notifyFinished(final TestSession ts)534     public void notifyFinished(final TestSession ts) {
535         // As test run on a session, so just keep session info in debug level
536         Log.d("Session " + ts.getId() + " finished.");
537 
538         synchronized (sTestSessionSync) {
539             sTestSessionSync.notify();
540         }
541         ts.getSessionLog().sessionComplete();
542     }
543 
544     /**
545      * Tear down ADB connection.
546      */
tearDown()547     public void tearDown() {
548         AndroidDebugBridge.disconnectBridge();
549         AndroidDebugBridge.terminate();
550     }
551 
552     /**
553      * Get the sessions connected with devices.
554      *
555      * @return The sessions.
556      */
getSessions()557     public Collection<TestSession> getSessions() {
558         return sSessions;
559     }
560 
561     /**
562      * Get session by session ID.
563      *
564      * @param sessionId The session ID.
565      * @return The session.
566      */
getSession(final int sessionId)567     public TestSession getSession(final int sessionId) {
568         for (TestSession session : sSessions) {
569             if (session.getId() == sessionId) {
570                 return session;
571             }
572         }
573         return null;
574     }
575 
576     /**
577      * Get session by test plan name.
578      *
579      * @param testPlanName Test plan name.
580      * @return The session corresponding to the test plan name.
581      */
getSessionList(final String testPlanName)582     public ArrayList<TestSession> getSessionList(final String testPlanName) {
583         ArrayList<TestSession> list = new ArrayList<TestSession>();
584         for (TestSession session : sSessions) {
585             if (testPlanName.equals(session.getSessionLog().getTestPlanName())) {
586                 list.add(session);
587             }
588         }
589         return list;
590     }
591 
592     /**
593      * List the ID, name and status of all {@link TestDevice} which connected to
594      * the {@link TestHost}.
595      *
596      * @return a string list of {@link TestDevice}'s id, name and status.
597      */
listDevices()598     public String[] listDevices() {
599         ArrayList<String> deviceList = new ArrayList<String>();
600         TestDevice[] devices = sDeviceManager.getDeviceList();
601 
602         for (TestDevice device : devices) {
603             deviceList.add(device.getSerialNumber() + "\t" + device.getStatusAsString());
604         }
605         return deviceList.toArray(new String[deviceList.size()]);
606     }
607 
608     /**
609      * Get device list connected with the host.
610      *
611      * @return The device list connected with the host.
612      */
getDeviceList()613     public TestDevice[] getDeviceList() {
614         return sDeviceManager.getDeviceList();
615     }
616 
617     /**
618      * Get the first available device.
619      *
620      * @return the first available device or null if none are available.
621      */
getFirstAvailableDevice()622     public TestDevice getFirstAvailableDevice() {
623         for (TestDevice td : sDeviceManager.getDeviceList()) {
624             if (td.getStatus() == TestDevice.STATUS_IDLE) {
625                 return td;
626             }
627         }
628         return null;
629     }
630 
631     /**
632      * Get session logs.
633      *
634      * @return Session logs.
635      */
getSessionLogs()636     public Collection<TestSessionLog> getSessionLogs() {
637         ArrayList<TestSessionLog> sessionLogs = new ArrayList<TestSessionLog>();
638         for (TestSession session : sSessions) {
639             sessionLogs.add(session.getSessionLog());
640         }
641         return sessionLogs;
642     }
643     /**
644      * Start a test session.
645      *
646      * @param testPlanName TestPlan config file name
647      * @param deviceId Target device ID
648      * @param profile The profile of the device being tested.
649      * @param javaPkgName The specific java package name to be run.
650      */
startSession(final String testPlanName, String deviceId, final String javaPkgName)651     public TestSession startSession(final String testPlanName,
652             String deviceId, final String javaPkgName)
653             throws IOException, DeviceNotAvailableException,
654             TestNotFoundException, SAXException, ParserConfigurationException,
655             TestPlanNotFoundException, IllegalTestNameException,
656             DeviceDisconnectedException, NoSuchAlgorithmException,
657             InvalidNameSpaceException, InvalidApkPathException {
658 
659         TestSession ts = createSession(testPlanName);
660         if ((javaPkgName != null) && (javaPkgName.length() != 0)) {
661             runTest(ts, deviceId, null, javaPkgName, ActionType.RUN_SINGLE_JAVA_PACKAGE);
662         } else {
663             runTest(ts, deviceId, null, javaPkgName, ActionType.START_NEW_SESSION);
664         }
665 
666         ts.getSessionLog().sessionComplete();
667         return ts;
668     }
669 
670     /**
671      * Start a test session.
672      *
673      * @param ts The test session.
674      * @param deviceId Target device ID.
675      * @param testFullName Specific test full name.
676      * @param javaPkgName The specific java package name to be run.
677      * @param type The action type to activate the test session.
678      */
startSession(final TestSession ts, String deviceId, final String testFullName, final String javaPkgName, ActionType type)679     public TestSession startSession(final TestSession ts, String deviceId,
680             final String testFullName, final String javaPkgName, ActionType type)
681             throws DeviceNotAvailableException,
682             TestNotFoundException, IllegalTestNameException,
683             DeviceDisconnectedException, InvalidNameSpaceException,
684             InvalidApkPathException {
685 
686         runTest(ts, deviceId, testFullName, javaPkgName, type);
687         ts.getSessionLog().sessionComplete();
688         return ts;
689     }
690 
691     /**
692      * Get plan name from what is typed in by the user.
693      *
694      * @param rawPlanName The raw plan name.
695      * @return The plan name.
696      */
getPlanName(final String rawPlanName)697     public String getPlanName(final String rawPlanName) {
698         if (rawPlanName.indexOf("\\") != -1) {
699             return rawPlanName.replaceAll("\\\\", "");
700         }
701         if (rawPlanName.indexOf("\"") != -1) {
702             return rawPlanName.replaceAll("\"", "");
703         }
704         return rawPlanName;
705     }
706 
707     /**
708      * Add test session.
709      *
710      * @param ts The test session.
711      */
addSession(TestSession ts)712     public void addSession(TestSession ts) {
713         sSessions.add(ts);
714     }
715 }
716