1 /*
2  * Copyright (C) 2019 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 package com.android.atest.commandAdapter;
17 
18 import com.android.atest.AtestUtils;
19 import com.android.atest.Constants;
20 import com.android.atest.toolWindow.AtestToolWindow;
21 import com.android.atest.widget.AtestNotification;
22 import com.intellij.build.BuildTextConsoleView;
23 import com.intellij.execution.ExecutionException;
24 import com.intellij.execution.ExecutionManager;
25 import com.intellij.execution.configurations.PtyCommandLine;
26 import com.intellij.execution.executors.DefaultRunExecutor;
27 import com.intellij.execution.process.KillableColoredProcessHandler;
28 import com.intellij.execution.process.ProcessListener;
29 import com.intellij.execution.ui.RunContentDescriptor;
30 import com.intellij.notification.Notifications;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.project.Project;
33 import org.jetbrains.annotations.NotNull;
34 
35 import javax.swing.*;
36 import java.awt.*;
37 import java.nio.charset.StandardCharsets;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 
41 /** A manager to handle command. */
42 public class CommandRunner {
43 
44     private static final Logger LOG = Logger.getInstance(CommandRunner.class);
45     private static final String ATEST_COMMAND_PREFIX =
46             "export USER_FROM_TOOL=\"IntelliJ_Atest_plugin\" && source build/envsetup.sh && lunch ";
47     private static KillableColoredProcessHandler sProcessHandler;
48     private PtyCommandLine mCommand;
49     private ProcessListener mProcessListener;
50     private Project mProject;
51 
52     /**
53      * Initializes CommandRunner by the command.
54      *
55      * @param cmds the command to run.
56      * @param workPath the work path to run the command.
57      */
CommandRunner(ArrayList<String> cmds, String workPath)58     public CommandRunner(ArrayList<String> cmds, String workPath) {
59         mCommand = new PtyCommandLine(cmds);
60         mCommand.setCharset(StandardCharsets.UTF_8);
61         mCommand.setWorkDirectory(workPath);
62     }
63 
64     /**
65      * Initializes CommandRunner by Atest lunch target and test target.
66      *
67      * @param lunchTarget the Atest lunch target.
68      * @param testTarget the Atest test target.
69      * @param workPath the work path to run the command.
70      * @param toolWindow an AtestToolWindow to display the output.
71      * @param project the current intelliJ project.
72      */
CommandRunner( String lunchTarget, String testTarget, String workPath, @NotNull AtestToolWindow toolWindow, @NotNull Project project)73     public CommandRunner(
74             String lunchTarget,
75             String testTarget,
76             String workPath,
77             @NotNull AtestToolWindow toolWindow,
78             @NotNull Project project)
79             throws IllegalArgumentException {
80         if (AtestUtils.checkEmpty(lunchTarget, testTarget, workPath)) {
81             throw new IllegalArgumentException();
82         }
83         mProject = project;
84         StringBuffer commandBuffer = new StringBuffer(ATEST_COMMAND_PREFIX);
85         String atestCommand =
86                 commandBuffer
87                         .append(lunchTarget)
88                         .append(" && atest ")
89                         .append(testTarget)
90                         .toString();
91         LOG.info("Atest command: " + atestCommand + ", work path: " + workPath);
92 
93         String[] commandArray = {"/bin/bash", "-c", atestCommand};
94         ArrayList<String> cmds = new ArrayList<>(Arrays.asList(commandArray));
95         mCommand = new PtyCommandLine(cmds);
96         mCommand.setCharset(StandardCharsets.UTF_8);
97         mCommand.setWorkDirectory(workPath);
98         mProcessListener = new AtestProcessListener(toolWindow);
99     }
100 
101     /**
102      * Sets the process listener.
103      *
104      * @param processListener a processListener handle the output.
105      */
setProcessListener(ProcessListener processListener)106     public void setProcessListener(ProcessListener processListener) {
107         mProcessListener = processListener;
108     }
109 
110     /**
111      * Executes the command in processHandler.
112      *
113      * <p>Execute this method when caller is ready to access linux command.
114      */
run()115     public void run() {
116         try {
117             stopProcess(mProject);
118             sProcessHandler = new KillableColoredProcessHandler(mCommand);
119             if (mProcessListener != null) {
120                 sProcessHandler.addProcessListener(mProcessListener);
121             }
122             launchConsole();
123             sProcessHandler.startNotify();
124         } catch (ExecutionException e) {
125             Notifications.Bus.notify(new AtestNotification("Command execution failed."));
126             LOG.error("Command executes fail: " + mCommand.getCommandLineString());
127         }
128     }
129 
130     /** Launch the console view of current process. */
launchConsole()131     private void launchConsole() {
132         BuildTextConsoleView consoleView = new BuildTextConsoleView(mProject);
133         JPanel panel = new JPanel(new BorderLayout());
134         consoleView.attachToProcess(sProcessHandler);
135         panel.add(consoleView.getComponent(), BorderLayout.CENTER);
136         panel.updateUI();
137         RunContentDescriptor contentDescriptor =
138                 new RunContentDescriptor(consoleView, sProcessHandler, panel, Constants.ATEST_NAME);
139         ExecutionManager.getInstance(mProject)
140                 .getContentManager()
141                 .showRunContent(DefaultRunExecutor.getRunExecutorInstance(), contentDescriptor);
142     }
143 
144     /**
145      * Stops the current process.
146      *
147      * @param project the current intelliJ project.
148      */
stopProcess(Project project)149     public static void stopProcess(Project project) {
150         if (sProcessHandler != null) {
151             RunContentDescriptor contentDescriptor =
152                     ExecutionManager.getInstance(project)
153                             .getContentManager()
154                             .findContentDescriptor(
155                                     DefaultRunExecutor.getRunExecutorInstance(), sProcessHandler);
156             ExecutionManager.getInstance(project)
157                     .getContentManager()
158                     .hideRunContent(DefaultRunExecutor.getRunExecutorInstance(), contentDescriptor);
159             sProcessHandler.killProcess();
160             sProcessHandler = null;
161         }
162     }
163 }
164