/* * Copyright 2010-2017 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.generators.tests; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.LineSeparator; import com.intellij.util.containers.ContainerUtil; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class GenerateRangesCodegenTestData { private static final File TEST_DATA_DIR = new File("compiler/testData/codegen/box/ranges"); private static final File AS_LITERAL_DIR = new File(TEST_DATA_DIR, "literal"); private static final File AS_EXPRESSION_DIR = new File(TEST_DATA_DIR, "expression"); private static final File[] SOURCE_TEST_FILES = { new File("libraries/stdlib/test/ranges/RangeIterationTest.kt"), new File("libraries/stdlib/test/ranges/RangeIterationJVMTest.kt") }; private static final Pattern TEST_FUN_PATTERN = Pattern.compile("@Test fun (\\w+)\\(\\) \\{.+?}", Pattern.DOTALL); private static final Pattern SUBTEST_INVOCATION_PATTERN = Pattern.compile("doTest\\(([^,]+), [^,]+, [^,]+, [^,]+,\\s+listOf[\\w<>]*\\(([^\\n]*)\\)\\)", Pattern.DOTALL); // $LIST.size() check is needed in order for tests not to run forever public static final String LITERAL_TEMPLATE = " val $LIST = ArrayList<$TYPE>()\n" + " for (i in $RANGE_EXPR) {\n" + " $LIST.add(i)\n" + " if ($LIST.size > 23) break\n" + " }\n" + " if ($LIST != listOf<$TYPE>($LIST_ELEMENTS)) {\n" + " return \"Wrong elements for $RANGE_EXPR_ESCAPED: $$LIST\"\n" + " }\n" + "\n"; public static final String EXPRESSION_TEMPLATE = " val $LIST = ArrayList<$TYPE>()\n" + " val $RANGE = $RANGE_EXPR\n" + " for (i in $RANGE) {\n" + " $LIST.add(i)\n" + " if ($LIST.size > 23) break\n" + " }\n" + " if ($LIST != listOf<$TYPE>($LIST_ELEMENTS)) {\n" + " return \"Wrong elements for $RANGE_EXPR_ESCAPED: $$LIST\"\n" + " }\n" + "\n"; private static final Map<String, String> ELEMENT_TYPE_KNOWN_SUBSTRINGS = new ContainerUtil.ImmutableMapBuilder<String, String>() .put("'", "Char") .put("\"", "Char") .put("Float.NaN", "Float") .put("Double.NaN", "Double") .put("MaxL", "Long") .put("MinL", "Long") .put("MaxC", "Char") .put("MinC", "Char") .build(); private static String detectElementType(String rangeExpression) { Matcher matcher = Pattern.compile("\\.to(\\w+)").matcher(rangeExpression); if (matcher.find()) { String elementType = matcher.group(1); return elementType.equals("Byte") || elementType.equals("Short") ? "Int" : elementType; } if (Pattern.compile("\\d\\.\\d").matcher(rangeExpression).find()) { return "Double"; } for (String substring : ELEMENT_TYPE_KNOWN_SUBSTRINGS.keySet()) { if (rangeExpression.contains(substring)) { return ELEMENT_TYPE_KNOWN_SUBSTRINGS.get(substring); } } return "Int"; } private static String renderTemplate(String template, int number, String elementType, String rangeExpression, String expectedListElements) { return template .replace("$RANGE_EXPR_ESCAPED", StringUtil.escapeStringCharacters(rangeExpression)) .replace("$RANGE_EXPR", rangeExpression) .replace("$LIST_ELEMENTS", expectedListElements) .replace("$LIST", "list" + number) .replace("$RANGE", "range" + number) .replace("$TYPE", elementType) .replace("\n", LineSeparator.getSystemLineSeparator().getSeparatorString()); } private static final List<String> INTEGER_PRIMITIVES = Arrays.asList("Integer", "Byte", "Short", "Long", "Character"); private static final List<String> IGNORED_FOR_JS_BACKEND = Arrays.asList( "inexactDownToMinValue.kt", "inexactToMaxValue.kt", "maxValueMinusTwoToMaxValue.kt", "maxValueToMaxValue.kt", "maxValueToMinValue.kt", "progressionDownToMinValue.kt", "progressionMaxValueMinusTwoToMaxValue.kt", "progressionMaxValueToMaxValue.kt", "progressionMaxValueToMinValue.kt", "progressionMinValueToMinValue.kt"); private static final List<String> IGNORED_FOR_NATIVE_BACKEND = Arrays.asList( "inexactDownToMinValue.kt", "inexactToMaxValue.kt", "maxValueMinusTwoToMaxValue.kt", "maxValueToMaxValue.kt", "maxValueToMinValue.kt", "progressionDownToMinValue.kt", "progressionMaxValueMinusTwoToMaxValue.kt", "progressionMaxValueToMaxValue.kt", "progressionMaxValueToMinValue.kt", "progressionMinValueToMinValue.kt" ); private static void writeIgnoreBackendDirective(PrintWriter out, String backendName) { out.printf("// TODO: muted automatically, investigate should it be ran for %s or not%n", backendName); out.printf("// IGNORE_BACKEND: %s%n%n", backendName); } private static void writeToFile(File file, String generatedBody) { PrintWriter out; try { //noinspection IOResourceOpenedButNotSafelyClosed out = new PrintWriter(file); } catch (FileNotFoundException e) { throw new AssertionError(e); } if (IGNORED_FOR_JS_BACKEND.contains(file.getName())) { writeIgnoreBackendDirective(out, "JS"); } if (IGNORED_FOR_NATIVE_BACKEND.contains(file.getName())) { writeIgnoreBackendDirective(out, "NATIVE"); } out.println("// Auto-generated by " + GenerateRangesCodegenTestData.class.getName() + ". DO NOT EDIT!"); out.println("// WITH_RUNTIME"); out.println(); if (generatedBody.contains("Max") || generatedBody.contains("Min")) { // Import min/max values, but only in case when the generated test case actually uses them (not to clutter tests which don't) out.println(); for (String primitive : INTEGER_PRIMITIVES) { out.println("import java.lang." + primitive + ".MAX_VALUE as Max" + primitive.charAt(0)); out.println("import java.lang." + primitive + ".MIN_VALUE as Min" + primitive.charAt(0)); } } out.println(); out.println("fun box(): String {"); out.print(generatedBody); out.println(" return \"OK\""); out.println("}"); out.close(); } public static void main(String[] args) { try { FileUtil.delete(AS_LITERAL_DIR); FileUtil.delete(AS_EXPRESSION_DIR); //noinspection ResultOfMethodCallIgnored AS_LITERAL_DIR.mkdirs(); //noinspection ResultOfMethodCallIgnored AS_EXPRESSION_DIR.mkdirs(); for (File file : SOURCE_TEST_FILES) { String sourceContent = FileUtil.loadFile(file); Matcher testFunMatcher = TEST_FUN_PATTERN.matcher(sourceContent); while (testFunMatcher.find()) { String testFunName = testFunMatcher.group(1); if (testFunName.equals("emptyConstant")) { continue; } String testFunText = testFunMatcher.group(); StringBuilder asLiteralBody = new StringBuilder(); StringBuilder asExpressionBody = new StringBuilder(); int index = 0; Matcher matcher = SUBTEST_INVOCATION_PATTERN.matcher(testFunText); while (matcher.find()) { index++; String rangeExpression = matcher.group(1); String expectedListElements = matcher.group(2); String elementType = detectElementType(rangeExpression); asLiteralBody.append(renderTemplate(LITERAL_TEMPLATE, index, elementType, rangeExpression, expectedListElements)); asExpressionBody.append(renderTemplate(EXPRESSION_TEMPLATE, index, elementType, rangeExpression, expectedListElements)); } String fileName = testFunName + ".kt"; writeToFile(new File(AS_LITERAL_DIR, fileName), asLiteralBody.toString()); writeToFile(new File(AS_EXPRESSION_DIR, fileName), asExpressionBody.toString()); } } } catch (IOException e) { throw new RuntimeException(e); } } private GenerateRangesCodegenTestData() { } }