/* * Copyright 2016-present Facebook, Inc. * * 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 com.facebook.buck.jvm.java.abi.source; import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiTest; import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiTestRunner; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.sun.source.tree.CompilationUnitTree; import java.io.IOException; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(CompilerTreeApiTestRunner.class) public class InterfaceValidatorTest extends CompilerTreeApiTest { @Test public void testSimpleClassPasses() throws IOException { compileWithValidation("public class Foo { }"); assertNoErrors(); } @Test public void testImplicitAnnotationSuperclassSucceeds() throws IOException { compileWithValidation("@interface Foo { };"); assertNoErrors(); } @Test public void testFullyQualifiedNameFromBootClasspathSucceeds() throws IOException { compileWithValidation("abstract class Foo implements java.util.List { }"); assertNoErrors(); } @Test public void testFullyQualifiedNameFromClasspathSucceeds() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/bar/Bar.java", Joiner.on('\n').join("package com.facebook.bar;", "public class Bar { }"))); compileWithValidation("class Foo extends com.facebook.bar.Bar { }"); assertNoErrors(); } @Test public void testSelfSucceeds() throws IOException { compileWithValidation(Joiner.on('\n').join("class Foo {", " Foo f;", "}")); assertNoErrors(); } @Test public void testSiblingTypeSucceeds() throws IOException { compileWithValidation( Joiner.on('\n') .join("class Foo {", " class Bar {", " Baz b;", " }", " class Baz { }", "}")); assertNoErrors(); } @Test public void testSiblingOfParentTypeSucceeds() throws IOException { compileWithValidation( Joiner.on('\n') .join( "class Foo {", " class Bar {", " class Baz {", " Waz w;", " }", " }", " class Waz { }", "}")); assertNoErrors(); } @Test public void testPackageReferenceFromInnerTypeSucceeds() throws IOException { compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n') .join( "package com.facebook.foo;", "class Foo {", " class Inner {", " Bar b;", " }", "}"), "Bar.java", Joiner.on('\n').join("package com.facebook.foo;", "class Bar { }"))); assertNoErrors(); } @Test public void testStarImportedTypeFromClasspathFails() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/bar/Bar.java", Joiner.on('\n').join("package com.facebook.bar;", "public class Bar { }"))); compileWithValidation( Joiner.on('\n') .join("import com.facebook.bar.*;", "public abstract class Foo extends Bar { }")); assertErrors( Joiner.on('\n') .join( "Foo.java:2: error: Must qualify the name: com.facebook.bar.Bar", "public abstract class Foo extends Bar { }", " ^")); } @Test public void testStarImportedTypeCompiledTogetherSucceeds() throws IOException { compileWithValidation( ImmutableMap.of( "com/facebook/bar/Bar.java", Joiner.on('\n').join("package com.facebook.bar;", "public class Bar { }"), "Foo.java", Joiner.on('\n') .join("import com.facebook.bar.*;", "public abstract class Foo extends Bar { }"))); assertNoErrors(); } @Test public void testStarImportedTypeFromBootClasspathFails() throws IOException { this.compileWithValidation( Joiner.on('\n') .join("import java.util.*;", "public abstract class Foo implements List<String> { }")); assertNoErrors(); } @Test public void testOwnMemberTypeSucceeds() throws IOException { compileWithValidation( Joiner.on('\n') .join( "package com.facebook.foo;", "public class Foo {", " Inner i;", " class Inner { }", "}")); assertNoErrors(); } @Test public void testSuperclassMemberTypeFromClasspathFails() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/bar/Bar.java", Joiner.on('\n') .join( "package com.facebook.bar;", "import com.facebook.foo.Baz;", "public class Bar implements Baz { }"), "com/facebook/foo/Baz.java", Joiner.on('\n') .join( "package com.facebook.foo;", "public interface Baz { interface Inner { } }"))); compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n') .join( "package com.facebook.foo;", "import com.facebook.bar.Bar;", "class Foo extends Bar {", " Inner i;", "}"))); assertError( Joiner.on('\n') .join("Foo.java:4: error: Must qualify the name: Baz.Inner", " Inner i;", " ^")); } @Test public void testImportedSuperclassMemberTypeFromClasspathSucceeds() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/bar/Bar.java", Joiner.on('\n') .join( "package com.facebook.bar;", "import com.facebook.foo.Baz;", "public class Bar implements Baz { }"), "com/facebook/foo/Baz.java", Joiner.on('\n') .join( "package com.facebook.foo;", "public interface Baz { interface Inner { } }"))); compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n') .join( "package com.facebook.foo;", "import com.facebook.bar.Bar;", "import com.facebook.foo.Baz.Inner;", "class Foo extends Bar {", " Inner i;", "}"))); assertNoErrors(); } @Test public void testPackageMemberTypeCompiledTogetherSucceeds() throws IOException { compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n').join("package com.facebook.foo;", "class Foo extends Bar { }"), "Bar.java", Joiner.on('\n').join("package com.facebook.foo;", "class Bar { }"))); assertNoErrors(); } @Test public void testPackageMemberTypeFromClasspathSucceeds() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/foo/Bar.java", Joiner.on('\n').join("package com.facebook.foo;", "class Bar { }"))); compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n').join("package com.facebook.foo;", "class Foo extends Bar { }"))); assertNoErrors(); } @Test public void testQualifiedPackageMemberInnerTypeFromClasspathSucceeds() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/foo/Bar.java", Joiner.on('\n') .join("package com.facebook.foo;", "class Bar { static class Inner { } }"))); compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n').join("package com.facebook.foo;", "class Foo extends Bar.Inner { }"))); assertNoErrors(); } @Test public void testUnqualifiedPackageMemberInnerTypeFromClasspathFails() throws IOException { withClasspath( ImmutableMap.of( "com/facebook/foo/Bar.java", Joiner.on('\n') .join("package com.facebook.foo;", "class Bar { static class Inner { } }"))); compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n') .join("package com.facebook.foo;", "class Foo extends Bar {", " Inner i;", "}"))); assertError( Joiner.on('\n') .join("Foo.java:3: error: Must qualify the name: Bar.Inner", " Inner i;", " ^")); } @Test public void testConstantCompiledTogetherSucceeds() throws IOException { compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n') .join( "class Foo {", " public static final int CONSTANT = Constants.CONSTANT + 1 + Constants.CONSTANT2;", "}"), "Constants.java", Joiner.on('\n') .join( "class Constants {", " public static final int CONSTANT = 3 + 5;", " public static final int CONSTANT2 = 3;", "}"))); assertNoErrors(); } @Test public void testConstantFromClasspathFails() throws IOException { withClasspath( ImmutableMap.of( "Constants.java", Joiner.on('\n') .join( "class Constants {", " public static final int CONSTANT = 3 + 5;", " public static final int CONST2 = 3;", "}"))); compileWithValidation( ImmutableMap.of( "Foo.java", Joiner.on('\n') .join( "class Foo {", " public static final int CONSTANT = Constants.CONSTANT + 1 + Constants.CONST2;", "}"))); assertErrors( Joiner.on('\n') .join( "Foo.java:2: error: Must inline the constant value: 8", " public static final int CONSTANT = Constants.CONSTANT + 1 + Constants.CONST2;", " ^"), Joiner.on('\n') .join( "Foo.java:2: error: Must inline the constant value: 3", " public static final int CONSTANT = Constants.CONSTANT + 1 + Constants.CONST2;", " ^")); } protected Iterable<? extends CompilationUnitTree> compileWithValidation(String source) throws IOException { return compileWithValidation(ImmutableMap.of("Foo.java", source)); } protected Iterable<? extends CompilationUnitTree> compileWithValidation( Map<String, String> sources) throws IOException { return compile( sources, // A side effect of our hacky test class loader appears to be that this only works if // it's NOT a lambda. LoL. new ValidatingTaskListenerFactory()); } }