1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package tests.support;
19
20import junit.framework.AssertionFailedError;
21import junit.framework.TestCase;
22
23import java.io.File;
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.InputStreamReader;
27import java.io.Reader;
28import java.io.StringWriter;
29import java.util.Arrays;
30import java.util.concurrent.Callable;
31import java.util.concurrent.ExecutorService;
32import java.util.concurrent.Executors;
33import java.util.concurrent.Future;
34import java.util.concurrent.TimeUnit;
35
36public class Support_Exec extends TestCase {
37
38    private static final boolean againstDalvik
39            = System.getProperty("java.vendor").contains("Android");
40
41    /**
42     * Returns a builder configured with the appropriate VM ("dalvikvm" or
43     * "java") and arguments (as specified by the system property
44     * {@code hy.test.vmargs}).
45     */
46    public static ProcessBuilder javaProcessBuilder()
47            throws IOException, InterruptedException {
48        ProcessBuilder builder = new ProcessBuilder();
49
50        // construct the name of executable file
51        builder.command().add(againstDalvik ? "dalvikvm" : "java");
52
53        // parse hy.test.vmargs if was given
54        String testVMArgs = System.getProperty("hy.test.vmargs");
55        if (testVMArgs != null) {
56            builder.command().addAll(Arrays.asList(testVMArgs.split("\\s+")));
57        }
58
59        return builder;
60    }
61
62    /**
63     * Returns a command-line ready path formed by joining the path elements
64     * with the system path separator as a separator.
65     */
66    public static String createPath(String... elements) {
67        StringBuilder result = new StringBuilder();
68        for (String element : elements) {
69            result.append(File.pathSeparator);
70            result.append(element);
71        }
72        return result.toString();
73    }
74
75    /**
76     * Starts the specified process, collects its output from standard out and
77     * standard err, and returns. If the stream emits anything to standard err,
78     * an AssertionFailedError will be thrown.
79     *
80     * <p>This method assumes the target process will complete within ten
81     * seconds. If it does not, an AssertionFailedError will be thrown.
82     */
83    public static String execAndGetOutput(ProcessBuilder builder) throws IOException {
84        Process process = builder.start();
85        ExecutorService executorService = Executors.newFixedThreadPool(2);
86        try {
87            Future<String> errFuture = executorService.submit(
88                    streamToStringCallable(process.getErrorStream()));
89            Future<String> outFuture = executorService.submit(
90                    streamToStringCallable(process.getInputStream()));
91
92            Throwable failure;
93            String out = "";
94            try {
95                out = outFuture.get(10, TimeUnit.SECONDS);
96                String err = errFuture.get(10, TimeUnit.SECONDS);
97                failure = err.length() > 0
98                        ? new AssertionFailedError("Unexpected err stream data:\n" + err)
99                        : null;
100            } catch (Exception e) {
101                failure = e;
102            }
103
104            if (failure != null) {
105                AssertionFailedError error = new AssertionFailedError(
106                        "Failed to execute " + builder.command() + "; output was:\n" + out);
107                error.initCause(failure);
108                throw error;
109            } else {
110                return out;
111            }
112        } finally {
113            executorService.shutdown();
114        }
115    }
116
117    /**
118     * Starts the process described by 'builder', and asserts that it sees
119     * 'expectedOut' on stdout and 'expectedErr' on stderr. Times out after
120     * 10s.
121     */
122    public static void execAndCheckOutput(ProcessBuilder builder,
123            String expectedOut, String expectedErr) throws Exception {
124        Process process = builder.start();
125        ExecutorService executorService = Executors.newFixedThreadPool(2);
126        try {
127            Future<String> errFuture =
128                    executorService.submit(streamToStringCallable(process.getErrorStream()));
129            Future<String> outFuture =
130                    executorService.submit(streamToStringCallable(process.getInputStream()));
131            assertEquals(expectedOut, outFuture.get(10, TimeUnit.SECONDS));
132            assertEquals(expectedErr, errFuture.get(10, TimeUnit.SECONDS));
133        } finally {
134            executorService.shutdown();
135            process.waitFor();
136        }
137    }
138
139    private static Callable<String> streamToStringCallable(final InputStream in) {
140        return new Callable<String>() {
141            public String call() throws Exception {
142                StringWriter writer = new StringWriter();
143                Reader reader = new InputStreamReader(in);
144                int c;
145                while ((c = reader.read()) != -1) {
146                    writer.write(c);
147                }
148                return writer.toString();
149            }
150        };
151    }
152}
153