/* * Copyright 2010-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.kotlin.cli; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import kotlin.Pair; import kotlin.collections.CollectionsKt; import kotlin.io.FilesKt; import kotlin.text.Charsets; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.cli.common.CLICompiler; import org.jetbrains.kotlin.cli.common.ExitCode; import org.jetbrains.kotlin.cli.js.K2JSCompiler; import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler; import org.jetbrains.kotlin.config.KotlinCompilerVersion; import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion; import org.jetbrains.kotlin.test.InTextDirectivesUtils; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.TestCaseWithTmpdir; import org.jetbrains.kotlin.utils.ExceptionUtilsKt; import org.jetbrains.kotlin.utils.JsMetadataVersion; import org.jetbrains.kotlin.utils.PathUtil; import org.jetbrains.kotlin.utils.StringsKt; import org.junit.Assert; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; public abstract class AbstractCliTest extends TestCaseWithTmpdir { @NotNull public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLICompiler<?> compiler, @NotNull List<String> args) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); PrintStream origErr = System.err; try { System.setErr(new PrintStream(bytes)); ExitCode exitCode = CLICompiler.doMainNoExit(compiler, ArrayUtil.toStringArray(args)); return new Pair<>(bytes.toString("utf-8"), exitCode); } catch (Exception e) { throw ExceptionUtilsKt.rethrow(e); } finally { System.setErr(origErr); } } @NotNull public static String getNormalizedCompilerOutput(@NotNull String pureOutput, @NotNull ExitCode exitCode, @NotNull String testDataDir) { String testDataAbsoluteDir = new File(testDataDir).getAbsolutePath(); String normalizedOutputWithoutExitCode = StringUtil.convertLineSeparators(pureOutput) .replace(testDataAbsoluteDir, "$TESTDATA_DIR$") .replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), "$TESTDATA_DIR$") .replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$") .replace("expected version is " + JvmMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$") .replace("expected version is " + JsMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$") .replace("\\", "/") .replace(KotlinCompilerVersion.VERSION, "$VERSION$"); return normalizedOutputWithoutExitCode + exitCode; } private void doTest(@NotNull String fileName, @NotNull CLICompiler<?> compiler) throws Exception { System.setProperty("java.awt.headless", "true"); Pair<String, ExitCode> outputAndExitCode = executeCompilerGrabOutput(compiler, readArgs(fileName, tmpdir.getPath())); String actual = getNormalizedCompilerOutput( outputAndExitCode.getFirst(), outputAndExitCode.getSecond(), new File(fileName).getParent() ); File outFile = new File(fileName.replaceFirst("\\.args$", ".out")); KotlinTestUtils.assertEqualsToFile(outFile, actual); File additionalTestConfig = new File(fileName.replaceFirst("\\.args$", ".test")); if (additionalTestConfig.exists()) { doTestAdditionalChecks(additionalTestConfig); } } private void doTestAdditionalChecks(@NotNull File testConfigFile) throws IOException { List<String> diagnostics = new ArrayList<>(0); String content = FilesKt.readText(testConfigFile, Charsets.UTF_8); List<String> existsList = InTextDirectivesUtils.findListWithPrefixes(content, "// EXISTS: "); for (String fileName : existsList) { File file = new File(tmpdir, fileName); if (!file.exists()) { diagnostics.add("File does not exist, but should: " + fileName); } else if (!file.isFile()) { diagnostics.add("File is a directory, but should be a normal file: " + fileName); } } List<String> absentList = InTextDirectivesUtils.findListWithPrefixes(content, "// ABSENT: "); for (String fileName : absentList) { File file = new File(tmpdir, fileName); if (file.exists() && file.isFile()) { diagnostics.add("File exists, but shouldn't: " + fileName); } } if (!diagnostics.isEmpty()) { diagnostics.add(0, diagnostics.size() + " problem(s) found:"); Assert.fail(StringsKt.join(diagnostics, "\n")); } } @NotNull static List<String> readArgs(@NotNull String argsFilePath, @NotNull String tempDir) throws IOException { List<String> lines = FilesKt.readLines(new File(argsFilePath), Charsets.UTF_8); return CollectionsKt.mapNotNull(lines, arg -> { if (arg.isEmpty()) { return null; } // Do not replace ':' after '\' (used in compiler plugin tests) String argsWithColonsReplaced = arg .replace("\\:", "$COLON$") .replace(":", File.pathSeparator) .replace("$COLON$", ":"); return argsWithColonsReplaced .replace("$TEMP_DIR$", tempDir) .replace("$TESTDATA_DIR$", new File(argsFilePath).getParent()); }); } protected void doJvmTest(@NotNull String fileName) throws Exception { doTest(fileName, new K2JVMCompiler()); } protected void doJsTest(@NotNull String fileName) throws Exception { doTest(fileName, new K2JSCompiler()); } public static String removePerfOutput(String output) { String[] lines = StringUtil.splitByLinesKeepSeparators(output); StringBuilder result = new StringBuilder(); for (String line : lines) { if (!line.contains("PERF:")) { result.append(line); } } return result.toString(); } }