| // 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.devtools.build.android.ParsedAndroidDataBuilder.file; |
| import static com.google.devtools.build.android.ParsedAndroidDataBuilder.xml; |
| |
| import com.google.common.base.MoreObjects; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.jimfs.Jimfs; |
| import com.google.common.truth.Truth; |
| import com.google.devtools.build.android.xml.IdXmlResourceValue; |
| import com.google.devtools.build.android.xml.ResourcesAttribute; |
| import java.nio.file.FileSystem; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for the AndroidDataSerializer and AndroidDataDeserializer. */ |
| @RunWith(JUnit4.class) |
| public class AndroidDataSerializerAndDeserializerTest { |
| |
| private FileSystem fs; |
| private FullyQualifiedName.Factory fqnFactory; |
| private Path source; |
| private Path manifest; |
| |
| @Before |
| public void createCleanEnvironment() throws Exception { |
| fs = Jimfs.newFileSystem(); |
| fqnFactory = FullyQualifiedName.Factory.from(ImmutableList.<String>of()); |
| source = Files.createDirectory(fs.getPath("source")); |
| manifest = Files.createFile(source.resolve("AndroidManifest.xml")); |
| } |
| |
| @Test |
| public void serializeAssets() throws Exception { |
| Path binaryPath = fs.getPath("out.bin"); |
| AndroidDataSerializer serializer = AndroidDataSerializer.create(); |
| UnwrittenMergedAndroidData expected = |
| UnwrittenMergedAndroidData.of( |
| manifest, |
| ParsedAndroidDataBuilder.buildOn(source) |
| .assets(file().source("hunting/of/the/boojum")) |
| .build(), |
| ParsedAndroidDataBuilder.empty()); |
| expected.serializeTo(serializer); |
| serializer.flushTo(binaryPath); |
| |
| AndroidDataDeserializer deserializer = AndroidParsedDataDeserializer.create(); |
| TestMapConsumer<DataAsset> assets = TestMapConsumer.ofAssets(); |
| deserializer.read(binaryPath, KeyValueConsumers.of(null, null, assets)); |
| Truth.assertThat(assets).isEqualTo(expected.getPrimary().getAssets()); |
| } |
| |
| @Test |
| public void serializeCombiningResource() throws Exception { |
| Path binaryPath = fs.getPath("out.bin"); |
| AndroidDataSerializer serializer = AndroidDataSerializer.create(); |
| UnwrittenMergedAndroidData expected = |
| UnwrittenMergedAndroidData.of( |
| manifest, |
| ParsedAndroidDataBuilder.buildOn(source, fqnFactory) |
| .combining( |
| xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) |
| .build(), |
| ParsedAndroidDataBuilder.empty()); |
| expected.serializeTo(serializer); |
| serializer.flushTo(binaryPath); |
| |
| AndroidDataDeserializer deserializer = AndroidParsedDataDeserializer.create(); |
| TestMapConsumer<DataResource> resources = TestMapConsumer.ofResources(); |
| deserializer.read( |
| binaryPath, |
| KeyValueConsumers.of( |
| null, // overwriting |
| resources, // combining |
| null // assets |
| )); |
| Truth.assertThat(resources).isEqualTo(expected.getPrimary().getCombiningResources()); |
| } |
| |
| @Test |
| public void serializeOverwritingResource() throws Exception { |
| Path binaryPath = fs.getPath("out.bin"); |
| AndroidDataSerializer serializer = AndroidDataSerializer.create(); |
| UnwrittenMergedAndroidData expected = |
| UnwrittenMergedAndroidData.of( |
| manifest, |
| ParsedAndroidDataBuilder.buildOn(source, fqnFactory) |
| .overwritable(file("layout/banker").source("layout/banker.xml")) |
| .build(), |
| ParsedAndroidDataBuilder.empty()); |
| expected.serializeTo(serializer); |
| serializer.flushTo(binaryPath); |
| |
| AndroidDataDeserializer deserializer = AndroidParsedDataDeserializer.create(); |
| TestMapConsumer<DataResource> resources = TestMapConsumer.ofResources(); |
| deserializer.read( |
| binaryPath, |
| KeyValueConsumers.of( |
| resources, // overwriting |
| null, // combining |
| null // assets |
| )); |
| Truth.assertThat(resources).isEqualTo(expected.getPrimary().getOverwritingResources()); |
| } |
| |
| @Test |
| public void serializeFileWithIds() throws Exception { |
| Path binaryPath = fs.getPath("out.bin"); |
| AndroidDataSerializer serializer = AndroidDataSerializer.create(); |
| ParsedAndroidData direct = |
| AndroidDataBuilder.of(source) |
| .addResource( |
| "layout/some_layout.xml", |
| AndroidDataBuilder.ResourceType.LAYOUT, |
| "<TextView android:id=\"@+id/MyTextView\"", |
| " android:text=\"@string/walrus\"", |
| " android:layout_width=\"wrap_content\"", |
| " android:layout_height=\"wrap_content\" />") |
| // Test what happens if a user accidentally uses the same ID in multiple layouts too. |
| .addResource( |
| "layout/another_layout.xml", |
| AndroidDataBuilder.ResourceType.LAYOUT, |
| "<TextView android:id=\"@+id/MyTextView\"", |
| " android:text=\"@string/walrus\"", |
| " android:layout_width=\"wrap_content\"", |
| " android:layout_height=\"wrap_content\" />") |
| // Also check what happens if a value XML file also contains the same ID. |
| .addResource( |
| "values/ids.xml", |
| AndroidDataBuilder.ResourceType.VALUE, |
| "<item name=\"MyTextView\" type=\"id\"/>", |
| "<item name=\"OtherId\" type=\"id\"/>") |
| .addResource( |
| "values/strings.xml", |
| AndroidDataBuilder.ResourceType.VALUE, |
| "<string name=\"walrus\">I has a bucket</string>") |
| .createManifest("AndroidManifest.xml", "com.carroll.lewis", "") |
| .buildParsed(); |
| UnwrittenMergedAndroidData expected = |
| UnwrittenMergedAndroidData.of( |
| manifest, |
| direct, |
| ParsedAndroidDataBuilder.empty()); |
| expected.serializeTo(serializer); |
| serializer.flushTo(binaryPath); |
| |
| AndroidDataDeserializer deserializer = AndroidParsedDataDeserializer.create(); |
| TestMapConsumer<DataResource> overwriting = TestMapConsumer.ofResources(); |
| TestMapConsumer<DataResource> combining = TestMapConsumer.ofResources(); |
| deserializer.read( |
| binaryPath, |
| KeyValueConsumers.of( |
| overwriting, |
| combining, |
| null // assets |
| )); |
| Truth.assertThat(overwriting).isEqualTo(expected.getPrimary().getOverwritingResources()); |
| Truth.assertThat(combining).isEqualTo(expected.getPrimary().getCombiningResources()); |
| } |
| |
| @Test |
| public void serialize() throws Exception { |
| Path binaryPath = fs.getPath("out.bin"); |
| AndroidDataSerializer serializer = AndroidDataSerializer.create(); |
| UnwrittenMergedAndroidData expected = |
| UnwrittenMergedAndroidData.of( |
| manifest, |
| ParsedAndroidDataBuilder.buildOn(source, fqnFactory) |
| .overwritable( |
| file("layout/banker").source("layout/banker.xml"), |
| xml("<resources>/foo").source("values/ids.xml") |
| .value(ResourcesAttribute.of( |
| fqnFactory.parse("<resources>/foo"), "foo", "fooVal"))) |
| .combining( |
| xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) |
| .assets(file().source("hunting/of/the/boojum")) |
| .build(), |
| ParsedAndroidDataBuilder.buildOn(source, fqnFactory) |
| .overwritable(file("layout/butcher").source("layout/butcher.xml")) |
| .combining( |
| xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) |
| .assets(file().source("hunting/of/the/snark")) |
| .build()); |
| expected.serializeTo(serializer); |
| serializer.flushTo(binaryPath); |
| |
| KeyValueConsumers primary = |
| KeyValueConsumers.of( |
| TestMapConsumer.ofResources(), // overwriting |
| TestMapConsumer.ofResources(), // combining |
| TestMapConsumer.ofAssets() // assets |
| ); |
| |
| AndroidDataDeserializer deserializer = AndroidParsedDataDeserializer.create(); |
| deserializer.read(binaryPath, primary); |
| Truth.assertThat(primary.overwritingConsumer) |
| .isEqualTo(expected.getPrimary().getOverwritingResources()); |
| Truth.assertThat(primary.combiningConsumer) |
| .isEqualTo(expected.getPrimary().getCombiningResources()); |
| Truth.assertThat(primary.assetConsumer).isEqualTo(expected.getPrimary().getAssets()); |
| } |
| |
| @Test |
| public void testDeserializeMissing() throws Exception { |
| Path binaryPath = fs.getPath("out.bin"); |
| AndroidDataSerializer serializer = AndroidDataSerializer.create(); |
| UnwrittenMergedAndroidData expected = |
| UnwrittenMergedAndroidData.of( |
| manifest, |
| ParsedAndroidDataBuilder.buildOn(source, fqnFactory) |
| .overwritable( |
| file("layout/banker").source("layout/banker.xml"), |
| xml("<resources>/foo").source("values/ids.xml") |
| .value(ResourcesAttribute.of( |
| fqnFactory.parse("<resources>/foo"), "foo", "fooVal"))) |
| .combining( |
| xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) |
| .assets(file().source("hunting/of/the/boojum")) |
| .build(), |
| ParsedAndroidDataBuilder.buildOn(source, fqnFactory) |
| .overwritable(file("layout/butcher").source("layout/butcher.xml")) |
| .combining( |
| xml("id/snark").source("values/ids.xml").value(IdXmlResourceValue.of())) |
| .assets(file().source("hunting/of/the/snark")) |
| .build()); |
| expected.serializeTo(serializer); |
| serializer.flushTo(binaryPath); |
| |
| AndroidDataDeserializer deserializer = |
| AndroidParsedDataDeserializer.withFilteredResources( |
| ImmutableList.of("the/boojum", "values/ids.xml", "layout/banker.xml")); |
| |
| KeyValueConsumers primary = |
| KeyValueConsumers.of( |
| TestMapConsumer.ofResources(), // overwriting |
| TestMapConsumer.ofResources(), // combining |
| null // assets |
| ); |
| |
| deserializer.read(binaryPath, primary); |
| Truth.assertThat(primary.overwritingConsumer).isEqualTo(Collections.emptyMap()); |
| Truth.assertThat(primary.combiningConsumer).isEqualTo(Collections.emptyMap()); |
| } |
| |
| private static class TestMapConsumer<T extends DataValue> |
| implements ParsedAndroidData.KeyValueConsumer<DataKey, T>, Map<DataKey, T> { |
| |
| Map<DataKey, T> target; |
| |
| static TestMapConsumer<DataAsset> ofAssets() { |
| return new TestMapConsumer<>(new HashMap<DataKey, DataAsset>()); |
| } |
| |
| static TestMapConsumer<DataResource> ofResources() { |
| return new TestMapConsumer<>(new HashMap<DataKey, DataResource>()); |
| } |
| |
| public TestMapConsumer(Map<DataKey, T> target) { |
| this.target = target; |
| } |
| |
| @Override |
| public void accept(DataKey key, T value) { |
| target.put(key, value); |
| } |
| |
| @Override |
| public int size() { |
| return target.size(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return target.isEmpty(); |
| } |
| |
| @Override |
| public boolean containsKey(Object key) { |
| return target.containsKey(key); |
| } |
| |
| @Override |
| public boolean containsValue(Object value) { |
| return target.containsValue(value); |
| } |
| |
| @Override |
| public T get(Object key) { |
| return target.get(key); |
| } |
| |
| @Override |
| public T put(DataKey key, T value) { |
| return target.put(key, value); |
| } |
| |
| @Override |
| public T remove(Object key) { |
| return target.remove(key); |
| } |
| |
| @Override |
| public void putAll(Map<? extends DataKey, ? extends T> m) { |
| target.putAll(m); |
| } |
| |
| @Override |
| public void clear() { |
| target.clear(); |
| } |
| |
| @Override |
| public Set<DataKey> keySet() { |
| return target.keySet(); |
| } |
| |
| @Override |
| public Collection<T> values() { |
| return target.values(); |
| } |
| |
| @Override |
| public Set<java.util.Map.Entry<DataKey, T>> entrySet() { |
| return target.entrySet(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return target.equals(o); |
| } |
| |
| @Override |
| public int hashCode() { |
| return target.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return MoreObjects.toStringHelper(this).add("target", target).toString(); |
| } |
| } |
| } |