blob: 94529e3db31948a4976f7182c54f858572d4b81b [file] [log] [blame]
// 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();
}
}
}