• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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  *      http://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.tradefed.util;
18 
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.log.LogUtil.CLog;
21 import com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer;
22 
23 import java.io.File;
24 import java.io.IOException;
25 
26 /**
27  * A helper class for executing VTS python scripts.
28  */
29 public class VtsPythonRunnerHelper {
30     // The timeout for the runner's teardown prodedure.
31     public static final long TEST_ABORT_TIMEOUT_MSECS = 1000 * 40;
32 
33     static final String PATH = "PATH";
34     static final String PYTHONHOME = "PYTHONHOME";
35     static final String VTS = "vts";
36 
37     // Python virtual environment root path
38     private File mVirtualenvPath;
39     protected IRunUtil mRunUtil;
40 
VtsPythonRunnerHelper(IBuildInfo buildInfo, File workingDir)41     public VtsPythonRunnerHelper(IBuildInfo buildInfo, File workingDir) {
42         this(buildInfo.getBuildAttributes().get(VtsPythonVirtualenvPreparer.VIRTUAL_ENV),
43                 workingDir);
44     }
45 
VtsPythonRunnerHelper(String virtualEnvPath, File workingDir)46     public VtsPythonRunnerHelper(String virtualEnvPath, File workingDir) {
47         this(virtualEnvPath == null ? null : new File(virtualEnvPath), workingDir);
48     }
49 
VtsPythonRunnerHelper(File virtualEnvPath, File workingDir)50     public VtsPythonRunnerHelper(File virtualEnvPath, File workingDir) {
51         mVirtualenvPath = virtualEnvPath;
52         mRunUtil = new RunUtil();
53         activateVirtualenv(mRunUtil, getPythonVirtualEnv());
54         mRunUtil.setWorkingDir(workingDir);
55     }
56 
57     /**
58      * Create a {@link ProcessHelper} from mRunUtil.
59      *
60      * @param cmd the command to run.
61      * @throws IOException if fails to start Process.
62      */
createProcessHelper(String[] cmd)63     protected ProcessHelper createProcessHelper(String[] cmd) throws IOException {
64         return new ProcessHelper(mRunUtil.runCmdInBackground(cmd));
65     }
66 
67     /**
68      * Run VTS Python runner and handle interrupt from TradeFed.
69      *
70      * @param cmd the command to start VTS Python runner.
71      * @param commandResult the object containing the command result.
72      * @param timeout command timeout value.
73      * @return null if the command terminates or times out; a message string if the command is
74      * interrupted by TradeFed.
75      */
runPythonRunner(String[] cmd, CommandResult commandResult, long timeout)76     public String runPythonRunner(String[] cmd, CommandResult commandResult, long timeout) {
77         ProcessHelper process;
78         try {
79             process = createProcessHelper(cmd);
80         } catch (IOException e) {
81             CLog.e(e);
82             commandResult.setStatus(CommandStatus.EXCEPTION);
83             commandResult.setStdout("");
84             commandResult.setStderr("");
85             return null;
86         }
87 
88         String interruptMessage;
89         try {
90             CommandStatus commandStatus;
91             try {
92                 commandStatus = process.waitForProcess(timeout);
93                 interruptMessage = null;
94             } catch (RunInterruptedException e) {
95                 CLog.e("Python process is interrupted.");
96                 commandStatus = CommandStatus.TIMED_OUT;
97                 interruptMessage = (e.getMessage() != null ? e.getMessage() : "");
98             }
99             if (process.isRunning()) {
100                 CLog.e("Cancel Python process and wait %d seconds.",
101                         TEST_ABORT_TIMEOUT_MSECS / 1000);
102                 try {
103                     process.closeStdin();
104                     // Wait for the process to clean up and ignore the CommandStatus.
105                     // Propagate RunInterruptedException if this is interrupted again.
106                     process.waitForProcess(TEST_ABORT_TIMEOUT_MSECS);
107                 } catch (IOException e) {
108                     CLog.e("Fail to cancel Python process.");
109                 }
110             }
111             commandResult.setStatus(commandStatus);
112         } finally {
113             process.cleanUp();
114         }
115         commandResult.setStdout(process.getStdout());
116         commandResult.setStderr(process.getStderr());
117         return interruptMessage;
118     }
119 
120     /**
121      * Gets python bin directory path.
122      *
123      * This method will check the directory existence.
124      *
125      * @return python bin directory; null if not exist.
126      */
getPythonBinDir(String virtualenvPath)127     public static String getPythonBinDir(String virtualenvPath) {
128         if (virtualenvPath == null) {
129             return null;
130         }
131         String binDirName = EnvUtil.isOnWindows() ? "Scripts" : "bin";
132         File res = new File(virtualenvPath, binDirName);
133         if (!res.exists()) {
134             return null;
135         }
136         return res.getAbsolutePath();
137     }
138 
139     /**
140      * Get python virtualenv path
141      * @return virutalenv path. null if doesn't exist
142      */
getPythonVirtualEnv()143     public String getPythonVirtualEnv() {
144         if (mVirtualenvPath == null) {
145             return null;
146         }
147         return mVirtualenvPath.getAbsolutePath();
148     }
149 
150     /**
151      * Activate virtualenv for a RunUtil.
152      *
153      * This method will check for python bin directory existence
154      *
155      * @param runUtil
156      * @param virtualenvPath
157      */
activateVirtualenv(IRunUtil runUtil, String virtualenvPath)158     public static void activateVirtualenv(IRunUtil runUtil, String virtualenvPath) {
159         String pythonBinDir = getPythonBinDir(virtualenvPath);
160         if (pythonBinDir == null || !new File(pythonBinDir).exists()) {
161             CLog.e("Invalid python virtualenv path. Using python from system path.");
162         } else {
163             String separater = EnvUtil.isOnWindows() ? ";" : ":";
164             runUtil.setEnvVariable(PATH, pythonBinDir + separater + System.getenv().get(PATH));
165             runUtil.setEnvVariable(VtsPythonVirtualenvPreparer.VIRTUAL_ENV, virtualenvPath);
166             runUtil.unsetEnvVariable(PYTHONHOME);
167         }
168     }
169 }
170