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