From 280d12c62d6e7bc2f776109dc50e910d2bae8a17 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Fri, 17 May 2024 01:44:14 +0200 Subject: [PATCH] [test-framework] Allow deity decrees when testing: We _know_ lombok is correct, it is the tests that are wrong: Useful for small refactors that cause big test file changes. --- test/core/src/lombok/AbstractRunTests.java | 126 ++++++++++++--------- test/core/src/lombok/DirectoryRunner.java | 13 ++- test/core/src/lombok/LombokTestSource.java | 30 +++-- 3 files changed, 103 insertions(+), 66 deletions(-) diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index 0cb379ff..6877bcfa 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -25,10 +25,12 @@ import static org.junit.Assert.*; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; import java.net.URI; import java.util.ArrayList; import java.util.Collection; @@ -49,13 +51,7 @@ import lombok.transform.TestLombokFilesIdempotent; import lombok.transform.TestSourceFiles; public abstract class AbstractRunTests { - private final File dumpActualFilesHere; - - public AbstractRunTests() { - this.dumpActualFilesHere = findPlaceToDumpActualFiles(); - } - - public final FileTester createTester(final DirectoryRunner.TestParams params, final File file, String platform, int version) throws IOException { + public final FileTester createTester(final DirectoryRunner.TestParams params, final File file, String platform, int version, boolean copyToSource) throws IOException { ConfigurationKeysLoader.LoaderLoader.loadAllConfigurationKeys(); AssertionError directiveFailure = null; LombokTestSource sourceDirectives = null; @@ -107,7 +103,7 @@ public abstract class AbstractRunTests { if (params.expectChanges() && !forceUnchanged && !changed) result.addMessage(new CompilerMessage(-1, -1, true, "not flagged modified")); if (!params.expectChanges() && changed) result.addMessage(new CompilerMessage(-1, -1, true, "unexpected modification")); - compare(file.getName(), expected, result, params.printErrors(), sourceDirectives_.isSkipCompareContent() || expected.isSkipCompareContent()); + compare(file.getName(), expected, result, params.printErrors(), sourceDirectives_.isSkipCompareContent() || expected.isSkipCompareContent(), copyToSource); } }; } @@ -131,20 +127,34 @@ public abstract class AbstractRunTests { return result.toString(); } - private static File findPlaceToDumpActualFiles() { - String location = System.getProperty("lombok.tests.dump_actual_files"); - if (location != null) { - File dumpActualFilesHere = new File(location); - dumpActualFilesHere.mkdirs(); - return dumpActualFilesHere; - } - return null; - } - private static void dumpToFile(File file, String content) throws IOException { + List directives = new ArrayList(); + + if (file.exists()) { + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + try { + while (true) { + String line = br.readLine(); + if (line == null) break; + if (line.startsWith("//")) directives.add(line); + else if (!line.isEmpty()) break; + } + } finally { + br.close(); + } + } + FileOutputStream fos = new FileOutputStream(file); try { - fos.write(content.getBytes("UTF-8")); + for (String directive : directives) fos.write((directive + "\n").getBytes("UTF-8")); + + boolean atStart = true; + for (String line : content.split("(\\r?\\n)")) { + if (line.isEmpty()) continue; + if (atStart && line.startsWith("//")) continue; + atStart = false; + fos.write((line + "\n").getBytes("UTF-8")); + } } finally { fos.close(); } @@ -152,6 +162,7 @@ public abstract class AbstractRunTests { private static void dumpToFile(File file, Collection content) throws IOException { FileOutputStream fos = new FileOutputStream(file); + System.err.println(">>> DUMPING ERR MSG: " + file.getAbsolutePath()); try { for (CompilerMessage message : content) { fos.write(CompilerMessageMatcher.asCompilerMessageMatcher(message).toString().getBytes("UTF-8")); @@ -162,56 +173,67 @@ public abstract class AbstractRunTests { } } - private void compare(String name, LombokTestSource expected, TransformationResult result, boolean printErrors, boolean skipCompareContent) throws Throwable { + private void compare(String name, LombokTestSource expected, TransformationResult result, boolean printErrors, boolean skipCompareContent, boolean copyToSource) throws Throwable { String actualFile = result.getOutput(); LinkedHashSet actualMessages = result.getMessages(); if (!skipCompareContent) try { compareContent(name, expected.getContent(), actualFile); } catch (Throwable e) { - if (printErrors) { - System.out.println("***** " + name + " *****"); - System.out.println(e.getMessage()); - System.out.println("**** Expected ******"); - System.out.println(expected.getContent()); - System.out.println("**** Actual ******"); - System.out.println(actualFile); - if (actualMessages != null && !actualMessages.isEmpty()) { - System.out.println("**** Actual Errors *****"); - for (CompilerMessage actualMessage : actualMessages) { - System.out.println(actualMessage); + if (copyToSource) { + dumpToFile(expected.getSourceFile(), actualFile); + System.out.println("UPDATED: " + expected.getSourceFile()); + } else { + if (printErrors) { + System.out.println("***** " + name + " *****"); + System.out.println(e.getMessage()); + System.out.println("**** Expected ******"); + System.out.println(expected.getContent()); + System.out.println("**** Actual ******"); + System.out.println(actualFile); + if (actualMessages != null && !actualMessages.isEmpty()) { + System.out.println("**** Actual Errors *****"); + for (CompilerMessage actualMessage : actualMessages) { + System.out.println(actualMessage); + } } + System.out.println("*******************"); } - System.out.println("*******************"); + throw e; } - if (dumpActualFilesHere != null) { - dumpToFile(new File(dumpActualFilesHere, name), actualFile); - } - throw e; } try { compareMessages(name, expected.getMessages(), actualMessages); } catch (Throwable e) { - if (printErrors) { - System.out.println("***** " + name + " *****"); - System.out.println(e.getMessage()); - System.out.println("**** Expected ******"); - for (CompilerMessageMatcher expectedMessage : expected.getMessages()) { - System.out.println(expectedMessage); + if (copyToSource) { + if (actualMessages.isEmpty()) { + if (expected.getMessagesFile().exists()) { + expected.getMessagesFile().delete(); + System.out.println("DELETED: " + expected.getMessagesFile()); + } + } else { + dumpToFile(expected.getMessagesFile(), actualMessages); + System.out.println("UPDATED: " + expected.getMessagesFile()); } - System.out.println("**** Actual ******"); - for (CompilerMessage actualMessage : actualMessages) { - System.out.println(actualMessage); + } else { + if (printErrors) { + System.out.println("***** " + name + " *****"); + System.out.println(e.getMessage()); + System.out.println("**** Expected ******"); + for (CompilerMessageMatcher expectedMessage : expected.getMessages()) { + System.out.println(expectedMessage); + } + System.out.println("**** Actual ******"); + for (CompilerMessage actualMessage : actualMessages) { + System.out.println(actualMessage); + } + System.out.println("**** Actual File ******"); + System.out.println(lineNumber(actualFile)); + System.out.println("*******************"); } - System.out.println("**** Actual File ******"); - System.out.println(lineNumber(actualFile)); - System.out.println("*******************"); + throw e; } - if (dumpActualFilesHere != null) { - dumpToFile(new File(dumpActualFilesHere, name + ".messages"), actualMessages); - } - throw e; } } diff --git a/test/core/src/lombok/DirectoryRunner.java b/test/core/src/lombok/DirectoryRunner.java index 53347e24..1ed46206 100644 --- a/test/core/src/lombok/DirectoryRunner.java +++ b/test/core/src/lombok/DirectoryRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2021 The Project Lombok Authors. + * Copyright (C) 2009-2024 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,6 +42,13 @@ public class DirectoryRunner extends Runner { private static final List DEBUG_FOCUS_ON_FILE = Arrays.asList( ); + /* + * If this is set to true, you are decreeing that all tests pass, and that any test that dares to say it does not, means the test is wrong and not lombok: The framework will update the 'target' so that it now passes. + * This is, naturally, an exceedingly dangerous tool. The general way to use it is: First ensure all tests pass, then, do some minor refactor that massively impacts the tests, then, enable this, run the tests, then turn this off again, + * then, thoroughly review your commit diff. + */ + private static final boolean ITS_ALL_GOOD = false; + public enum Compiler { DELOMBOK { @Override public int getVersion() { @@ -181,10 +188,10 @@ public class DirectoryRunner extends Runner { File file = new File(params.getBeforeDirectory(), fileName); switch (params.getCompiler()) { case DELOMBOK: - return new RunTestsViaDelombok().createTester(params, file, "javac", params.getVersion()); + return new RunTestsViaDelombok().createTester(params, file, "javac", params.getVersion(), ITS_ALL_GOOD); case ECJ: String platform = RunTestsViaEcj.eclipseAvailable() ? "eclipse" : "ecj"; - return new RunTestsViaEcj().createTester(params, file, platform, params.getVersion()); + return new RunTestsViaEcj().createTester(params, file, platform, params.getVersion(), ITS_ALL_GOOD); default: case JAVAC: throw new UnsupportedOperationException(); diff --git a/test/core/src/lombok/LombokTestSource.java b/test/core/src/lombok/LombokTestSource.java index 92bc53ea..79203cd0 100644 --- a/test/core/src/lombok/LombokTestSource.java +++ b/test/core/src/lombok/LombokTestSource.java @@ -50,7 +50,8 @@ import lombok.core.configuration.ConfigurationSource; import lombok.core.configuration.SingleConfigurationSource; public class LombokTestSource { - private final File file; + private final File sourceFile; + private final File messagesFile; private final String content; private final LombokImmutableList messages; private final Map formatPreferences; @@ -86,8 +87,12 @@ public class LombokTestSource { return version >= versionLowerLimit && version <= versionUpperLimit; } - public File getFile() { - return file; + public File getSourceFile() { + return sourceFile; + } + + public File getMessagesFile() { + return messagesFile; } public String getContent() { @@ -169,8 +174,9 @@ public class LombokTestSource { private static final Pattern ISSUE_REF_PATTERN = Pattern.compile("^\\s*issue #?\\d+(:?\\s+.*)?$", Pattern.CASE_INSENSITIVE); private static final Pattern VERIFY_DIET_PATTERN = Pattern.compile("^\\s*eclipse:\\s*verify[- ]?diet\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); - private LombokTestSource(File file, String content, List messages, List directives) { - this.file = file; + private LombokTestSource(File sourceFile, File messagesFile, String content, List messages, List directives) { + this.sourceFile = sourceFile; + this.messagesFile = messagesFile; this.content = content; this.messages = messages == null ? LombokImmutableList.of() : LombokImmutableList.copyOf(messages); @@ -227,7 +233,7 @@ public class LombokTestSource { if (lc.startsWith("version ")) { int[] limits = parseVersionLimit(lc.substring(7).trim()); if (limits == null) { - Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: version must be followed by a single integer."); + Assert.fail("Directive line \"" + directive + "\" in '" + sourceFile.getAbsolutePath() + "' invalid: version must be followed by a single integer."); throw new RuntimeException(); } versionLower = limits[0]; @@ -258,7 +264,7 @@ public class LombokTestSource { if (lc.startsWith("issue ")) continue; - Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: unrecognized directive."); + Assert.fail("Directive line \"" + directive + "\" in '" + sourceFile.getAbsolutePath() + "' invalid: unrecognized directive."); throw new RuntimeException(); } this.specifiedEncoding = encoding; @@ -276,7 +282,7 @@ public class LombokTestSource { Assert.fail("Problem on directive line: " + problem + " at conf line #" + lineNumber + " (" + line + ")"); } }; - final ConfigurationFile configurationFile = ConfigurationFile.fromCharSequence(file.getAbsoluteFile().getPath(), conf, ConfigurationFile.getLastModifiedOrMissing(file)); + final ConfigurationFile configurationFile = ConfigurationFile.fromCharSequence(sourceFile.getAbsoluteFile().getPath(), conf, ConfigurationFile.getLastModifiedOrMissing(sourceFile)); final ConfigurationSource source = SingleConfigurationSource.parse(configurationFile, new ConfigurationParser(reporter)); ConfigurationFileToSource sourceFinder = new ConfigurationFileToSource() { @Override public ConfigurationSource parsed(ConfigurationFile fileLocation) { @@ -315,7 +321,7 @@ public class LombokTestSource { } } - return new LombokTestSource(file, "", null, directives); + return new LombokTestSource(file, null, "", null, directives); } public static LombokTestSource read(File sourceFolder, File messagesFolder, String fileName) throws IOException { @@ -360,8 +366,9 @@ public class LombokTestSource { if (content == null) content = new StringBuilder(); List messages = null; + File messagesFile = null; if (messagesFolder != null) { - File messagesFile = new File(messagesFolder, fileName + ".messages"); + messagesFile = new File(messagesFolder, fileName + ".messages"); try { InputStream rawIn = new FileInputStream(messagesFile); try { @@ -372,10 +379,11 @@ public class LombokTestSource { } } catch (FileNotFoundException e) { messages = null; + messagesFile = null; } } - LombokTestSource source = new LombokTestSource(sourceFile, content.toString(), messages, directives); + LombokTestSource source = new LombokTestSource(sourceFile, messagesFile, content.toString(), messages, directives); String specifiedEncoding = source.getSpecifiedEncoding(); // The source file has an 'encoding' header to test encoding issues. Of course, reading the encoding header