| // Copyright 2017 The Bazel Authors. All rights reserved. | |
| // | |
| // 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.google.devtools.build.android; | |
| import static com.google.common.truth.Truth.assertAbout; | |
| import static com.google.common.truth.Truth.assertThat; | |
| import static com.google.devtools.build.android.ParsedAndroidDataBuilder.file; | |
| import static com.google.devtools.build.android.ParsedAndroidDataBuilder.xml; | |
| import com.google.common.base.Optional; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.jimfs.Jimfs; | |
| import com.google.common.truth.Subject; | |
| import com.google.devtools.build.android.AndroidDataBuilder.ResourceType; | |
| import com.google.devtools.build.android.AndroidDataMerger.MergeConflictException; | |
| import com.google.devtools.build.android.AndroidDataMerger.SourceChecker; | |
| import com.google.devtools.build.android.xml.IdXmlResourceValue; | |
| import com.google.devtools.build.android.xml.PublicXmlResourceValue; | |
| import com.google.devtools.build.android.xml.SimpleXmlResourceValue; | |
| import com.google.devtools.build.android.xml.StyleableXmlResourceValue; | |
| import java.io.IOException; | |
| import java.nio.file.FileSystem; | |
| import java.nio.file.Path; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.logging.Handler; | |
| import java.util.logging.Level; | |
| import java.util.logging.LogRecord; | |
| import java.util.logging.Logger; | |
| import org.junit.After; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.JUnit4; | |
| /** Tests for {@link AndroidDataMerger}. */ | |
| @RunWith(JUnit4.class) | |
| public class AndroidDataMergerTest { | |
| static final String XLIFF_NAMESPACE = "urn:oasis:names:tc:xliff:document:1.2"; | |
| static final String XLIFF_PREFIX = "xliff"; | |
| private FileSystem fileSystem; | |
| private FullyQualifiedName.Factory fqnFactory; | |
| private TestLoggingHandler loggingHandler; | |
| private Logger mergerLogger; | |
| @Before | |
| public void setUp() throws Exception { | |
| fileSystem = Jimfs.newFileSystem(); | |
| fqnFactory = FullyQualifiedName.Factory.from(ImmutableList.<String>of()); | |
| mergerLogger = Logger.getLogger(AndroidDataMerger.class.getCanonicalName()); | |
| loggingHandler = new TestLoggingHandler(); | |
| mergerLogger.addHandler(loggingHandler); | |
| } | |
| @After | |
| public void removeLoggingHandler() { | |
| mergerLogger.removeHandler(loggingHandler); | |
| } | |
| @Test | |
| public void mergeDirectDeps() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| DataSource primaryStrings = DataSource.of(primaryRoot.resolve("res/values/resources.xml")); | |
| DataSource directStrings = DataSource.of(directRoot.resolve("res/values/strings.xml")); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| file("layout/exit").source("res/layout/exit.xml"), | |
| xml("string/exit") | |
| .source(directStrings) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .combining(xml("id/exit").source("values/ids.xml").value(IdXmlResourceValue.of())) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/resources.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source(primaryStrings.overwrite(directStrings)) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable(file("layout/exit").root(directRoot).source("res/layout/exit.xml")) | |
| .combining( | |
| xml("id/exit") | |
| .root(directRoot) | |
| .source("values/ids.xml") | |
| .value(IdXmlResourceValue.of())) | |
| .build()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeDirectAndTransitiveDeps() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| DataSource directString = DataSource.of(directRoot.resolve("res/values/resources.xml")); | |
| DataSource primaryString = | |
| DataSource.of(primaryRoot.resolve("res").resolve("values/resources.xml")); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable(file("layout/enter").source("res/layout/enter.xml")) | |
| .combining(xml("id/exit").source("values/ids.xml").value(IdXmlResourceValue.of())) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| file("layout/exit").source("res/layout/exit.xml"), | |
| xml("string/exit") | |
| .source(directString) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .combining(xml("id/exit").source("values/ids.xml").value(IdXmlResourceValue.of())) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/resources.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .root(primaryRoot) | |
| .source(primaryString.overwrite(directString)) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| file("layout/enter").root(transitiveRoot).source("res/layout/enter.xml"), | |
| file("layout/exit").root(directRoot).source("res/layout/exit.xml")) | |
| .combining( | |
| xml("id/exit") | |
| .root(directRoot) | |
| .source("values/ids.xml") | |
| .value(IdXmlResourceValue.of())) | |
| .build()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeWithOverwriteInTransitiveDeps() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| Path descendentRoot = fileSystem.getPath("descendent"); | |
| DataSource descendentLayout = DataSource.of(descendentRoot.resolve("res/layout/enter.xml")); | |
| DataSource transitiveLayout = DataSource.of(transitiveRoot.resolve("res/layout/enter.xml")); | |
| DataSource primaryString = DataSource.of(primaryRoot.resolve("res/values/resources.xml")); | |
| DataSource directStrings = DataSource.of(directRoot.resolve("res/values/strings.xml")); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable(file("layout/enter").source(descendentLayout)) | |
| .overwritable(file("layout/enter").source(transitiveLayout.overwrite(descendentLayout))) | |
| .combining(xml("id/exit").source("values/ids.xml").value(IdXmlResourceValue.of())) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| file("layout/exit").source("res/layout/exit.xml"), | |
| xml("string/exit") | |
| .source(directStrings) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .combining(xml("id/exit").source("values/ids.xml").value(IdXmlResourceValue.of())) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/resources.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData transitive = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source(primaryString.overwrite(directStrings)) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| file("layout/enter").source(transitiveLayout.overwrite(descendentLayout)), | |
| file("layout/exit").root(directRoot).source("res/layout/exit.xml")) | |
| .combining( | |
| xml("id/exit") | |
| .root(directRoot) | |
| .source("values/ids.xml") | |
| .value(IdXmlResourceValue.of())) | |
| .build()); | |
| assertAbout(unwrittenMergedAndroidData).that(transitive).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeDirectConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| // Two string/exit will create conflict. | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out")), | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| fqnFactory.parse("string/exit"), | |
| DataResourceXml.createWithNoNamespace( | |
| directRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out")), | |
| DataResourceXml.createWithNoNamespace( | |
| directRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeDirectConflictDuplicated() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| // Two string/exit will create conflict. | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out")), | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = | |
| AndroidDataMerger.createWithDefaultThreadPool( | |
| new SourceChecker() { | |
| @Override | |
| public boolean checkEquality(DataSource one, DataSource two) throws IOException { | |
| return one.equals(two); | |
| } | |
| }); | |
| assertAbout(unwrittenMergedAndroidData) | |
| .that(merger.merge(transitiveDependency, directDependency, primary, false, true)) | |
| .isEqualTo( | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.empty(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .root(directRoot) | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build())); | |
| } | |
| @Test | |
| public void mergeDirectConflictDuplicatedWithDifferentSources() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| // Two string/exit will create conflict. | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out")), | |
| xml("string/exit") | |
| .source("values/more_strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out")), | |
| xml("string/another_key") | |
| .source("values/more_strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "another way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| assertThat(loggingHandler.warnings).isEmpty(); | |
| } | |
| @Test | |
| public void mergeDirectConflictWithPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| DataSource primaryStrings = DataSource.of(primaryRoot.resolve("res/values/strings.xml")); | |
| DataSource directStrings = DataSource.of(directRoot.resolve("res/values/strings.xml")); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| // Two string/exit will create conflict. | |
| .overwritable( | |
| xml("string/exit") | |
| .source(directStrings) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out")), | |
| xml("string/exit") | |
| .source(directStrings) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/strings.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, true, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source(primaryStrings.overwrite(directStrings)) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeTransitiveConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| // Two string/exit will create conflict. | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out")), | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| ParsedAndroidData directDependency = ParsedAndroidDataBuilder.empty(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| fqnFactory.parse("string/exit"), | |
| DataResourceXml.createWithNoNamespace( | |
| transitiveRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out")), | |
| DataResourceXml.createWithNoNamespace( | |
| transitiveRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeTransitiveConflictWithPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| // Two string/exit will create conflict. | |
| .overwritable( | |
| xml("string/exit") | |
| .root(transitiveRoot.resolve("1")) | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out")), | |
| xml("string/exit") | |
| .root(transitiveRoot.resolve("2")) | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory).build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/strings.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, true, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .root(primaryRoot) | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeDirectAndTransitiveConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| fqnFactory.parse("string/exit"), | |
| DataResourceXml.createWithNoNamespace( | |
| directRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out")), | |
| DataResourceXml.createWithNoNamespace( | |
| transitiveRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeDirectTransitivePrimaryConflictWithoutPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/strings.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| FullyQualifiedName fullyQualifiedName = fqnFactory.parse("string/exit"); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| fullyQualifiedName, | |
| DataResourceXml.createWithNoNamespace( | |
| transitiveRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out")), | |
| DataResourceXml.createWithNoNamespace( | |
| directRoot.resolve("res/values/strings.xml"), | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeDirectTransitivePrimaryConflictWithThrowOnConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source("values/strings.xml") | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/strings.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| try { | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| throw new Exception("Expected a MergeConflictException!"); | |
| } catch (MergeConflictException e) { | |
| return; | |
| } | |
| } | |
| @Test | |
| public void mergeDirectTransitivePrimaryConflictWithPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| DataSource primaryStrings = DataSource.of(primaryRoot.resolve("res/values/strings.xml")); | |
| DataSource directStrings = DataSource.of(directRoot.resolve("res/values/strings.xml")); | |
| DataSource transitiveStrings = DataSource.of(transitiveRoot.resolve("res/values/strings.xml")); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source(transitiveStrings) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "no way out"))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source(directStrings) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "wrong way out"))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/strings.xml", ResourceType.VALUE, "<string name='exit'>way out</string>") | |
| .buildUnvalidated(); | |
| UnwrittenMergedAndroidData data = | |
| AndroidDataMerger.createWithDefaults() | |
| .merge(transitiveDependency, directDependency, primary, true, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(fqnFactory) | |
| .overwritable( | |
| xml("string/exit") | |
| .source(primaryStrings.overwrite(directStrings)) | |
| .value( | |
| SimpleXmlResourceValue.createWithValue( | |
| SimpleXmlResourceValue.Type.STRING, "way out"))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeDirectAndTransitiveNinepatchConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| // A drawable nine-patch png and plain png with the same base filename will conflict. | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable(file("drawable/rounded_corners").source("drawable/rounded_corners.9.png")) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable(file("drawable/rounded_corners").source("drawable/rounded_corners.png")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| fqnFactory.parse("drawable/rounded_corners"), | |
| DataValueFile.of(directRoot.resolve("res/drawable/rounded_corners.png")), | |
| DataValueFile.of(transitiveRoot.resolve("res/drawable/rounded_corners.9.png"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeAssetsDirectDeps() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addAsset("bin/boojum") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.builder() | |
| .assets(file().root(primaryRoot).source("bin/boojum")) | |
| .build(), | |
| ParsedAndroidDataBuilder.builder() | |
| .assets(file().root(directRoot).source("hunting/of/the/snark.txt")) | |
| .build()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeCombiningResources() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .combining( | |
| xml("styleable/RubADubDub") | |
| .source("values/attrs.xml") | |
| .value( | |
| StyleableXmlResourceValue.createAllAttrAsReferences( | |
| fqnFactory.parse("attr/baker")))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .combining( | |
| xml("styleable/RubADubDub") | |
| .source("values/attrs.xml") | |
| .value( | |
| StyleableXmlResourceValue.createAllAttrAsReferences( | |
| fqnFactory.parse("attr/candlestickmaker")))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/attrs.xml", | |
| ResourceType.VALUE, | |
| "<declare-styleable name='RubADubDub'>", | |
| " <attr name='butcher'/>", | |
| "</declare-styleable>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(primaryRoot, fqnFactory) | |
| .combining( | |
| xml("styleable/RubADubDub") | |
| .source("values/attrs.xml") | |
| .value( | |
| StyleableXmlResourceValue.createAllAttrAsReferences( | |
| fqnFactory.parse("attr/baker"), | |
| fqnFactory.parse("attr/butcher"), | |
| fqnFactory.parse("attr/candlestickmaker")))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeCombiningResourcesWithNamespaces() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| // TODO(corysmith): Make conflict uris for a single prefix and error | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .combining( | |
| xml("styleable/RubADubDub") | |
| .source("values/attrs.xml") | |
| .namespace(XLIFF_PREFIX, XLIFF_NAMESPACE) | |
| .namespace("tools", "http://schemas.android.com/tools") | |
| .value( | |
| StyleableXmlResourceValue.createAllAttrAsReferences( | |
| fqnFactory.parse("attr/baker")))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .combining( | |
| xml("styleable/RubADubDub") | |
| .source("values/attrs.xml") | |
| .namespace(XLIFF_PREFIX, "wrong uri") | |
| .value( | |
| StyleableXmlResourceValue.createAllAttrAsReferences( | |
| fqnFactory.parse("attr/candlestickmaker")))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/attrs.xml", | |
| ResourceType.VALUE, | |
| "<declare-styleable name='RubADubDub'>", | |
| " <attr name='butcher'/>", | |
| "</declare-styleable>") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(primaryRoot, fqnFactory) | |
| .combining( | |
| xml("styleable/RubADubDub") | |
| .source("values/attrs.xml") | |
| .namespace(XLIFF_PREFIX, "wrong uri") | |
| .namespace("tools", "http://schemas.android.com/tools") | |
| .value( | |
| StyleableXmlResourceValue.createAllAttrAsReferences( | |
| fqnFactory.parse("attr/baker"), | |
| fqnFactory.parse("attr/butcher"), | |
| fqnFactory.parse("attr/candlestickmaker")))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeCombiningLayoutIDResources() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| DataSource transitiveLayout = | |
| DataSource.of(transitiveRoot.resolve("res/layout/transitive.xml")); | |
| DataSource directLayout = DataSource.of(directRoot.resolve("res/layout/direct.xml")); | |
| DataSource primaryLayout = DataSource.of(primaryRoot.resolve("res/layout/primary.xml")); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .overwritable(file("layout/transitive").source(transitiveLayout)) | |
| .combining( | |
| xml("id/back_door").source(transitiveLayout).value(IdXmlResourceValue.of()), | |
| xml("id/door").source("values/ids.xml").value(IdXmlResourceValue.of()), | |
| xml("id/slide").source(transitiveLayout).value(IdXmlResourceValue.of())) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable(file("layout/zzDirect").source(directLayout)) | |
| .combining( | |
| xml("id/door").source(directLayout).value(IdXmlResourceValue.of()), | |
| xml("id/slide").source(directLayout).value(IdXmlResourceValue.of()), | |
| xml("id/window").source(directLayout).value(IdXmlResourceValue.of())) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "layout/primary.xml", | |
| ResourceType.LAYOUT, | |
| "<TextView android:id=\"@+id/door\"", | |
| " android:text=\"this way ---> \"", | |
| " android:layout_width=\"wrap_content\"", | |
| " android:layout_height=\"wrap_content\" />", | |
| "<TextView android:id=\"@+id/window\"", | |
| " android:text=\"no, not that way\"", | |
| " android:layout_width=\"wrap_content\"", | |
| " android:layout_height=\"wrap_content\" />") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(primaryRoot, fqnFactory) | |
| .overwritable(file("layout/primary").source(primaryLayout)) | |
| .combining( | |
| xml("id/window").source("layout/primary.xml").value(IdXmlResourceValue.of()), | |
| xml("id/door") | |
| .root(transitiveRoot) | |
| .source("values/ids.xml") | |
| .value(IdXmlResourceValue.of())) | |
| .build(), | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .overwritable( | |
| file("layout/transitive").source(transitiveLayout), | |
| file("layout/zzDirect").source(directLayout)) | |
| .combining( | |
| xml("id/back_door").source(transitiveLayout).value(IdXmlResourceValue.of()), | |
| xml("id/slide").source(directLayout).value(IdXmlResourceValue.of())) | |
| .build()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeCombiningPublicResources() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory) | |
| .combining( | |
| xml("public/RubADubDub") | |
| .source("values/public.xml") | |
| .value( | |
| PublicXmlResourceValue.create( | |
| com.android.resources.ResourceType.STRING, Optional.of(0x7f040000)))) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory) | |
| .combining( | |
| xml("public/RubADubDub") | |
| .source("values/public.xml") | |
| .value( | |
| PublicXmlResourceValue.create( | |
| com.android.resources.ResourceType.DIMEN, Optional.of(0x7f020000)))) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addResource( | |
| "values/public.xml", | |
| ResourceType.VALUE, | |
| "<public name='RubADubDub' type='color' id='0x7f030000' />") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(primaryRoot, fqnFactory) | |
| .combining( | |
| xml("public/RubADubDub") | |
| .source("values/public.xml") | |
| .value( | |
| PublicXmlResourceValue.of( | |
| ImmutableMap.of( | |
| com.android.resources.ResourceType.COLOR, | |
| Optional.of(0x7f030000), | |
| com.android.resources.ResourceType.DIMEN, | |
| Optional.of(0x7f020000), | |
| com.android.resources.ResourceType.STRING, | |
| Optional.of(0x7f040000))))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeAssetsDirectAndTransitiveDeps() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot) | |
| .assets(file().source("hunting/of/the/jubjub.bird")) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addAsset("bin/boojum") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, false, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.builder() | |
| .assets(file().root(primaryRoot).source("bin/boojum")) | |
| .build(), | |
| ParsedAndroidDataBuilder.builder() | |
| .assets( | |
| file().root(directRoot).source("hunting/of/the/snark.txt"), | |
| file().root(transitiveRoot).source("hunting/of/the/jubjub.bird")) | |
| .build()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeAssetsDirectConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRootOne = fileSystem.getPath("directOne"); | |
| Path directRootTwo = fileSystem.getPath("directTwo"); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.builder() | |
| .assets( | |
| file().root(directRootOne).source("hunting/of/the/snark.txt"), | |
| file().root(directRootTwo).source("hunting/of/the/snark.txt")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| RelativeAssetPath.Factory.of(directRootOne.resolve("assets")) | |
| .create(directRootOne.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(directRootOne.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(directRootTwo.resolve("assets/hunting/of/the/snark.txt"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeAssetsDirectConflictWithPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRootOne = fileSystem.getPath("directOne"); | |
| Path directRootTwo = fileSystem.getPath("directTwo"); | |
| ParsedAndroidData transitiveDependency = ParsedAndroidDataBuilder.empty(); | |
| String assetFile = "hunting/of/the/snark.txt"; | |
| DataSource primarySource = DataSource.of(primaryRoot.resolve("assets/" + assetFile)); | |
| DataSource directSourceOne = DataSource.of(directRootOne.resolve("assets/" + assetFile)); | |
| DataSource directSourceTwo = DataSource.of(directRootTwo.resolve("assets/" + assetFile)); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.builder() | |
| .assets( | |
| file().root(directRootOne).source(directSourceOne), | |
| file().root(directRootTwo).source(directSourceTwo)) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addAsset(assetFile) | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, true, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.builder() | |
| .assets( | |
| file() | |
| .root(primaryRoot) | |
| .source(primarySource.overwrite(directSourceOne, directSourceTwo))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeAssetsTransitiveConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path transitiveRootOne = fileSystem.getPath("transitiveOne"); | |
| Path transitiveRootTwo = fileSystem.getPath("transitiveTwo"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.builder() | |
| .assets( | |
| file().root(transitiveRootOne).source("hunting/of/the/snark.txt"), | |
| file().root(transitiveRootTwo).source("hunting/of/the/snark.txt")) | |
| .build(); | |
| ParsedAndroidData directDependency = ParsedAndroidDataBuilder.empty(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| RelativeAssetPath.Factory.of(transitiveRootOne.resolve("assets")) | |
| .create(transitiveRootOne.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(transitiveRootOne.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(transitiveRootTwo.resolve("assets/hunting/of/the/snark.txt"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeAssetsTransitiveConflictWithPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| ParsedAndroidData directDependency = ParsedAndroidDataBuilder.empty(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addAsset("hunting/of/the/snark.txt") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| UnwrittenMergedAndroidData data = | |
| merger.merge(transitiveDependency, directDependency, primary, true, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(primaryRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| @Test | |
| public void mergeAssetsDirectAndTransitiveConflict() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| RelativeAssetPath.Factory.of(directRoot.resolve("assets")) | |
| .create(directRoot.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(directRoot.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(transitiveRoot.resolve("assets/hunting/of/the/snark.txt"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeAssetsDirectTransitivePrimaryConflictWithoutPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addAsset("hunting/of/the/snark.txt") | |
| .buildUnvalidated(); | |
| AndroidDataMerger merger = AndroidDataMerger.createWithDefaults(); | |
| merger.merge(transitiveDependency, directDependency, primary, false, false); | |
| assertThat(loggingHandler.warnings) | |
| .containsExactly( | |
| MergeConflict.of( | |
| RelativeAssetPath.Factory.of(directRoot.resolve("assets")) | |
| .create(directRoot.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(directRoot.resolve("assets/hunting/of/the/snark.txt")), | |
| DataValueFile.of(transitiveRoot.resolve("assets/hunting/of/the/snark.txt"))) | |
| .toConflictMessage()); | |
| } | |
| @Test | |
| public void mergeAssetsDirectTransitivePrimaryConflictWithPrimaryOverride() throws Exception { | |
| Path primaryRoot = fileSystem.getPath("primary"); | |
| Path directRoot = fileSystem.getPath("direct"); | |
| Path transitiveRoot = fileSystem.getPath("transitive"); | |
| DataSource primarySource = | |
| DataSource.of(primaryRoot.resolve("assets/hunting/of/the/snark.txt")); | |
| DataSource directSource = DataSource.of(directRoot.resolve("assets/hunting/of/the/snark.txt")); | |
| ParsedAndroidData transitiveDependency = | |
| ParsedAndroidDataBuilder.buildOn(transitiveRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| ParsedAndroidData directDependency = | |
| ParsedAndroidDataBuilder.buildOn(directRoot) | |
| .assets(file().source("hunting/of/the/snark.txt")) | |
| .build(); | |
| UnvalidatedAndroidData primary = | |
| AndroidDataBuilder.of(primaryRoot) | |
| .createManifest("AndroidManifest.xml", "com.google.mergetest") | |
| .addAsset("hunting/of/the/snark.txt") | |
| .buildUnvalidated(); | |
| UnwrittenMergedAndroidData data = | |
| AndroidDataMerger.createWithDefaults() | |
| .merge(transitiveDependency, directDependency, primary, true, true); | |
| UnwrittenMergedAndroidData expected = | |
| UnwrittenMergedAndroidData.of( | |
| primary.getManifest(), | |
| ParsedAndroidDataBuilder.buildOn(primaryRoot) | |
| .assets(file().source(primarySource.overwrite(directSource))) | |
| .build(), | |
| ParsedAndroidDataBuilder.empty()); | |
| assertAbout(unwrittenMergedAndroidData).that(data).isEqualTo(expected); | |
| } | |
| final Subject.Factory<UnwrittenMergedAndroidDataSubject, UnwrittenMergedAndroidData> | |
| unwrittenMergedAndroidData = UnwrittenMergedAndroidDataSubject::new; | |
| private static final class TestLoggingHandler extends Handler { | |
| public final List<String> warnings = new ArrayList<String>(); | |
| @Override | |
| public void publish(LogRecord record) { | |
| if (record.getLevel().equals(Level.WARNING)) { | |
| warnings.add(record.getMessage()); | |
| } | |
| } | |
| @Override | |
| public void flush() {} | |
| @Override | |
| public void close() throws SecurityException {} | |
| } | |
| } |