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