/* * Copyright 2015 Lukas Krejci * * 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.revapi.java; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.Nonnull; import org.junit.Assert; import org.junit.Test; import org.revapi.Element; import org.revapi.Report; import org.revapi.java.spi.Code; import org.revapi.simple.SimpleReporter; /** * @author Lukas Krejci * @since 0.1 */ public class MethodChecksTest extends AbstractJavaElementAnalyzerTest { @Test public void testMethodAdded() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/Added.java", "v2/methods/Added.java"); Assert.assertEquals(2, (int) reporter.getProblemCounters().get(Code.METHOD_ADDED.code())); Assert.assertEquals(4, (int) reporter.getProblemCounters().get(Code.METHOD_INHERITED_METHOD_MOVED_TO_CLASS.code())); Assert.assertEquals(2, (int) reporter.getProblemCounters().get(Code.METHOD_ADDED_TO_INTERFACE.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_STATIC_METHOD_ADDED_TO_INTERFACE.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_ABSTRACT_METHOD_ADDED.code())); } @Test public void testMethodRemoved() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v2/methods/Added.java", "v1/methods/Added.java"); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_NOW_ABSTRACT.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_NOW_FINAL.code())); Assert.assertEquals(4, (int) reporter.getProblemCounters().get(Code.METHOD_MOVED_TO_SUPERCLASS.code())); Assert.assertEquals(6, (int) reporter.getProblemCounters().get(Code.METHOD_REMOVED.code())); } @Test public void testDefaultValueChangedCheck() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/DefaultValue.java", "v2/methods/DefaultValue.java"); Assert.assertEquals(6, (int) reporter.getProblemCounters().get(Code.METHOD_DEFAULT_VALUE_CHANGED.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_DEFAULT_VALUE_ADDED.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_DEFAULT_VALUE_REMOVED.code())); } @Test public void testAnnotationTypeAttributeAdded() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/DefaultValue.java", "v2/methods/DefaultValue.java"); Assert.assertEquals(1, (int) reporter.getProblemCounters() .get(Code.METHOD_ATTRIBUTE_WITH_DEFAULT_ADDED_TO_ANNOTATION_TYPE.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters() .get(Code.METHOD_ATTRIBUTE_WITH_NO_DEFAULT_ADDED_TO_ANNOTATION_TYPE.code())); Assert.assertNull(reporter.getProblemCounters().get(Code.METHOD_ABSTRACT_METHOD_ADDED.code())); } @Test public void testAnnotationTypeAttributeRemoved() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v2/methods/DefaultValue.java", "v1/methods/DefaultValue.java"); Assert.assertEquals(2, (int) reporter.getProblemCounters() .get(Code.METHOD_ATTRIBUTE_REMOVED_FROM_ANNOTATION_TYPE.code())); Assert.assertNull(reporter.getProblemCounters().get(Code.METHOD_REMOVED.code())); } @Test public void testMethodFinality() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/Final.java", "v2/methods/Final.java"); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_NOW_FINAL.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_NO_LONGER_FINAL.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_FINAL_METHOD_ADDED_TO_NON_FINAL_CLASS.code())); } @Test public void testVisibilityChanges() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/Visibility.java", "v2/methods/Visibility.java"); Assert.assertEquals(2, (int) reporter.getProblemCounters().get(Code.METHOD_VISIBILITY_INCREASED.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_VISIBILITY_REDUCED.code())); } @Test public void testReturnTypeChanges() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/ReturnType.java", "v2/methods/ReturnType.java"); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_RETURN_TYPE_CHANGED.code())); Assert.assertEquals(2, (int) reporter.getProblemCounters().get(Code.METHOD_RETURN_TYPE_CHANGED_COVARIANTLY.code())); Assert.assertEquals(3, (int) reporter.getProblemCounters().get(Code.METHOD_RETURN_TYPE_TYPE_PARAMETERS_CHANGED.code())); } @Test public void testNofParamsChanged() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/NofParams.java", "v2/methods/NofParams.java"); Assert .assertEquals(2, (int) reporter.getProblemCounters().get(Code.METHOD_NUMBER_OF_PARAMETERS_CHANGED.code())); } @Test public void testParamTypeChanged() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/ParamType.java", "v2/methods/ParamType.java"); Assert.assertEquals(3, (int) reporter.getProblemCounters().get(Code.METHOD_PARAMETER_TYPE_CHANGED.code())); } @Test public void testStaticMethod() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/Static.java", "v2/methods/Static.java"); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_NOW_STATIC.code())); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_NO_LONGER_STATIC.code())); } @Test public void testExceptionsThrown() throws Exception { ProblemOccurrenceReporter reporter = new ProblemOccurrenceReporter(); runAnalysis(reporter, "v1/methods/Exceptions.java", "v2/methods/Exceptions.java"); Assert.assertEquals(1, (int) reporter.getProblemCounters().get(Code.METHOD_CHECKED_EXCEPTION_ADDED.code())); Assert.assertEquals(3, (int) reporter.getProblemCounters().get(Code.METHOD_CHECKED_EXCEPTION_REMOVED.code())); Assert.assertEquals(2, (int) reporter.getProblemCounters().get(Code.METHOD_RUNTIME_EXCEPTION_ADDED.code())); Assert.assertEquals(4, (int) reporter.getProblemCounters().get(Code.METHOD_RUNTIME_EXCEPTION_REMOVED.code())); } @Test public void testDefaultMethod() throws Exception { List<Report> reports = new ArrayList<>(); CollectingReporter reporter = new CollectingReporter(reports); runAnalysis(reporter, "v1/methods/DefaultMethod.java", "v2/methods/DefaultMethod.java"); Assert.assertEquals(6, reports.size()); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void DefaultMethod::a()", "method void DefaultMethod::a()", Code.METHOD_NO_LONGER_DEFAULT, Code.METHOD_NOW_ABSTRACT))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void DefaultMethod::a() @ DefaultMethod.Test", "method void DefaultMethod.Test::a()", Code.METHOD_INHERITED_METHOD_MOVED_TO_CLASS))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method int DefaultMethod::b()", "method int DefaultMethod::b()", Code.METHOD_NOW_DEFAULT, Code.METHOD_NO_LONGER_ABSTRACT))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method int DefaultMethod.Test::b()", "method int DefaultMethod::b() @ DefaultMethod.Test", Code.METHOD_MOVED_TO_SUPERCLASS))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( null, "method void DefaultMethod::c()", Code.METHOD_DEFAULT_METHOD_ADDED_TO_INTERFACE))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( null, "method void DefaultMethod::c() @ DefaultMethod.Test", Code.METHOD_ADDED))); } @Test public void testOverloadsResolution() throws Exception { List<Report> reports = new ArrayList<>(); SimpleReporter reporter = new SimpleReporter() { @Override public void report(@Nonnull Report report) { reports.add(report); } @Override public String toString() { final StringBuilder sb = new StringBuilder("Reports"); sb.append(reports); return sb.toString(); } }; runAnalysis(reporter, "v1/methods/Overloads.java", "v2/methods/Overloads.java"); Assert.assertEquals(7, reports.size()); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method int Overloads::a(int)", "method double Overloads::a(int)", Code.METHOD_RETURN_TYPE_CHANGED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void Overloads::a()", "method double Overloads::a()", Code.METHOD_RETURN_TYPE_CHANGED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void Overloads::a(int, long)", "method void Overloads::a(int, long, float)", Code.METHOD_NUMBER_OF_PARAMETERS_CHANGED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void Overloads::a(int, long, double, float)", "method void Overloads::a(long, int)", Code.METHOD_NUMBER_OF_PARAMETERS_CHANGED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method parameter void Overloads::b(===java.lang.Class<? extends java.lang.Integer>===, java.lang.Object)", "method parameter void Overloads::b(===java.lang.Class<?>===, java.lang.Object)", Code.METHOD_PARAMETER_TYPE_PARAMETER_CHANGED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method parameter void Overloads::c(java.lang.Class<java.lang.Long>, ===java.lang.Class<? extends java.lang.Integer>===, float)", "method parameter void Overloads::c(java.lang.Class<java.lang.Long>, ===int===, float)", Code.METHOD_PARAMETER_TYPE_CHANGED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method parameter void Overloads::c(java.lang.Class<? extends java.lang.Integer>, ===java.lang.Class<java.lang.Long>===, int)", "method parameter void Overloads::c(java.lang.Class<? extends java.lang.Integer>, ===java.lang.Class<?>===, int)", Code.METHOD_PARAMETER_TYPE_PARAMETER_CHANGED))); } @Test public void testAbstractMethod() throws Exception { ArrayList<Report> reports = new ArrayList<>(); CollectingReporter reporter = new CollectingReporter(reports); runAnalysis(reporter, "v1/methods/Abstract.java", "v2/methods/Abstract.java"); Assert.assertEquals(6, reports.size()); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "class Abstract.PubliclyUsedPrivateSuperClass", "class Abstract.PubliclyUsedPrivateSuperClass", Code.CLASS_NON_PUBLIC_PART_OF_API))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( null, "method void Abstract.A::method()", Code.METHOD_ADDED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( null, "method void Abstract.B::method()", Code.METHOD_ADDED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( null, "method void Abstract.C::method()", Code.METHOD_ADDED))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void Abstract::abstractMethod()", "method void Abstract::abstractMethod()", Code.METHOD_NO_LONGER_ABSTRACT))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void Abstract::concreteMethod()", "method void Abstract::concreteMethod()", Code.METHOD_NOW_ABSTRACT))); } @Test public void testInheritedMethodsWithExceptions() throws Exception { ArrayList<Report> reports = new ArrayList<>(); CollectingReporter reporter = new CollectingReporter(reports); runAnalysis(reporter, "v1/methods/ExceptionsAndInheritance.java", "v2/methods/ExceptionsAndInheritance.java"); Assert.assertEquals(5, reports.size()); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void ExceptionsAndInheritance.ChildWithNoExceptions::abstractUnchecked() throws java.lang.IllegalArgumentException", "method void ExceptionsAndInheritance.ChildWithNoExceptions::abstractUnchecked()", Code.METHOD_RUNTIME_EXCEPTION_REMOVED ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void ExceptionsAndInheritance.Base::concreteChecked() throws java.io.IOException @ ExceptionsAndInheritance.ChildWithNoExceptions", "method void ExceptionsAndInheritance.ChildWithNoExceptions::concreteChecked()", Code.METHOD_INHERITED_METHOD_MOVED_TO_CLASS, Code.METHOD_CHECKED_EXCEPTION_REMOVED ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void ExceptionsAndInheritance.Base::concreteUnchecked() throws java.lang.IllegalArgumentException @ ExceptionsAndInheritance.ChildWithNoExceptions", "method void ExceptionsAndInheritance.ChildWithNoExceptions::concreteUnchecked() throws java.lang.IllegalArgumentException", Code.METHOD_INHERITED_METHOD_MOVED_TO_CLASS ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void ExceptionsAndInheritance.ChildWithSpecializedExceptions::abstractUnchecked()", "method void ExceptionsAndInheritance.ChildWithSpecializedExceptions::abstractUnchecked() throws java.lang.IllegalStateException", Code.METHOD_RUNTIME_EXCEPTION_ADDED ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method void ExceptionsAndInheritance.Base::concreteUnchecked() throws java.lang.IllegalArgumentException @ ExceptionsAndInheritance.ChildWithSpecializedExceptions", "method void ExceptionsAndInheritance.Base::concreteUnchecked() @ ExceptionsAndInheritance.ChildWithSpecializedExceptions", Code.METHOD_RUNTIME_EXCEPTION_REMOVED ))); } @Test public void testInheritedMethodsWithCovariantReturnTypes() throws Exception { ArrayList<Report> reports = new ArrayList<>(); CollectingReporter reporter = new CollectingReporter(reports); runAnalysis(reporter, "v1/methods/CovariantReturnTypeAndInheritance.java", "v2/methods/CovariantReturnTypeAndInheritance.java"); Assert.assertEquals(5, reports.size()); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method E CovariantReturnTypeAndInheritance.Class<E extends java.lang.Number>::genericMethod()", "method T CovariantReturnTypeAndInheritance.Base<T>::genericMethod() @ CovariantReturnTypeAndInheritance.Class", Code.METHOD_RETURN_TYPE_TYPE_PARAMETERS_CHANGED, Code.METHOD_MOVED_TO_SUPERCLASS ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( null, "method java.lang.Number CovariantReturnTypeAndInheritance.Class::genericMethod(int)", Code.METHOD_ADDED ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method E CovariantReturnTypeAndInheritance.Class<E extends java.lang.Number>::nonGenericMethod()", "method java.lang.Number CovariantReturnTypeAndInheritance.Base<T>::nonGenericMethod() @ CovariantReturnTypeAndInheritance.Class", Code.METHOD_RETURN_TYPE_TYPE_PARAMETERS_CHANGED, Code.METHOD_MOVED_TO_SUPERCLASS ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "class CovariantReturnTypeAndInheritance.Class<E extends java.lang.Number>", "class CovariantReturnTypeAndInheritance.Class", Code.GENERICS_FORMAL_TYPE_PARAMETER_REMOVED, Code.CLASS_SUPER_TYPE_TYPE_PARAMETERS_CHANGED ))); Assert.assertTrue(reports.stream().anyMatch(reportCheck( "method java.lang.Object CovariantReturnTypeAndInheritance.Base<T>::method() @ CovariantReturnTypeAndInheritance.Class<E extends java.lang.Number>", "method java.lang.String CovariantReturnTypeAndInheritance.Class::method()", Code.METHOD_RETURN_TYPE_CHANGED_COVARIANTLY, Code.METHOD_INHERITED_METHOD_MOVED_TO_CLASS ))); } private Predicate<Report> reportCheck(String expectedOld, String expectedNew, Code... expectedCodes) { return r -> Objects.toString(expectedOld).equals(asReadable(r.getOldElement())) && Objects.toString(expectedNew).equals(asReadable(r.getNewElement())) && r.getDifferences().size() == expectedCodes.length && Stream.of(expectedCodes).map(c -> r.getDifferences().stream().anyMatch(d -> d.code.equals(c.code()))) .reduce(true, Boolean::logicalAnd); } private static String asReadable(Element el) { return el == null ? String.valueOf((Object) null) : el.getFullHumanReadableString(); } }