1 /*
2  * Copyright (C) 2017 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 static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import org.junit.After;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.junit.runners.JUnit4;
29 
30 import java.io.ByteArrayInputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 
35 /**
36  * Test cases for {@link ProcessHelper}.
37  */
38 @RunWith(JUnit4.class)
39 public class ProcessHelperTest {
40     private ProcessHelper mProcessHelper;
41 
42     /**
43      * Return a mock process.
44      */
createMockProcess( String stdout, String stderr, int exitValue, long executionTimeMsecs)45     private Process createMockProcess(
46             String stdout, String stderr, int exitValue, long executionTimeMsecs) {
47         // No need to close OutputStream and ByteArrayInputStream because doing so has no effect.
48         OutputStream stdinStream = new OutputStream() {
49             @Override
50             public void write(int b) throws IOException {}
51         };
52         InputStream stdoutStream = new ByteArrayInputStream(stdout.getBytes());
53         InputStream stderrStream = new ByteArrayInputStream(stderr.getBytes());
54         long endTime = System.currentTimeMillis() + executionTimeMsecs;
55 
56         return new Process() {
57             private boolean destroyed = false;
58 
59             private boolean isRunning() {
60                 return System.currentTimeMillis() <= endTime && !destroyed;
61             }
62 
63             @Override
64             public void destroy() {
65                 destroyed = true;
66             }
67 
68             @Override
69             public int exitValue() {
70                 if (isRunning()) {
71                     throw new IllegalThreadStateException();
72                 }
73                 return exitValue;
74             }
75 
76             @Override
77             public InputStream getInputStream() {
78                 return stdoutStream;
79             }
80 
81             @Override
82             public OutputStream getOutputStream() {
83                 return stdinStream;
84             }
85 
86             @Override
87             public InputStream getErrorStream() {
88                 return stderrStream;
89             }
90 
91             @Override
92             public int waitFor() throws InterruptedException {
93                 while (isRunning()) {
94                     Thread.sleep(50);
95                 }
96                 return exitValue;
97             }
98         };
99     }
100 
101     /**
102      * Reset the ProcessHelper.
103      */
104     @Before
setUp()105     public void setUp() {
106         mProcessHelper = null;
107     }
108 
109     /**
110      * Terminate the process, join threads and close IO streams.
111      */
112     @After
tearDown()113     public void tearDown() {
114         if (mProcessHelper != null) {
115             mProcessHelper.cleanUp();
116         }
117     }
118 
119     /**
120      * Test running a process that returns zero.
121      */
122     @Test
testSuccess()123     public void testSuccess() {
124         mProcessHelper = new ProcessHelper(createMockProcess("123\n", "456\n", 0, 10));
125         CommandStatus status = mProcessHelper.waitForProcess(10000);
126         assertEquals(CommandStatus.SUCCESS, status);
127         assertFalse(mProcessHelper.isRunning());
128         assertTrue(mProcessHelper.getStdout().equals("123\n"));
129         assertTrue(mProcessHelper.getStderr().equals("456\n"));
130     }
131 
132     /**
133      * Test running a process that returns non-zero.
134      */
135     @Test
testFailure()136     public void testFailure() {
137         mProcessHelper = new ProcessHelper(createMockProcess("123\n", "456\n", 1, 10));
138         CommandStatus status = mProcessHelper.waitForProcess(10000);
139         assertEquals(CommandStatus.FAILED, status);
140         assertFalse(mProcessHelper.isRunning());
141         assertTrue(mProcessHelper.getStdout().equals("123\n"));
142         assertTrue(mProcessHelper.getStderr().equals("456\n"));
143     }
144 
145     /**
146      * Test running a process that times out.
147      */
148     @Test
testTimeout()149     public void testTimeout() {
150         mProcessHelper = new ProcessHelper(createMockProcess("", "", 1, 10000));
151         CommandStatus status = mProcessHelper.waitForProcess(100);
152         assertEquals(CommandStatus.TIMED_OUT, status);
153         assertTrue(mProcessHelper.isRunning());
154     }
155 
156     /**
157      * Test running a process and being interrupted.
158      */
159     @Test
testInterrupt()160     public void testInterrupt() throws InterruptedException {
161         mProcessHelper = new ProcessHelper(createMockProcess("", "", 1, 10000));
162         IRunUtil runUtil = RunUtil.getDefault();
163         Thread testThread = Thread.currentThread();
164 
165         Thread timer = new Thread() {
166             @Override
167             public void run() {
168                 try {
169                     Thread.sleep(50);
170                 } catch (InterruptedException e) {
171                     fail();
172                 }
173                 runUtil.interrupt(testThread, "unit test");
174             }
175         };
176 
177         runUtil.allowInterrupt(true);
178         timer.start();
179         try {
180             mProcessHelper.waitForProcess(100);
181             fail();
182         } catch (RunInterruptedException e) {
183             assertTrue(mProcessHelper.isRunning());
184         } finally {
185             timer.join(1000);
186         }
187     }
188 }
189