blob: ded73805fbad23b0f960838ebf1f5078daafe0d3 [file] [log] [blame]
// Copyright 2023 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.lib.skyframe.serialization.testutils;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.skyframe.serialization.testutils.Dumper.dumpStructure;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class DumperTest {
private static final String NAMESPACE = DumperTest.class.getCanonicalName();
@Test
public void testNull() {
assertThat(dumpStructure(null)).isEqualTo("null");
}
@Test
public void testInlinedTypes() {
assertThat(dumpStructure(Byte.valueOf((byte) 0x10))).isEqualTo("16");
assertThat(dumpStructure(Short.valueOf((short) 12345))).isEqualTo("12345");
assertThat(dumpStructure(Integer.valueOf(65536))).isEqualTo("65536");
assertThat(dumpStructure(Long.valueOf(4294967296L))).isEqualTo("4294967296");
assertThat(dumpStructure(Float.valueOf(0.01f))).isEqualTo("0.01");
assertThat(dumpStructure(Double.valueOf(12.123456789))).isEqualTo("12.123456789");
assertThat(dumpStructure(Boolean.TRUE)).isEqualTo("true");
assertThat(dumpStructure(Character.valueOf('c'))).isEqualTo("c");
assertThat(dumpStructure("text")).isEqualTo("text");
assertThat(dumpStructure(Thread.class)).isEqualTo("class java.lang.Thread");
// Lambdas are also inlined because they qualify as sythetic.
Runnable lambda = () -> {};
assertThat(lambda.getClass().isSynthetic()).isTrue();
// The string representation of lambdas is not stable, and has additional variability across
// JDK versions. It should always start with the namespace.
assertThat(dumpStructure(lambda)).matches(NAMESPACE + ".*");
}
@Test
public void testByteArray() {
byte[] bytes = new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF};
// Byte array output is special cased.
assertThat(dumpStructure(bytes)).isEqualTo("byte[](0) [DEADBEEF]");
}
@Test
public void nestedByteArrays() {
byte[] bytes1 = new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF};
byte[] bytes2 = new byte[] {(byte) 0xFA, (byte) 0xCE, (byte) 0xCA, (byte) 0xFE};
byte[][] nestedBytes = new byte[][] {bytes1, bytes2, bytes1, null};
assertThat(dumpStructure(nestedBytes))
.isEqualTo(
"byte[][](0) [\n"
+ " byte[](1) [DEADBEEF]\n"
+ " byte[](2) [FACECAFE]\n"
+ " byte[](1)\n" // backreference
+ " null\n"
+ "]");
}
@Test
public void testInlineArray() {
String[] input = new String[] {"abc", "def", "hij"};
// Arrays declared with types that are inlined (such as String) are special cased.
assertThat(dumpStructure(input)).isEqualTo("java.lang.String[](0) [abc, def, hij]");
}
@Test
public void testMap() {
var input = new LinkedHashMap<Object, Object>();
input.put("abc", "def");
input.put(10, 12.5);
input.put(null, 'c');
input.put("k1", null);
input.put(new Position(5, 10), "Value");
assertThat(dumpStructure(input))
.isEqualTo(
"java.util.LinkedHashMap(0) [\n"
+ " key=abc\n"
+ " value=def\n"
+ " key=10\n"
+ " value=12.5\n"
+ " key=null\n"
+ " value=c\n"
+ " key=k1\n"
+ " value=null\n"
+ (" key=" + NAMESPACE + ".Position(1) [\n")
+ " x=5\n"
+ " y=10\n"
+ " ]\n"
+ " value=Value\n"
+ "]");
}
@Test
public void testIterable() {
var input = new ArrayList<Object>();
input.add("abc");
input.add(10);
input.add(new Position(12, 24));
input.add(input); // cyclic
input.add(null);
assertThat(dumpStructure(input))
.isEqualTo(
"java.util.ArrayList(0) [\n"
+ " abc\n"
+ " 10\n"
+ (" " + NAMESPACE + ".Position(1) [\n")
+ " x=12\n"
+ " y=24\n"
+ " ]\n"
+ " java.util.ArrayList(0)\n" // cyclic backreference
+ " null\n"
+ "]");
}
@Test
public void testPlainFields() {
assertThat(dumpStructure(new ExamplePojo()))
.isEqualTo(
NAMESPACE
+ ".ExamplePojo(0) [\n"
+ " booleanValue=false\n"
+ " byteValue=16\n"
+ " charValue=c\n"
+ " classValue=interface java.lang.Runnable\n"
+ " doubleValue=12.123456789\n"
+ " floatValue=0.01\n"
+ " intValue=65536\n"
+ " longValue=4294967296\n"
+ " nullClass=null\n"
+ " nullString=null\n"
+ " shortValue=12345\n"
+ " stringValue=text\n"
+ "]");
}
@SuppressWarnings({"UnusedVariable", "FieldCanBeStatic"})
private static class ExamplePojo {
private final boolean booleanValue = false;
private final byte byteValue = (byte) 0x10;
private final short shortValue = (short) 12345;
private final char charValue = 'c';
private final int intValue = 65536;
private final long longValue = 4294967296L;
private final float floatValue = 0.01f;
private final double doubleValue = 12.123456789;
private final String stringValue = "text";
private final String nullString = null;
private final Class<?> classValue = Runnable.class;
private final Class<?> nullClass = null;
}
@Test
public void testShadowedFields() {
assertThat(dumpStructure(new ShadowedFieldsGrandchild()))
.isEqualTo(
NAMESPACE
+ ".ShadowedFieldsGrandchild(0) [\n"
+ " shadowed=1\n"
+ " shadowed=2\n"
+ " shadowed=3\n"
+ "]");
}
@SuppressWarnings({"UnusedVariable", "FieldCanBeStatic"})
private static class ShadowedFields {
private final int shadowed = 1;
}
@SuppressWarnings({"UnusedVariable", "FieldCanBeStatic"})
private static class ShadowedFieldsChild extends ShadowedFields {
private final int shadowed = 2;
}
@SuppressWarnings({"UnusedVariable", "FieldCanBeStatic"})
private static class ShadowedFieldsGrandchild extends ShadowedFieldsChild {
private final int shadowed = 3;
}
@Test
public void testComposition() {
// This test verifies that cross-nesting of the special cased types works as expected.
var input = new ArrayList<Object>();
// An array that contains an array, map, iterable and simple object.
var arrayInput =
new Object[] {
new String[] {"abc", "def"},
ImmutableMap.<Object, Object>of(10, true, 12, 0),
ImmutableList.<Object>of(false, 0, -1),
new Position(15, 64)
};
input.add(arrayInput);
// A map that contains an array, map, iterable and simple object.
var mapInput = new LinkedHashMap<Object, Object>();
mapInput.put(new Position[] {}, ImmutableMap.<Object, Object>of(0, false, 1, true, 2, false));
mapInput.put(ImmutableList.<Object>of("xyz", 0.1), new Position(1, 3));
input.add(mapInput);
// An iterable that contains an array, map, iterable and simple object.
var iterableInput = new ArrayList<Object>();
iterableInput.add(new int[] {1, 2, 3, 4, 5});
iterableInput.add(ImmutableMap.<String, String>of("a", "A", "b", "B", "c", "C"));
iterableInput.add(ImmutableList.<Object>of("a", 1, 0.1));
iterableInput.add(new Position(-1, 5));
input.add(iterableInput);
// An object that contains an array, map, iterable and simple object.
input.add(new CompositeObject());
assertThat(dumpStructure(input))
.isEqualTo(
"java.util.ArrayList(0) [\n"
+ " java.lang.Object[](1) [\n"
+ " java.lang.String[](2) [abc, def]\n"
+ " com.google.common.collect.RegularImmutableMap(3) [\n"
+ " key=10\n"
+ " value=true\n"
+ " key=12\n"
+ " value=0\n"
+ " ]\n"
+ " com.google.common.collect.RegularImmutableList(4) [\n"
+ " false\n"
+ " 0\n"
+ " -1\n"
+ " ]\n"
+ (" " + NAMESPACE + ".Position(5) [\n")
+ " x=15\n"
+ " y=64\n"
+ " ]\n"
+ " ]\n"
+ " java.util.LinkedHashMap(6) [\n"
+ (" key=" + NAMESPACE + ".Position[](7) []\n")
+ " value=com.google.common.collect.RegularImmutableMap(8) [\n"
+ " key=0\n"
+ " value=false\n"
+ " key=1\n"
+ " value=true\n"
+ " key=2\n"
+ " value=false\n"
+ " ]\n"
+ " key=com.google.common.collect.RegularImmutableList(9) [\n"
+ " xyz\n"
+ " 0.1\n"
+ " ]\n"
+ (" value=" + NAMESPACE + ".Position(10) [\n")
+ " x=1\n"
+ " y=3\n"
+ " ]\n"
+ " ]\n"
+ " java.util.ArrayList(11) [\n"
+ " int[](12) [1, 2, 3, 4, 5]\n"
+ " com.google.common.collect.RegularImmutableMap(13) [\n"
+ " key=a\n"
+ " value=A\n"
+ " key=b\n"
+ " value=B\n"
+ " key=c\n"
+ " value=C\n"
+ " ]\n"
+ " com.google.common.collect.RegularImmutableList(14) [\n"
+ " a\n"
+ " 1\n"
+ " 0.1\n"
+ " ]\n"
+ (" " + NAMESPACE + ".Position(15) [\n")
+ " x=-1\n"
+ " y=5\n"
+ " ]\n"
+ " ]\n"
+ (" " + NAMESPACE + ".CompositeObject(16) [\n")
+ " doubles=double[](17) [0.1, 0.2, 0.3]\n"
+ " iterable=com.google.common.collect.RegularImmutableList(18) [\n"
+ " interface java.lang.Runnable\n"
+ " class java.lang.Thread\n"
+ " ]\n"
+ " map=com.google.common.collect.RegularImmutableMap(19) [\n"
+ " key=x\n"
+ " value=0\n"
+ " key=y\n"
+ " value=1\n"
+ " ]\n"
+ (" obj=" + NAMESPACE + ".Position(20) [\n")
+ " x=12\n"
+ " y=13\n"
+ " ]\n"
+ " ]\n"
+ "]");
}
/** A class that contains an array, map, iterable and simple object. */
@SuppressWarnings({"UnusedVariable", "FieldCanBeStatic"})
private static class CompositeObject {
private final double[] doubles = new double[] {0.1, 0.2, 0.3};
private final ImmutableMap<String, Integer> map = ImmutableMap.of("x", 0, "y", 1);
private final ImmutableList<Class<?>> iterable = ImmutableList.of(Runnable.class, Thread.class);
private final Position obj = new Position(12, 13);
}
/** An arbitrary class used as test data. */
@SuppressWarnings({"UnusedVariable", "FieldCanBeLocal"})
private static class Position {
private final int x;
private final int y;
private Position(int x, int y) {
this.x = x;
this.y = y;
}
}
}