• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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.server.art.testing;
18 
19 import static org.mockito.Mockito.mock;
20 
21 import android.os.Binder;
22 import android.os.ParcelFileDescriptor;
23 
24 import com.android.modules.utils.BasicShellCommandHandler;
25 import com.android.server.art.Utils;
26 
27 import java.io.BufferedReader;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.InputStreamReader;
31 import java.io.PrintWriter;
32 import java.util.concurrent.CompletableFuture;
33 
34 /** A harness to test a {@link BasicShellCommandHandler}. */
35 public class CommandExecution implements AutoCloseable {
36     private ParcelFileDescriptor[] mStdinPipe = null;
37     private ParcelFileDescriptor[] mStdoutPipe = null;
38     private ParcelFileDescriptor[] mStderrPipe = null;
39     private PrintWriter mStdinWriter = null;
40     private BufferedReader mStdoutReader = null;
41     private BufferedReader mStderrReader = null;
42     private CompletableFuture<Integer> mFuture = null;
43 
CommandExecution(BasicShellCommandHandler commandHandler, String... args)44     public CommandExecution(BasicShellCommandHandler commandHandler, String... args)
45             throws Exception {
46         try {
47             mStdinPipe = ParcelFileDescriptor.createPipe();
48             mStdoutPipe = ParcelFileDescriptor.createPipe();
49             mStderrPipe = ParcelFileDescriptor.createPipe();
50             mStdinWriter = new PrintWriter(new FileOutputStream(mStdinPipe[1].getFileDescriptor()));
51             mStdoutReader = new BufferedReader(
52                     new InputStreamReader(new FileInputStream(mStdoutPipe[0].getFileDescriptor())));
53             mStderrReader = new BufferedReader(
54                     new InputStreamReader(new FileInputStream(mStderrPipe[0].getFileDescriptor())));
55             mFuture = CompletableFuture.supplyAsync(() -> exec(commandHandler, args));
56         } catch (Exception e) {
57             close();
58             throw e;
59         }
60     }
61 
exec(BasicShellCommandHandler commandHandler, String... args)62     private int exec(BasicShellCommandHandler commandHandler, String... args) {
63         int exitCode = commandHandler.exec(mock(Binder.class), mStdinPipe[0].getFileDescriptor(),
64                 mStdoutPipe[1].getFileDescriptor(), mStderrPipe[1].getFileDescriptor(), args);
65         try {
66             mStdinPipe[0].close();
67             mStdoutPipe[1].close();
68             mStderrPipe[1].close();
69         } catch (Exception e) {
70             throw new RuntimeException(e);
71         }
72         return exitCode;
73     }
74 
waitAndGetExitCode()75     public int waitAndGetExitCode() {
76         return Utils.getFuture(mFuture);
77     }
78 
getStdin()79     public PrintWriter getStdin() {
80         return mStdinWriter;
81     }
82 
closeStdin()83     public void closeStdin() throws Exception {
84         mStdinWriter.close();
85         mStdinPipe[1].close();
86     }
87 
getStdout()88     public BufferedReader getStdout() {
89         return mStdoutReader;
90     }
91 
getStderr()92     public BufferedReader getStderr() {
93         return mStderrReader;
94     }
95 
96     @Override
close()97     public void close() throws Exception {
98         if (mStdinWriter != null) {
99             mStdinWriter.close();
100         }
101         if (mStdoutReader != null) {
102             mStdoutReader.close();
103         }
104         if (mStderrReader != null) {
105             mStderrReader.close();
106         }
107         // Note that we still need to close the FDs after closing the streams. See the
108         // Android-specific warning at
109         // https://developer.android.com/reference/java/io/FileInputStream#FileInputStream(java.io.FileDescriptor)
110         if (mStdinPipe != null) {
111             mStdinPipe[0].close();
112             mStdinPipe[1].close();
113         }
114         if (mStdoutPipe != null) {
115             mStdoutPipe[0].close();
116             mStdoutPipe[1].close();
117         }
118         if (mStderrPipe != null) {
119             mStderrPipe[0].close();
120             mStderrPipe[1].close();
121         }
122     }
123 }
124