CompilerDiagnosticRegressionTest.java

/*******************************************************************************
 * Copyright (c) 2026 Carsten Hammer and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which accompanies this distribution,
 * and is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
package org.sandbox.jdt.ui.tests.quickfix.rules;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Set;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import org.eclipse.core.runtime.CoreException;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaCore;

import org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring;

import org.eclipse.jdt.ui.cleanup.ICleanUp;

import org.eclipse.ltk.core.refactoring.RefactoringStatus;

/** Characterizes the default before/after compiler-diagnostic guard. */
class CompilerDiagnosticRegressionTest {

	@RegisterExtension
	final DiagnosticHarness context= new DiagnosticHarness();

	@Test
	void rejectsANewWarning() throws Exception {
		context.getJavaProject().setOption(JavaCore.COMPILER_PB_UNUSED_LOCAL, JavaCore.WARNING);
		String before= source("int value= 1; System.out.println(value);"); //$NON-NLS-1$
		String after= source("int value= 1;"); //$NON-NLS-1$
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		AssertionError failure= assertThrows(AssertionError.class, () ->
				context.assertRefactoringResultAsExpected(new ICompilationUnit[] { unit }, new String[] { after }, null));

		assertTrue(failure.getMessage().contains("WARNING"), failure.getMessage()); //$NON-NLS-1$
	}

	@Test
	void whitespaceNormalizingAssertionAlsoRejectsANewWarning() throws Exception {
		context.getJavaProject().setOption(JavaCore.COMPILER_PB_UNUSED_LOCAL, JavaCore.WARNING);
		String before= source("int value= 1; System.out.println(value);"); //$NON-NLS-1$
		String after= source("int value= 1;"); //$NON-NLS-1$
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		assertThrows(AssertionError.class, () ->
				context.assertRefactoringResultAsExpectedNormalizingWhitespace(
						new ICompilationUnit[] { unit }, new String[] { after }, null));
	}

	@Test
	void rejectsANewCompilerError() throws Exception {
		String before= source("int value= 1; System.out.println(value);"); //$NON-NLS-1$
		String after= source("int value= missing; System.out.println(value);"); //$NON-NLS-1$
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		AssertionError failure= assertThrows(AssertionError.class, () ->
				context.assertRefactoringResultAsExpected(new ICompilationUnit[] { unit }, new String[] { after }, null));

		assertTrue(failure.getMessage().contains("ERROR"), failure.getMessage()); //$NON-NLS-1$
	}

	@Test
	void rejectsAnAdditionalOccurrenceOfTheSameError() throws Exception {
		String before= source("missing();"); //$NON-NLS-1$
		String after= source("missing(); missing();"); //$NON-NLS-1$
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		AssertionError failure= assertThrows(AssertionError.class, () ->
				context.assertRefactoringResultAsExpected(new ICompilationUnit[] { unit }, new String[] { after }, null));

		assertTrue(failure.getMessage().contains("before=1 after=2"), failure.getMessage()); //$NON-NLS-1$
	}

	@Test
	void changingTheDiagnosticArgumentsIsARegression() throws Exception {
		context.getJavaProject().setOption(JavaCore.COMPILER_PB_UNUSED_LOCAL, JavaCore.WARNING);
		String before= source("int first= 1;"); //$NON-NLS-1$
		String after= source("int second= 1;"); //$NON-NLS-1$
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		assertThrows(AssertionError.class, () ->
				context.assertRefactoringResultAsExpected(new ICompilationUnit[] { unit }, new String[] { after }, null));
	}

	@Test
	void diagnosticsAreComparedPerCompilationUnit() throws Exception {
		String broken= source("First", "missing();"); //$NON-NLS-1$ //$NON-NLS-2$
		String clean= source("Second", "System.out.println(1);"); //$NON-NLS-1$ //$NON-NLS-2$
		ICompilationUnit first= createUnit("First.java", broken); //$NON-NLS-1$
		ICompilationUnit second= createUnit("Second.java", clean); //$NON-NLS-1$
		context.output(clean, broken);

		AssertionError failure= assertThrows(AssertionError.class, () ->
				context.assertRefactoringResultAsExpected(
						new ICompilationUnit[] { first, second }, new String[] { clean, broken }, null));

		assertTrue(failure.getMessage().contains("Second.java"), failure.getMessage()); //$NON-NLS-1$
	}

	@Test
	void lineMovementOfTheSameExistingErrorIsAllowed() throws Exception {
		String before= """
				package test;
				class Sample {
					void run() {
						missing();
					}
				}
				""";
		String after= """
				package test;
				class Sample {

					void run() {
						missing();
					}
				}
				""";
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		assertDoesNotThrow(() ->
				context.assertRefactoringResultAsExpected(new ICompilationUnit[] { unit }, new String[] { after }, null));
	}

	@Test
	void removingAnExistingWarningIsAllowed() throws Exception {
		context.getJavaProject().setOption(JavaCore.COMPILER_PB_UNUSED_LOCAL, JavaCore.WARNING);
		String before= source("int value= 1;"); //$NON-NLS-1$
		String after= source("int value= 1; System.out.println(value);"); //$NON-NLS-1$
		ICompilationUnit unit= createUnit(before);
		context.output(after);

		assertDoesNotThrow(() ->
				context.assertRefactoringResultAsExpected(new ICompilationUnit[] { unit }, new String[] { after }, null));
	}

	private ICompilationUnit createUnit(String source) throws CoreException {
		return createUnit("Sample.java", source); //$NON-NLS-1$
	}

	private ICompilationUnit createUnit(String name, String source) throws CoreException {
		return context.getSourceFolder().createPackageFragment("test", false, null) //$NON-NLS-1$
				.createCompilationUnit(name, source, true, null);
	}

	private static String source(String statement) {
		return source("Sample", statement); //$NON-NLS-1$
	}

	private static String source(String typeName, String statement) {
		return """
				package test;
				class TYPE {
					void run() {
						STATEMENT
					}
				}
				""".replace("TYPE", typeName).replace("STATEMENT", statement); //$NON-NLS-1$ //$NON-NLS-2$
	}

	private static final class DiagnosticHarness extends AbstractEclipseJava {
		private String[] output= new String[0];

		DiagnosticHarness() {
			super("testresources/rtstubs_22.jar", JavaCore.VERSION_22); //$NON-NLS-1$
		}

		void output(String... source) {
			output= source.clone();
		}

		@Override
		protected RefactoringStatus performRefactoring(CleanUpRefactoring ref, ICompilationUnit[] units,
				ICleanUp[] cleanUps, Set<String> expectedGroups) throws CoreException {
			if (units.length != output.length) {
				throw new CoreException(org.eclipse.core.runtime.Status.error("Missing simulated refactoring output")); //$NON-NLS-1$
			}
			for (int index= 0; index < units.length; index++) {
				units[index].getBuffer().setContents(output[index]);
			}
			return new RefactoringStatus();
		}
	}
}