1/*
2 * Copyright (c) 2010, 2017, 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                try (PrintStream outputDest = new PrintStream(new FileOutputStream(errorFileName))) {
122                    TestHelper.dumpFile(outputDest, new StringReader(new String(err.toByteArray())));
123                    outputDest.println("--");
124                }
125            } else {
126                log(new String(err.toByteArray()));
127            }
128
129            if (errors != 0 && !expectCompileFailure) {
130                fail(String.format("%d errors compiling %s", errors, testFile));
131            }
132            if (checkCompilerMsg) {
133                compare(errorFileName, expectedFileName, true);
134            }
135        }
136
137        if (expectCompileFailure && errors == 0) {
138            fail(String.format("No errors encountered compiling negative test %s", testFile));
139        }
140    }
141
142    private void executeInThisProcess() {
143        final List<String> args = getRuntimeArgs();
144        final File outputFileHandle = new File(outputFileName);
145        final File errorFileHandle  = new File(errorFileName);
146
147        try (OutputStream outputFile = new FileOutputStream(outputFileName); OutputStream errorFile = new FileOutputStream(errorFileName)) {
148            final int errors = evaluateScript(outputFile, errorFile, args.toArray(new String[0]));
149
150            if (errors != 0 || errorFileHandle.length() > 0) {
151                if (expectRunFailure) {
152                    return;
153                }
154
155                if (!ignoreStdError) {
156                    if (outputFileHandle.length() > 0) {
157                        TestHelper.dumpFile(outputFileHandle);
158                    }
159                    fail(TestHelper.fullContent(errorFileHandle));
160                }
161            }
162
163            if (compare) {
164                compare(outputFileName, expectedFileName, false);
165            }
166        } catch (final IOException e) {
167            if (!expectRunFailure) {
168                fail("Failure running test " + testFile + ": " + e.getMessage());
169                // else success
170            }
171        }
172    }
173
174    private void executeInNewProcess() {
175
176        final String separator = System.getProperty("file.separator");
177        final List<String> cmd = new ArrayList<>();
178
179        cmd.add(System.getProperty("java.home") + separator + "bin" + separator + "java");
180        for (final String str : forkJVMOptions) {
181            if(!str.isEmpty()) {
182                cmd.add(str);
183        }
184        }
185        cmd.add(Shell.class.getName());
186        // now add the rest of the "in process" runtime arguments
187        cmd.addAll(getRuntimeArgs());
188
189        final File outputFileHandle = new File(outputFileName);
190        final File errorFileHandle = new File(errorFileName);
191
192        try {
193            final ProcessBuilder pb = new ProcessBuilder(cmd);
194            pb.redirectOutput(outputFileHandle);
195            pb.redirectError(errorFileHandle);
196            final Process process = pb.start();
197
198            final int exitCode = process.waitFor();
199
200            if (exitCode != 0 || errorFileHandle.length() > 0) {
201                if (expectRunFailure) {
202                    return;
203                }
204                if (!ignoreStdError) {
205                    if (outputFileHandle.length() > 0) {
206                        TestHelper.dumpFile(outputFileHandle);
207                    }
208                    fail(TestHelper.fullContent(errorFileHandle));
209                }
210            }
211
212            if (compare) {
213                compare(outputFileName, expectedFileName, false);
214            }
215        } catch (final IOException | InterruptedException e) {
216            if (!expectRunFailure) {
217                fail("Failure running test " + testFile + ": " + e.getMessage());
218                // else success
219            }
220        }
221    }
222
223    private void compare(final String outputFileName0, final String expectedFileName0, final boolean compareCompilerMsg) throws IOException {
224        final File expectedFile = new File(expectedFileName0);
225
226        BufferedReader expected;
227        if (expectedFile.exists()) {
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            expected = new BufferedReader(new InputStreamReader(new FileInputStream(expectedFileName0)));
239        } else {
240            expected = new BufferedReader(new StringReader(""));
241        }
242
243        try (BufferedReader actual = new BufferedReader(new InputStreamReader(new FileInputStream(outputFileName0)));
244             BufferedReader expected0 = expected){
245            compare(actual, expected0, compareCompilerMsg);
246        }
247    }
248}
249