blob: 8903cbf9824dfedcd739344a072ce43af28e5dd4 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.android.ide.common.repository;
import static com.android.SdkConstants.FN_PUBLIC_TXT;
import static com.android.SdkConstants.FN_RESOURCE_TEXT;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.android.ide.common.gradle.model.IdeAndroidArtifact;
import com.android.ide.common.gradle.model.IdeAndroidLibrary;
import com.android.ide.common.gradle.model.IdeDependencies;
import com.android.ide.common.gradle.model.IdeVariant;
import com.android.resources.ResourceType;
import com.android.resources.ResourceUrl;
import com.android.testutils.TestUtils;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
public class ResourceVisibilityLookupTest extends TestCase {
public void test() throws IOException {
IdeAndroidLibrary library =
createMockLibrary(
"com.android.tools:test-library:1.0.0",
""
+ "int dimen activity_horizontal_margin 0x7f030000\n"
+ "int dimen activity_vertical_margin 0x7f030001\n"
+ "int id action_settings 0x7f060000\n"
+ "int layout activity_main 0x7f020000\n"
+ "int menu menu_main 0x7f050000\n"
+ "int string action_settings 0x7f040000\n"
+ "int string app_name 0x7f040001\n"
+ "int string hello_world 0x7f040002",
""
+ ""
+ "dimen activity_vertical\n"
+ "id action_settings\n"
+ "layout activity_main\n");
ResourceVisibilityLookup visibility = ResourceVisibilityLookup.create(library);
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_horizontal_margin"));
assertFalse(visibility.isPrivate(ResourceType.ID, "action_settings"));
assertFalse(visibility.isPrivate(ResourceType.LAYOUT, "activity_main"));
//noinspection ConstantConditions
assertTrue(visibility.isPrivate(ResourceUrl.parse("@dimen/activity_horizontal_margin")));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "activity_vertical")); // public
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "unknown")); // not in this library
}
public void testAllPrivate() throws IOException {
IdeAndroidLibrary library =
createMockLibrary(
"com.android.tools:test-library:1.0.0",
""
+ "int dimen activity_horizontal_margin 0x7f030000\n"
+ "int dimen activity_vertical_margin 0x7f030001\n"
+ "int id action_settings 0x7f060000\n"
+ "int layout activity_main 0x7f020000\n"
+ "int menu menu_main 0x7f050000\n"
+ "int string action_settings 0x7f040000\n"
+ "int string app_name 0x7f040001\n"
+ "int string hello_world 0x7f040002",
"");
ResourceVisibilityLookup visibility = ResourceVisibilityLookup.create(library);
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_horizontal_margin"));
assertTrue(visibility.isPrivate(ResourceType.ID, "action_settings"));
assertTrue(visibility.isPrivate(ResourceType.LAYOUT, "activity_main"));
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_vertical_margin"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "unknown")); // not in this library
}
public void testNotDeclared() throws IOException {
IdeAndroidLibrary library =
createMockLibrary("com.android.tools:test-library:1.0.0", "", null);
ResourceVisibilityLookup visibility = ResourceVisibilityLookup.create(library);
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "activity_horizontal_margin"));
assertFalse(visibility.isPrivate(ResourceType.ID, "action_settings"));
assertFalse(visibility.isPrivate(ResourceType.LAYOUT, "activity_main"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "activity_vertical"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "unknown")); // not in this library
}
public void testCombined() throws IOException {
IdeAndroidLibrary library1 =
createMockLibrary(
"com.android.tools:test-library:1.0.0",
""
+ "int dimen activity_horizontal_margin 0x7f030000\n"
+ "int dimen activity_vertical_margin 0x7f030001\n"
+ "int id action_settings 0x7f060000\n"
+ "int layout activity_main 0x7f020000\n"
+ "int menu menu_main 0x7f050000\n"
+ "int string action_settings 0x7f040000\n"
+ "int string app_name 0x7f040001\n"
+ "int string hello_world 0x7f040002",
"string hello_world");
IdeAndroidLibrary library2 =
createMockLibrary(
"com.android.tools:test-library2:1.0.0",
""
+ "int layout foo 0x7f030001\n"
+ "int layout bar 0x7f060000\n"
// Used public, but not explicitly declared: should remain public
// even though from the perspective of this library it looks private
// since this is a usage/override, not a declaration
+ "int string hello_world 0x7f040003",
"" + "layout foo\n");
List<IdeAndroidLibrary> androidLibraries = Arrays.asList(library1, library2);
ResourceVisibilityLookup visibility =
ResourceVisibilityLookup.create(androidLibraries, null);
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_horizontal_margin"));
assertTrue(visibility.isPrivate(ResourceType.ID, "action_settings"));
assertTrue(visibility.isPrivate(ResourceType.LAYOUT, "activity_main"));
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_vertical_margin"));
assertFalse(visibility.isPrivate(ResourceType.LAYOUT, "foo"));
assertTrue(visibility.isPrivate(ResourceType.LAYOUT, "bar"));
assertFalse(visibility.isPrivate(ResourceType.STRING, "hello_world"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "unknown")); // not in this library
}
public void testDependency() throws IOException {
IdeAndroidLibrary library1 =
createMockLibrary(
"com.android.tools:test-library:1.0.0",
""
+ "int dimen activity_horizontal_margin 0x7f030000\n"
+ "int dimen activity_vertical_margin 0x7f030001\n"
+ "int id action_settings 0x7f060000\n"
+ "int layout activity_main 0x7f020000\n"
+ "int menu menu_main 0x7f050000\n"
+ "int string action_settings 0x7f040000\n"
+ "int string app_name 0x7f040001\n"
+ "int string hello_world 0x7f040002",
"");
IdeAndroidLibrary library2 =
createMockLibrary(
"com.android.tools:test-library2:1.0.0",
"" + "int layout foo 0x7f030001\n" + "int layout bar 0x7f060000\n",
""
+ "layout foo\n" /*,
Collections.singletonList(library1)*/); // TODO(b/158836360): Review when the dependency hierarchy is available.
List<IdeAndroidLibrary> androidLibraries = Arrays.asList(library1, library2);
ResourceVisibilityLookup visibility =
ResourceVisibilityLookup.create(androidLibraries, null);
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_horizontal_margin"));
assertTrue(visibility.isPrivate(ResourceType.ID, "action_settings"));
assertTrue(visibility.isPrivate(ResourceType.LAYOUT, "activity_main"));
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "activity_vertical_margin"));
assertFalse(visibility.isPrivate(ResourceType.LAYOUT, "foo"));
assertTrue(visibility.isPrivate(ResourceType.LAYOUT, "bar"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "unknown")); // not in this library
}
public void testManager() throws IOException {
IdeAndroidLibrary library =
createMockLibrary(
"com.android.tools:test-library:1.0.0",
""
+ "int dimen activity_horizontal_margin 0x7f030000\n"
+ "int dimen activity_vertical_margin 0x7f030001\n"
+ "int id action_settings 0x7f060000\n"
+ "int layout activity_main 0x7f020000\n"
+ "int menu menu_main 0x7f050000\n"
+ "int string action_settings 0x7f040000\n"
+ "int string app_name 0x7f040001\n"
+ "int string hello_world 0x7f040002",
"");
ResourceVisibilityLookup.Provider provider = new ResourceVisibilityLookup.Provider();
assertSame(provider.get(library), provider.get(library));
assertTrue(provider.get(library).isPrivate(ResourceType.DIMEN,
"activity_horizontal_margin"));
IdeAndroidArtifact artifact = createMockArtifact(Collections.singletonList(library));
assertSame(provider.get(artifact), provider.get(artifact));
assertTrue(provider.get(artifact).isPrivate(ResourceType.DIMEN,
"activity_horizontal_margin"));
}
public void testImportedResources() throws IOException {
// Regression test for https://code.google.com/p/android/issues/detail?id=183120 :
// When a library depends on another library, all the resources from the dependency
// are imported and exposed in R.txt from the downstream library too. When both
// libraries expose public resources, we have to be careful such that we don't
// take the presence of a resource (imported) and the absence of a public.txt declaration
// (for the imported symbol in the dependent library) as evidence that this is a private
// resource.
IdeAndroidLibrary library1 =
createMockLibrary(
"com.android.tools:test-library:1.0.0",
""
+ "int dimen public_library1_resource1 0x7f030000\n"
+ "int dimen public_library1_resource2 0x7f030001\n"
+ "int dimen private_library1_resource 0x7f030002\n",
""
+ "dimen public_library1_resource1\n"
+ "dimen public_library1_resource2\n");
IdeAndroidLibrary library2 =
createMockLibrary(
"com.android.tools:test-library2:1.0.0",
""
+ "int dimen public_library2_resource1 0x7f030000\n"
+ "int dimen public_library2_resource2 0x7f030001\n",
null // nothing marked as private: everything exposed
);
IdeAndroidLibrary library3 =
createMockLibrary(
"com.android.tools:test-library3:1.0.0",
""
+ "int dimen public_library1_resource1 0x7f030000\n" // merged from
// library1
+ "int dimen public_library1_resource2 0x7f030001\n"
+ "int dimen private_library1_resource 0x7f030002\n"
+ "int dimen public_library2_resource1 0x7f030003\n" // merged from
// library2
+ "int dimen public_library2_resource2 0x7f030004\n"
+ "int dimen public_library3_resource1 0x7f030005\n" // unique to
// library3
+ "int dimen private_library3_resource 0x7f030006\n",
""
+ "dimen public_library2_resource1\n" /*,
Arrays.asList(library1, library2)*/); // TODO(b/158836360): Review when the dependency hierarchy is available.
List<IdeAndroidLibrary> androidLibraries = Arrays.asList(library1, library2, library3);
ResourceVisibilityLookup visibility =
ResourceVisibilityLookup.create(androidLibraries, null);
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "private_library1_resource"));
assertTrue(visibility.isPrivate(ResourceType.DIMEN, "private_library3_resource"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "public_library1_resource1"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "public_library1_resource2"));
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "public_library2_resource1"));
assertTrue(
visibility.isPrivate(
ResourceType.DIMEN, "public_library2_resource2")); // private in one library
assertFalse(visibility.isPrivate(ResourceType.DIMEN, "public_library3_resource"));
}
// TODO(b/158836360): Review when the dependency hierarchy is available.
// public void testSymbolProvider() throws Exception {
// IdeLibrary library1 = createMockLibrary(
// "com.android.tools:test-library:1.0.0",
// ""
// + "int dimen public_library1_resource1 0x7f030000\n"
// + "int dimen public_library1_resource2 0x7f030001\n"
// + "int dimen private_library1_resource 0x7f030002\n",
// ""
// + "dimen public_library1_resource1\n"
// + "dimen public_library1_resource2\n"
// );
//
// IdeLibrary library3 = createMockLibrary(
// "com.android.tools:test-library3:1.0.0",
// ""
// + "int dimen public_library1_resource1 0x7f030000\n" // merged from library1
// + "int dimen public_library1_resource2 0x7f030001\n"
// + "int dimen private_library1_resource 0x7f030002\n"
// + "int dimen public_library2_resource1 0x7f030003\n" // merged from library2
// + "int dimen public_library2_resource2 0x7f030004\n"
// + "int dimen public_library3_resource1 0x7f030005\n" // unique to library3
// + "int dimen private_library3_resource 0x7f030006\n",
// ""
// + "dimen public_library2_resource1\n",
// Collections.singletonList(library1)
// );
//
// SymbolProvider provider = new SymbolProvider();
// Multimap<String, ResourceType> symbols = provider.getSymbols(library3);
//
// // Exclude imported symbols
// assertFalse(symbols.get("public_library1_resource1").iterator().hasNext());
//
// // Make sure non-imported symbols are there
// assertSame(ResourceType.DIMEN,
// symbols.get("public_library3_resource1").iterator().next());
//
// // Make sure we're actually caching results
// Multimap<String, ResourceType> symbols2 = provider.getSymbols(library3);
// assertSame(symbols, symbols2);
// }
public static IdeVariant createMockVariant(IdeAndroidArtifact artifact) {
IdeVariant variant = createNiceMock(IdeVariant.class);
expect(variant.getMainArtifact()).andReturn(artifact).anyTimes();
replay(variant);
return variant;
}
public static IdeAndroidArtifact createMockArtifact(List<IdeAndroidLibrary> libraries) {
IdeDependencies dependencies = createNiceMock(IdeDependencies.class);
expect(dependencies.getAndroidLibraries()).andReturn(libraries).anyTimes();
expect(dependencies.getModuleDependencies()).andReturn(Collections.emptyList()).anyTimes();
replay(dependencies);
IdeAndroidArtifact artifact = createNiceMock(IdeAndroidArtifact.class);
expect(artifact.getLevel2Dependencies()).andReturn(dependencies).anyTimes();
replay(artifact);
return artifact;
}
public static IdeAndroidLibrary createMockLibrary(
String name, String allResources, String publicResources) throws IOException {
// Identical to PrivateResourceDetectorTest, but these are in test modules that
// can't access each other
final File tempDir = TestUtils.createTempDirDeletedOnExit().toFile();
File rFile = new File(tempDir, FN_RESOURCE_TEXT);
Files.asCharSink(rFile, Charsets.UTF_8).write(allResources);
File publicTxtFile = new File(tempDir, FN_PUBLIC_TXT);
if (publicResources != null) {
Files.asCharSink(publicTxtFile, Charsets.UTF_8).write(publicResources);
}
IdeAndroidLibrary library = mock(IdeAndroidLibrary.class);
when(library.getPublicResources()).thenReturn(publicTxtFile.getPath());
when(library.getSymbolFile()).thenReturn(rFile.getPath());
GradleCoordinate c = GradleCoordinate.parseCoordinateString(name);
assertNotNull(c);
when(library.getArtifactAddress())
.thenReturn(
c.getGroupId() + ":" + c.getArtifactId() + ":" + c.getRevision() + "@aar");
when(library.getArtifact())
.thenReturn(
new File(
"intermediates"
+ File.separator
+ "exploded-aar"
+ File.separator
+ name));
return library;
}
}