/* * 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; import static javax.tools.StandardLocation.ANNOTATION_PROCESSOR_PATH; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Lists; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Path; import java.nio.file.Paths; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import org.junit.Before; import org.junit.Test; public class ClassUsageTrackerTest { private static final FileSystem WINDOWS_FILE_SYSTEM = Jimfs.newFileSystem(Configuration.windows()); private static final FileSystem UNIX_FILE_SYSTEM = Jimfs.newFileSystem(Configuration.unix()); private static final String WINDOWS_FILE_NAME = "Windows"; private static final String[] FILE_NAMES = { "A", "B", "C", "D", "E", "F", "NonJava", "OTHER", "SOURCE", "HTML" }; private static final String SINGLE_FILE_NAME = "C"; private static final String SINGLE_NON_JAVA_FILE_NAME = "NonJava"; private static final File SINGLE_FILE = new File("C"); private static final Path TEST_JAR_PATH = UNIX_FILE_SYSTEM.getPath("/test.jar"); private static final Path WINDOWS_JAR_PATH = WINDOWS_FILE_SYSTEM.getPath("C:\\test.jar"); private ClassUsageTracker tracker; private StandardJavaFileManager fileManager; private FakeStandardJavaFileManager fakeFileManager; @Before public void setUp() { tracker = new ClassUsageTracker(); fakeFileManager = new FakeStandardJavaFileManager(); fileManager = tracker.wrapFileManager(fakeFileManager); fakeFileManager.addFile(WINDOWS_JAR_PATH, WINDOWS_FILE_NAME, JavaFileObject.Kind.CLASS); for (String fileName : FILE_NAMES) { fakeFileManager.addFile(TEST_JAR_PATH, fileName, JavaFileObject.Kind.CLASS); } } @Test public void testWindowsPathsDontBlowUp() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput(null, WINDOWS_FILE_NAME, JavaFileObject.Kind.CLASS); javaFileObject.openInputStream(); assertFilesRead(WINDOWS_JAR_PATH, WINDOWS_FILE_NAME); } @Test public void readingFilesFromListShouldBeTracked() throws IOException { for (JavaFileObject javaFileObject : fileManager.list(null, null, null, false)) { javaFileObject.openInputStream(); } assertFilesRead(TEST_JAR_PATH, FILE_NAMES); } @Test public void readingFileFromGetJavaFileForInputShouldBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput(null, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS); javaFileObject.openInputStream(); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingFileFromGetJavaFileForOutputShouldBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForOutput(null, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS, null); javaFileObject.openInputStream(); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingJavaFileFromGetFileForInputShouldBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForInput(null, null, SINGLE_FILE_NAME); fileObject.openInputStream(); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingJavaFileFromGetFileForOutputShouldBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForOutput(null, null, SINGLE_FILE_NAME, null); fileObject.openInputStream(); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingNonJavaFileFromGetFileForInputShouldBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForInput(null, null, SINGLE_NON_JAVA_FILE_NAME); fileObject.openInputStream(); assertTrue(fileWasRead(TEST_JAR_PATH, SINGLE_NON_JAVA_FILE_NAME)); } @Test public void readingNonJavaFileFromGetFileForOutputShouldBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForOutput(null, null, SINGLE_NON_JAVA_FILE_NAME, null); fileObject.openInputStream(); assertTrue(fileWasRead(TEST_JAR_PATH, SINGLE_NON_JAVA_FILE_NAME)); } @Test public void readingFileFromGetJavaFileObjectsFromFilesShouldNotBeTracked() throws IOException { final Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjectsFromFiles(Lists.newArrayList(SINGLE_FILE)); for (JavaFileObject javaFileObject : javaFileObjects) { javaFileObject.openInputStream(); } assertNoFilesRead(); } @Test public void readingAnnotationProcessorFilesFromListShouldNotBeTracked() throws IOException { Iterable<JavaFileObject> listIterator = fileManager.list(ANNOTATION_PROCESSOR_PATH, null, null, false); for (JavaFileObject javaFileObject : listIterator) { javaFileObject.openInputStream(); } assertNoFilesRead(); } @Test public void readingAnnotationProcessorFileFromGetJavaFileForOutputShouldNotBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForOutput( ANNOTATION_PROCESSOR_PATH, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS, null); javaFileObject.openInputStream(); assertNoFilesRead(); } @Test public void readingAnnotationProcessorFileFromGetJavaFileForInputShouldBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput( ANNOTATION_PROCESSOR_PATH, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS); javaFileObject.openInputStream(); assertNoFilesRead(); } @Test public void readingJavaAnnotationProcessorFileFromGetFileForInputShouldNotBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForInput(ANNOTATION_PROCESSOR_PATH, null, SINGLE_FILE_NAME); fileObject.openInputStream(); assertNoFilesRead(); } @Test public void readingJavaAnnotationProcessorFileFromGetFileForOutputShouldNotBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForOutput(ANNOTATION_PROCESSOR_PATH, null, SINGLE_FILE_NAME, null); fileObject.openInputStream(); assertNoFilesRead(); } @Test public void readingNonJavaAnnotationProcessorFileFromGetFileForInputShouldNotBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForInput(ANNOTATION_PROCESSOR_PATH, null, SINGLE_NON_JAVA_FILE_NAME); fileObject.openInputStream(); assertNoFilesRead(); } @Test public void readingNonJavaAnnotationProcessorFileFromGetFileForOutputShouldNotBeTracked() throws IOException { final FileObject fileObject = fileManager.getFileForOutput( ANNOTATION_PROCESSOR_PATH, null, SINGLE_NON_JAVA_FILE_NAME, null); fileObject.openInputStream(); assertNoFilesRead(); } @Test public void readingFileFromGetJavaFileObjectsFileOverloadShouldNotBeTracked() throws IOException { final Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjects(SINGLE_FILE); for (JavaFileObject javaFileObject : javaFileObjects) { javaFileObject.openInputStream(); } assertNoFilesRead(); } @Test public void readingFileFromGetJavaFileObjectsFromStringsShouldNotBeTracked() throws IOException { final Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjectsFromStrings(Lists.newArrayList(SINGLE_FILE_NAME)); for (JavaFileObject javaFileObject : javaFileObjects) { javaFileObject.openInputStream(); } assertNoFilesRead(); } @Test public void readingFileFromGetJavaFileObjectsStringsOverloadShouldNotBeTracked() throws IOException { final Iterable<? extends JavaFileObject> javaFileObjects = fileManager.getJavaFileObjects(SINGLE_FILE_NAME); for (JavaFileObject javaFileObject : javaFileObjects) { javaFileObject.openInputStream(); } assertNoFilesRead(); } @Test public void readingFileByOpeningStreamShouldBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput(null, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS); javaFileObject.openInputStream(); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingFileByOpeningReaderShouldBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput(null, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS); javaFileObject.openReader(false); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingFileWithGetCharContentShouldBeTracked() throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput(null, SINGLE_FILE_NAME, JavaFileObject.Kind.CLASS); javaFileObject.getCharContent(false); assertFilesRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } @Test public void readingSourceFileShouldNotBeTracked() throws IOException { assertFalse(fileTypeIsTracked(JavaFileObject.Kind.SOURCE)); } @Test public void readingHTMLFileShouldNotBeTracked() throws IOException { assertFalse(fileTypeIsTracked(JavaFileObject.Kind.HTML)); } @Test public void readingOtherFileShouldNotBeTracked() throws IOException { assertFalse(fileTypeIsTracked(JavaFileObject.Kind.OTHER)); } @Test public void readingAnonymousClassShouldNotBeTracked() throws IOException { String anonymousClassName = "Foo$3.class"; fakeFileManager.addFile(TEST_JAR_PATH, anonymousClassName, JavaFileObject.Kind.CLASS); fileManager .getJavaFileForInput(null, anonymousClassName, JavaFileObject.Kind.CLASS) .getCharContent(false); assertNoFilesRead(); } @Test public void readingLocalClassShouldNotBeTracked() throws IOException { String localClassName = "Foo$3SomeLocalCLass.class"; fakeFileManager.addFile(TEST_JAR_PATH, localClassName, JavaFileObject.Kind.CLASS); fileManager .getJavaFileForInput(null, localClassName, JavaFileObject.Kind.CLASS) .getCharContent(false); assertNoFilesRead(); } private boolean fileTypeIsTracked(JavaFileObject.Kind kind) throws IOException { final JavaFileObject javaFileObject = fileManager.getJavaFileForInput(null, kind.toString(), kind); javaFileObject.openInputStream(); return fileWasRead(TEST_JAR_PATH, SINGLE_FILE_NAME); } private boolean fileWasRead(Path jarPath, String fileName) { final ImmutableSetMultimap<Path, Path> classUsageMap = tracker.getClassUsageMap(); final ImmutableSet<Path> paths = classUsageMap.get(jarPath); return paths.contains(Paths.get(fileName)); } private void assertFilesRead(Path jarPath, String... files) { final ImmutableSetMultimap<Path, Path> classUsageMap = tracker.getClassUsageMap(); final ImmutableSet<Path> paths = classUsageMap.get(jarPath); assertEquals(files.length, paths.size()); for (String file : files) { assertTrue(fileWasRead(jarPath, file)); } } private void assertNoFilesRead() { assertEquals(0, tracker.getClassUsageMap().size()); } }