1f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License.  You may obtain a copy of the License at
8f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage tests.support;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
201f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport junit.framework.AssertionFailedError;
211f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport junit.framework.TestCase;
221f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.File;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
261f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.io.InputStreamReader;
271f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.io.Reader;
281f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.io.StringWriter;
291f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.util.Arrays;
301f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.util.concurrent.Callable;
311f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.util.concurrent.ExecutorService;
321f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.util.concurrent.Executors;
331f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.util.concurrent.Future;
341f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilsonimport java.util.concurrent.TimeUnit;
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class Support_Exec extends TestCase {
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
381f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson    private static final boolean againstDalvik
391f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            = System.getProperty("java.vendor").contains("Android");
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
421f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     * Returns a builder configured with the appropriate VM ("dalvikvm" or
431f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     * "java") and arguments (as specified by the system property
441f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     * {@code hy.test.vmargs}).
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
461f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson    public static ProcessBuilder javaProcessBuilder()
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throws IOException, InterruptedException {
481f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        ProcessBuilder builder = new ProcessBuilder();
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
501f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        // construct the name of executable file
511f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        builder.command().add(againstDalvik ? "dalvikvm" : "java");
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
531f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        // parse hy.test.vmargs if was given
541f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        String testVMArgs = System.getProperty("hy.test.vmargs");
551f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        if (testVMArgs != null) {
561f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            builder.command().addAll(Arrays.asList(testVMArgs.split("\\s+")));
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
591f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        return builder;
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
621f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson    /**
631f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     * Returns a command-line ready path formed by joining the path elements
641f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     * with the system path separator as a separator.
651f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     */
661f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson    public static String createPath(String... elements) {
671f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        StringBuilder result = new StringBuilder();
681f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        for (String element : elements) {
691f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            result.append(File.pathSeparator);
701f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            result.append(element);
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
721f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        return result.toString();
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
751f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson    /**
769c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson     * Starts the specified process, collects its output from standard out and
779c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson     * standard err, and returns. If the stream emits anything to standard err,
789c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson     * an AssertionFailedError will be thrown.
791f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     *
80513ac78ca8aaeb61fa06928ca10aeff34a7b4683Brian Carlstrom     * <p>This method assumes the target process will complete within thirty
811f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     * seconds. If it does not, an AssertionFailedError will be thrown.
821f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson     */
831805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes    public static String execAndGetOutput(ProcessBuilder builder) throws IOException {
849c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson        Process process = builder.start();
859c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson        ExecutorService executorService = Executors.newFixedThreadPool(2);
861f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        try {
879c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson            Future<String> errFuture = executorService.submit(
889c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                    streamToStringCallable(process.getErrorStream()));
899c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson            Future<String> outFuture = executorService.submit(
909c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                    streamToStringCallable(process.getInputStream()));
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
921f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            Throwable failure;
939c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson            String out = "";
941f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            try {
95513ac78ca8aaeb61fa06928ca10aeff34a7b4683Brian Carlstrom                out = outFuture.get(30, TimeUnit.SECONDS);
96513ac78ca8aaeb61fa06928ca10aeff34a7b4683Brian Carlstrom                String err = errFuture.get(30, TimeUnit.SECONDS);
979c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                failure = err.length() > 0
989c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                        ? new AssertionFailedError("Unexpected err stream data:\n" + err)
999c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                        : null;
1001f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            } catch (Exception e) {
1011f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson                failure = e;
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1041f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            if (failure != null) {
1051f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson                AssertionFailedError error = new AssertionFailedError(
1061f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson                        "Failed to execute " + builder.command() + "; output was:\n" + out);
1071f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson                error.initCause(failure);
1081f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson                throw error;
1091f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            } else {
1101f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson                return out;
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1121f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson        } finally {
1131f4eba15dc9e4086ce7e2a4fc7cac83fe6bbc03dJesse Wilson            executorService.shutdown();
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1171805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes    /**
1181805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes     * Starts the process described by 'builder', and asserts that it sees
1191805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes     * 'expectedOut' on stdout and 'expectedErr' on stderr. Times out after
1201805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes     * 10s.
1211805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes     */
1221805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes    public static void execAndCheckOutput(ProcessBuilder builder,
1231805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            String expectedOut, String expectedErr) throws Exception {
1241805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes        Process process = builder.start();
1251805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes        ExecutorService executorService = Executors.newFixedThreadPool(2);
1261805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes        try {
1271805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            Future<String> errFuture =
1281805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes                    executorService.submit(streamToStringCallable(process.getErrorStream()));
1291805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            Future<String> outFuture =
1301805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes                    executorService.submit(streamToStringCallable(process.getInputStream()));
1311805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            assertEquals(expectedOut, outFuture.get(10, TimeUnit.SECONDS));
1321805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            assertEquals(expectedErr, errFuture.get(10, TimeUnit.SECONDS));
1331805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes        } finally {
1341805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            executorService.shutdown();
1351805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes            process.waitFor();
1361805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes        }
1371805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes    }
1381805727c24b2b80161fef93c4b7742cf2322bdeaElliott Hughes
1399c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson    private static Callable<String> streamToStringCallable(final InputStream in) {
1409c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson        return new Callable<String>() {
1419c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson            public String call() throws Exception {
1429c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                StringWriter writer = new StringWriter();
1439c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                Reader reader = new InputStreamReader(in);
1449c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                int c;
1459c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                while ((c = reader.read()) != -1) {
1469c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                    writer.write(c);
1479c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                }
1489c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson                return writer.toString();
1499c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson            }
1509c048fb7738ee3ac3677ba2170e360de8d70dcabJesse Wilson        };
151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
153