1/*
2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.test.framework;
27
28import java.io.BufferedReader;
29import java.io.ByteArrayOutputStream;
30import java.io.File;
31import java.io.FileInputStream;
32import java.io.FileOutputStream;
33import java.io.IOException;
34import java.io.InputStreamReader;
35import java.io.OutputStream;
36import java.io.PrintStream;
37import java.io.PrintWriter;
38import java.io.StringReader;
39import java.nio.file.FileSystems;
40import java.nio.file.Files;
41import java.nio.file.StandardCopyOption;
42import java.util.ArrayList;
43import java.util.List;
44import java.util.Map;
45import jdk.nashorn.tools.Shell;
46import org.testng.Assert;
47import org.testng.ITest;
48import org.testng.annotations.Test;
49
50/**
51 * Compiles a single JavaScript script source file and executes the resulting
52 * class. Optionally, output from running the script is compared against the
53 * corresponding .EXPECTED file.
54 */
55@SuppressWarnings("javadoc")
56public final class ScriptRunnable extends AbstractScriptRunnable implements ITest {
57    public ScriptRunnable(final String framework, final File testFile, final List<String> engineOptions, final Map<String, String> testOptions,  final List<String> scriptArguments) {
58        super(framework, testFile, engineOptions, testOptions, scriptArguments);
59
60        if (this.shouldRun) {
61          // add --dump-on-error option always so that we can get detailed error msg.
62          engineOptions.add("-doe");
63        }
64    }
65
66    @Override
67    public String getTestName() {
68        return testFile.getAbsolutePath();
69    }
70
71    @Test
72    @Override
73    public void runTest() throws IOException {
74        try {
75            super.runTest();
76        } catch(final AssertionError e) {
77            throw new AssertionError("Failed executing test " + testFile, e);
78        }
79    }
80
81    @Override
82    protected void execute() {
83        if (fork) {
84            executeInNewProcess();
85        } else {
86            executeInThisProcess();
87        }
88    }
89
90    // avoid direct System.out.println - use reporter to capture
91    @Override
92    protected void log(final String msg) {
93        org.testng.Reporter.log(msg, true);
94    }
95
96    // throw Assert fail - but log as well so that user can see this at console
97    @Override
98    protected void fail(final String msg) {
99        log(msg);
100        Assert.fail(msg);
101    }
102
103    @Override
104    protected void compile() throws IOException {
105        final ByteArrayOutputStream out = new ByteArrayOutputStream();
106        final ByteArrayOutputStream err = new ByteArrayOutputStream();
107        final List<String> args = getCompilerArgs();
108        int errors;
109
110        try {
111            errors = evaluateScript(out, err, args.toArray(new String[0]));
112        } catch (final AssertionError e) {
113            final PrintWriter writer = new PrintWriter(err);
114            e.printStackTrace(writer);
115            writer.flush();
116            errors = 1;
117        }
118
119        if (errors != 0 || checkCompilerMsg) {
120            if (expectCompileFailure || checkCompilerMsg) {
121                final PrintStream outputDest = new PrintStream(new FileOutputStream(errorFileName));
122                TestHelper.dumpFile(outputDest, new StringReader(new String(err.toByteArray())));
123                outputDest.println("--");
124            } else {
125                log(new String(err.toByteArray()));
126            }
127
128            if (errors != 0 && !expectCompileFailure) {
129                fail(String.format("%d errors compiling %s", errors, testFile));
130            }
131            if (checkCompilerMsg) {
132                compare(errorFileName, expectedFileName, true);
133            }
134        }
135
136        if (expectCompileFailure && errors == 0) {
137            fail(String.format("No errors encountered compiling negative test %s", testFile));
138        }
139    }
140
141    private void executeInThisProcess() {
142        final List<String> args = getRuntimeArgs();
143        final File outputFileHandle = new File(outputFileName);
144        final File errorFileHandle  = new File(errorFileName);
145
146        try (OutputStream outputFile = new FileOutputStream(outputFileName); OutputStream errorFile = new FileOutputStream(errorFileName)) {
147            final int errors = evaluateScript(outputFile, errorFile, args.toArray(new String[0]));
148
149            if (errors != 0 || errorFileHandle.length() > 0) {
150                if (expectRunFailure) {
151                    return;
152                }
153
154                if (!ignoreStdError) {
155                    if (outputFileHandle.length() > 0) {
156                        TestHelper.dumpFile(outputFileHandle);
157                    }
158                    fail(TestHelper.fullContent(errorFileHandle));
159                }
160            }
161
162            if (compare) {
163                compare(outputFileName, expectedFileName, false);
164            }
165        } catch (final IOException e) {
166            if (!expectRunFailure) {
167                fail("Failure running test " + testFile + ": " + e.getMessage());
168                // else success
169            }
170        }
171    }
172
173    private void executeInNewProcess() {
174
175        final String separator = System.getProperty("file.separator");
176        final List<String> cmd = new ArrayList<>();
177
178        cmd.add(System.getProperty("java.home") + separator + "bin" + separator + "java");
179        for (final String str : forkJVMOptions) {
180            if(!str.isEmpty()) {
181                cmd.add(str);
182        }
183        }
184        cmd.add(Shell.class.getName());
185        // now add the rest of the "in process" runtime arguments
186        cmd.addAll(getRuntimeArgs());
187
188        final File outputFileHandle = new File(outputFileName);
189        final File errorFileHandle = new File(errorFileName);
190
191        try {
192            final ProcessBuilder pb = new ProcessBuilder(cmd);
193            pb.redirectOutput(outputFileHandle);
194            pb.redirectError(errorFileHandle);
195            final Process process = pb.start();
196
197            final int exitCode = process.waitFor();
198
199            if (exitCode != 0 || errorFileHandle.length() > 0) {
200                if (expectRunFailure) {
201                    return;
202                }
203                if (!ignoreStdError) {
204                    if (outputFileHandle.length() > 0) {
205                        TestHelper.dumpFile(outputFileHandle);
206                    }
207                    fail(TestHelper.fullContent(errorFileHandle));
208                }
209            }
210
211            if (compare) {
212                compare(outputFileName, expectedFileName, false);
213            }
214        } catch (final IOException | InterruptedException e) {
215            if (!expectRunFailure) {
216                fail("Failure running test " + testFile + ": " + e.getMessage());
217                // else success
218            }
219        }
220    }
221
222    private void compare(final String outputFileName0, final String expectedFileName0, final boolean compareCompilerMsg) throws IOException {
223        final File expectedFile = new File(expectedFileName0);
224
225        BufferedReader expected;
226        if (expectedFile.exists()) {
227            expected = new BufferedReader(new InputStreamReader(new FileInputStream(expectedFileName0)));
228            // copy expected file overwriting existing file and preserving last
229            // modified time of source
230            try {
231                Files.copy(FileSystems.getDefault().getPath(expectedFileName0),
232                        FileSystems.getDefault().getPath(copyExpectedFileName),
233                        StandardCopyOption.REPLACE_EXISTING,
234                        StandardCopyOption.COPY_ATTRIBUTES);
235            } catch (final IOException ex) {
236                fail("failed to copy expected " + expectedFileName + " to " + copyExpectedFileName + ": " + ex.getMessage());
237            }
238        } else {
239            expected = new BufferedReader(new StringReader(""));
240        }
241
242        final BufferedReader actual = new BufferedReader(new InputStreamReader(new FileInputStream(outputFileName0)));
243        compare(actual, expected, compareCompilerMsg);
244    }
245}
246