/*
* Copyright 2017-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;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import org.junit.Before;
import org.junit.Test;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
public class AbiFilteringClassVisitorTest {
private ClassVisitor mockVisitor;
private AbiFilteringClassVisitor filteringVisitor;
@Before
public void setUp() {
mockVisitor = createMock(ClassVisitor.class);
filteringVisitor = new AbiFilteringClassVisitor(mockVisitor);
}
@Test
public void testExcludesPrivateFields() {
testExcludesFieldWithAccess(Opcodes.ACC_PRIVATE);
}
@Test
public void testExcludesPrivateStaticFields() {
testExcludesFieldWithAccess(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC);
}
@Test
public void testExcludesSyntheticFields() {
testExcludesFieldWithAccess(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC);
}
@Test
public void testIncludesPackageFields() {
testIncludesFieldWithAccess(0);
}
@Test
public void testIncludesPackageStaticFields() {
testIncludesFieldWithAccess(Opcodes.ACC_STATIC);
}
@Test
public void testIncludesPublicFields() {
testIncludesFieldWithAccess(Opcodes.ACC_PUBLIC);
}
@Test
public void testIncludesProtectedFields() {
testIncludesFieldWithAccess(Opcodes.ACC_PROTECTED);
}
@Test
public void testNotConfusedByOtherFieldAccessFlagsIncluding() {
testIncludesFieldWithAccess(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE);
}
@Test
public void testNotConfusedByOtherFieldAccessFlagsExcluding() {
testExcludesFieldWithAccess(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE);
}
@Test
public void testExcludesPrivateMethods() {
testExcludesMethodWithAccess(Opcodes.ACC_PRIVATE);
}
@Test
public void testIncludesPackageMethods() {
testIncludesMethodWithAccess(Opcodes.ACC_PUBLIC);
}
@Test
public void testIncludesProtectedMethods() {
testIncludesMethodWithAccess(Opcodes.ACC_PUBLIC);
}
@Test
public void testIncludesPublicMethods() {
testIncludesMethodWithAccess(Opcodes.ACC_PUBLIC);
}
@Test
public void testExcludesSyntheticMethods() {
testExcludesMethodWithAccess(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC);
}
@Test
public void testNotConfusedByOtherMethodAccessFlagsIncluding() {
testIncludesMethodWithAccess(
Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_SYNCHRONIZED);
}
@Test
public void testNotConfusedByOtherMethodAccessFlagsExcluding() {
testExcludesMethodWithAccess(
Opcodes.ACC_PRIVATE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_SYNCHRONIZED);
}
@Test
public void testExcludesStaticInitializers() {
testExcludesMethodWithAccess(Opcodes.ACC_STATIC, "<clinit>");
}
@Test
public void testAlwaysVisitsClassNode() {
visitClass(mockVisitor, "Foo");
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
verify(mockVisitor);
}
@Test
public void testIncludesInnerClassEntryForClassItself() {
visitClass(mockVisitor, "Foo$Inner");
mockVisitor.visitInnerClass("Foo$Inner", "Foo", "Inner", Opcodes.ACC_PUBLIC);
replay(mockVisitor);
visitClass(filteringVisitor, "Foo$Inner");
filteringVisitor.visitInnerClass("Foo$Inner", "Foo", "Inner", Opcodes.ACC_PUBLIC);
verify(mockVisitor);
}
@Test
public void testIncludesInnerClassEntryForInnerClass() {
visitClass(mockVisitor, "Foo");
mockVisitor.visitInnerClass("Foo$Inner", "Foo", "Inner", Opcodes.ACC_PUBLIC);
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
filteringVisitor.visitInnerClass("Foo$Inner", "Foo", "Inner", Opcodes.ACC_PUBLIC);
verify(mockVisitor);
}
@Test
public void testIncludesInnerClassEntryForOtherClassInnerClass() {
visitClass(mockVisitor, "Foo");
mockVisitor.visitInnerClass("Bar$Inner", "Bar", "Inner", Opcodes.ACC_PUBLIC);
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
filteringVisitor.visitInnerClass("Bar$Inner", "Bar", "Inner", Opcodes.ACC_PUBLIC);
verify(mockVisitor);
}
@Test
public void testExcludesPrivateInnerClasses() {
visitClass(mockVisitor, "Foo");
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
filteringVisitor.visitInnerClass("Foo$Inner", "Foo", "Inner", Opcodes.ACC_PRIVATE);
verify(mockVisitor);
}
@Test
public void testExcludesSyntheticInnerClasses() {
visitClass(mockVisitor, "Foo");
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
filteringVisitor.visitInnerClass(
"Foo$Inner", "Foo", "Inner", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC);
verify(mockVisitor);
}
@Test
public void testExcludesAnonymousInnerClasses() {
visitClass(mockVisitor, "Foo");
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
filteringVisitor.visitInnerClass("Foo$1", null, null, 0);
verify(mockVisitor);
}
@Test
public void testExcludesLocalClasses() {
visitClass(mockVisitor, "Foo");
replay(mockVisitor);
visitClass(filteringVisitor, "Foo");
filteringVisitor.visitInnerClass("Foo$1Bar", null, "Bar", 0);
verify(mockVisitor);
}
private static void visitClass(ClassVisitor cv, String name) {
cv.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name, null, "java/lang/Object", null);
}
private void testExcludesFieldWithAccess(int access) {
testFieldWithAccess(access, false);
}
private void testIncludesFieldWithAccess(int access) {
testFieldWithAccess(access, true);
}
private void testFieldWithAccess(int access, boolean shouldInclude) {
if (shouldInclude) {
expect(mockVisitor.visitField(access, "Foo", "I", null, null)).andReturn(null);
}
replay(mockVisitor);
filteringVisitor.visitField(access, "Foo", "I", null, null);
verify(mockVisitor);
}
private void testExcludesMethodWithAccess(int access) {
testExcludesMethodWithAccess(access, "foo");
}
private void testIncludesMethodWithAccess(int access) {
testIncludesMethodWithAccess(access, "foo");
}
private void testExcludesMethodWithAccess(int access, String name) {
testMethodWithAccess(access, name, false);
}
private void testIncludesMethodWithAccess(int access, String name) {
testMethodWithAccess(access, name, true);
}
private void testMethodWithAccess(int access, String name, boolean shouldInclude) {
if (shouldInclude) {
expect(mockVisitor.visitMethod(access, name, "()V", null, null)).andReturn(null);
}
replay(mockVisitor);
filteringVisitor.visitMethod(access, name, "()V", null, null);
verify(mockVisitor);
}
}