Merge commit '36bca61764984ff5395653cf8377ec5daa71b709' as 'libs/protobuf'

This commit is contained in:
Henry Winkel
2022-10-22 14:46:58 +02:00
2186 changed files with 838730 additions and 0 deletions

View File

@@ -0,0 +1,631 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.protobuf.util.Durations.toSecondsAsDouble;
import static org.junit.Assert.fail;
import com.google.common.collect.Lists;
import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Durations}. */
@RunWith(JUnit4.class)
public class DurationsTest {
private static final Duration INVALID_MAX =
Duration.newBuilder().setSeconds(Long.MAX_VALUE).setNanos(Integer.MAX_VALUE).build();
private static final Duration INVALID_MIN =
Duration.newBuilder().setSeconds(Long.MIN_VALUE).setNanos(Integer.MIN_VALUE).build();
@Test
public void testIsPositive() {
assertThat(Durations.isPositive(Durations.ZERO)).isFalse();
assertThat(Durations.isPositive(Durations.fromNanos(-1))).isFalse();
assertThat(Durations.isPositive(Durations.fromNanos(1))).isTrue();
}
@Test
public void testIsNegative() {
assertThat(Durations.isNegative(Durations.ZERO)).isFalse();
assertThat(Durations.isNegative(Durations.fromNanos(1))).isFalse();
assertThat(Durations.isNegative(Durations.fromNanos(-1))).isTrue();
}
@Test
public void testCheckNotNegative() {
Durations.checkNotNegative(Durations.ZERO);
Durations.checkNotNegative(Durations.fromNanos(1));
Durations.checkNotNegative(Durations.fromSeconds(1));
try {
Durations.checkNotNegative(Durations.fromNanos(-1));
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isEqualTo(
"duration (-0.000000001s) must not be negative");
}
try {
Durations.checkNotNegative(Durations.fromSeconds(-1));
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isEqualTo("duration (-1s) must not be negative");
}
}
@Test
public void testCheckPositive() {
Durations.checkPositive(Durations.fromNanos(1));
Durations.checkPositive(Durations.fromSeconds(1));
try {
Durations.checkPositive(Durations.ZERO);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isEqualTo("duration (0s) must be positive");
}
try {
Durations.checkPositive(Durations.fromNanos(-1));
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isEqualTo("duration (-0.000000001s) must be positive");
}
try {
Durations.checkPositive(Durations.fromSeconds(-1));
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isEqualTo("duration (-1s) must be positive");
}
}
@Test
public void testToSecondsAsDouble() {
assertThat(toSecondsAsDouble(duration(0, 1))).isEqualTo(1.0e-9);
assertThat(toSecondsAsDouble(Durations.fromMillis(999))).isEqualTo(0.999);
assertThat(toSecondsAsDouble(duration(1, 0))).isEqualTo(1.0);
assertWithMessage("Precision loss detected")
.that(toSecondsAsDouble(Durations.fromNanos(1234567890987654321L)))
.isWithin(1.0e-6)
.of(1234567890.9876544);
}
@Test
public void testRoundtripConversions() {
for (long value : Arrays.asList(-123456, -42, -1, 0, 1, 42, 123456)) {
assertThat(Durations.toDays(Durations.fromDays(value))).isEqualTo(value);
assertThat(Durations.toHours(Durations.fromHours(value))).isEqualTo(value);
assertThat(Durations.toMinutes(Durations.fromMinutes(value))).isEqualTo(value);
assertThat(Durations.toSeconds(Durations.fromSeconds(value))).isEqualTo(value);
assertThat(Durations.toMillis(Durations.fromMillis(value))).isEqualTo(value);
assertThat(Durations.toMicros(Durations.fromMicros(value))).isEqualTo(value);
assertThat(Durations.toNanos(Durations.fromNanos(value))).isEqualTo(value);
}
}
@Test
public void testStaticFactories() {
Duration[] durations = {
Durations.fromDays(1),
Durations.fromHours(24),
Durations.fromMinutes(1440),
Durations.fromSeconds(86_400L),
Durations.fromMillis(86_400_000L),
Durations.fromMicros(86_400_000_000L),
Durations.fromNanos(86_400_000_000_000L)
};
for (Duration d1 : durations) {
assertThat(d1).isNotEqualTo(null);
for (Duration d2 : durations) {
assertThat(d1).isEqualTo(d2);
}
}
}
@Test
public void testMinMaxAreValid() {
assertThat(Durations.isValid(Durations.MAX_VALUE)).isTrue();
assertThat(Durations.isValid(Durations.MIN_VALUE)).isTrue();
}
@Test
public void testIsValid_false() {
assertThat(Durations.isValid(-315576000001L, 0)).isFalse();
assertThat(Durations.isValid(315576000001L, 0)).isFalse();
assertThat(Durations.isValid(42L, -42)).isFalse();
assertThat(Durations.isValid(-42L, 42)).isFalse();
}
@Test
public void testIsValid_true() {
assertThat(Durations.isValid(0L, 42)).isTrue();
assertThat(Durations.isValid(0L, -42)).isTrue();
assertThat(Durations.isValid(42L, 0)).isTrue();
assertThat(Durations.isValid(42L, 42)).isTrue();
assertThat(Durations.isValid(-315576000000L, 0)).isTrue();
assertThat(Durations.isValid(315576000000L, 0)).isTrue();
}
@Test
public void testParse_outOfRange() throws ParseException {
try {
Duration x = Durations.parse("316576000000.123456789123456789s");
fail("expected ParseException");
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isEqualTo("Duration value is out of range.");
assertThat(expected).hasCauseThat().isNotNull();
}
}
@Test
public void testDurationStringFormat() throws Exception {
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
Duration duration = Timestamps.between(start, end);
assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s");
duration = Timestamps.between(end, start);
assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s");
// Generated output should contain 3, 6, or 9 fractional digits.
duration = Duration.newBuilder().setSeconds(1).build();
assertThat(Durations.toString(duration)).isEqualTo("1s");
duration = Duration.newBuilder().setNanos(10000000).build();
assertThat(Durations.toString(duration)).isEqualTo("0.010s");
duration = Duration.newBuilder().setNanos(10000).build();
assertThat(Durations.toString(duration)).isEqualTo("0.000010s");
duration = Duration.newBuilder().setNanos(10).build();
assertThat(Durations.toString(duration)).isEqualTo("0.000000010s");
// Parsing accepts an fractional digits as long as they fit into nano
// precision.
duration = Durations.parse("0.1s");
assertThat(duration.getNanos()).isEqualTo(100000000);
duration = Durations.parse("0.0001s");
assertThat(duration.getNanos()).isEqualTo(100000);
duration = Durations.parse("0.0000001s");
assertThat(duration.getNanos()).isEqualTo(100);
// Repeat using parseUnchecked().
duration = Durations.parseUnchecked("0.1s");
assertThat(duration.getNanos()).isEqualTo(100000000);
duration = Durations.parseUnchecked("0.0001s");
assertThat(duration.getNanos()).isEqualTo(100000);
duration = Durations.parseUnchecked("0.0000001s");
assertThat(duration.getNanos()).isEqualTo(100);
// Duration must support range from -315,576,000,000s to +315576000000s
// which includes negative values.
duration = Durations.parse("315576000000.999999999s");
assertThat(duration.getSeconds()).isEqualTo(315576000000L);
assertThat(duration.getNanos()).isEqualTo(999999999);
duration = Durations.parse("-315576000000.999999999s");
assertThat(duration.getSeconds()).isEqualTo(-315576000000L);
assertThat(duration.getNanos()).isEqualTo(-999999999);
// Repeat using parseUnchecked().
duration = Durations.parseUnchecked("315576000000.999999999s");
assertThat(duration.getSeconds()).isEqualTo(315576000000L);
assertThat(duration.getNanos()).isEqualTo(999999999);
duration = Durations.parseUnchecked("-315576000000.999999999s");
assertThat(duration.getSeconds()).isEqualTo(-315576000000L);
assertThat(duration.getNanos()).isEqualTo(-999999999);
}
@Test
public void testDurationInvalidFormat() {
// Value too small.
try {
Durations.toString(
Duration.newBuilder().setSeconds(Durations.DURATION_SECONDS_MIN - 1).build());
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.toString(
Duration.newBuilder().setSeconds(Durations.DURATION_SECONDS_MAX + 1).build());
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Invalid nanos value.
try {
Durations.toString(Duration.newBuilder().setSeconds(1).setNanos(-1).build());
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Invalid seconds value.
try {
Durations.toString(Duration.newBuilder().setSeconds(-1).setNanos(1).build());
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Value too small.
try {
Durations.parse("-315576000001s");
fail();
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.parseUnchecked("-315576000001s");
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Value too large.
try {
Durations.parse("315576000001s");
fail();
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.parseUnchecked("315576000001s");
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Empty.
try {
Durations.parse("");
fail();
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.parseUnchecked("");
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Missing "s".
try {
Durations.parse("0");
fail();
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.parseUnchecked("0");
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Invalid trailing data.
try {
Durations.parse("0s0");
fail();
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.parseUnchecked("0s0");
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
// Invalid prefix.
try {
Durations.parse("--1s");
fail();
} catch (ParseException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
try {
Durations.parseUnchecked("--1s");
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().isNotNull();
}
}
@Test
public void testDurationConversion() throws Exception {
Duration duration = Durations.parse("1.111111111s");
assertThat(Durations.toNanos(duration)).isEqualTo(1111111111);
assertThat(Durations.toMicros(duration)).isEqualTo(1111111);
assertThat(Durations.toMillis(duration)).isEqualTo(1111);
assertThat(Durations.toSeconds(duration)).isEqualTo(1);
duration = Durations.fromNanos(1111111111);
assertThat(Durations.toString(duration)).isEqualTo("1.111111111s");
duration = Durations.fromMicros(1111111);
assertThat(Durations.toString(duration)).isEqualTo("1.111111s");
duration = Durations.fromMillis(1111);
assertThat(Durations.toString(duration)).isEqualTo("1.111s");
duration = Durations.fromSeconds(1);
assertThat(Durations.toString(duration)).isEqualTo("1s");
duration = Durations.parse("-1.111111111s");
assertThat(Durations.toNanos(duration)).isEqualTo(-1111111111);
assertThat(Durations.toMicros(duration)).isEqualTo(-1111111);
assertThat(Durations.toMillis(duration)).isEqualTo(-1111);
assertThat(Durations.toSeconds(duration)).isEqualTo(-1);
duration = Durations.fromNanos(-1111111111);
assertThat(Durations.toString(duration)).isEqualTo("-1.111111111s");
duration = Durations.fromMicros(-1111111);
assertThat(Durations.toString(duration)).isEqualTo("-1.111111s");
duration = Durations.fromMillis(-1111);
assertThat(Durations.toString(duration)).isEqualTo("-1.111s");
duration = Durations.fromSeconds(-1);
assertThat(Durations.toString(duration)).isEqualTo("-1s");
}
@Test
public void testTimeOperations() throws Exception {
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
Duration duration = Timestamps.between(start, end);
assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s");
Timestamp value = Timestamps.add(start, duration);
assertThat(value).isEqualTo(end);
value = Timestamps.subtract(end, duration);
assertThat(value).isEqualTo(start);
duration = Timestamps.between(end, start);
assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s");
value = Timestamps.add(end, duration);
assertThat(value).isEqualTo(start);
value = Timestamps.subtract(start, duration);
assertThat(value).isEqualTo(end);
duration = Durations.parse("-1.125s");
assertThat(Durations.toString(duration)).isEqualTo("-1.125s");
duration = Durations.add(duration, duration);
assertThat(Durations.toString(duration)).isEqualTo("-2.250s");
duration = Durations.subtract(duration, Durations.parse("-1s"));
assertThat(Durations.toString(duration)).isEqualTo("-1.250s");
}
@Test
public void testToString() {
assertThat(Durations.toString(duration(1, 1))).isEqualTo("1.000000001s");
assertThat(Durations.toString(duration(-1, -1))).isEqualTo("-1.000000001s");
assertThat(Durations.toString(duration(1, 0))).isEqualTo("1s");
assertThat(Durations.toString(duration(-1, 0))).isEqualTo("-1s");
assertThat(Durations.toString(duration(0, 1))).isEqualTo("0.000000001s");
assertThat(Durations.toString(duration(0, -1))).isEqualTo("-0.000000001s");
}
@Test
public void testAdd() {
assertThat(Durations.add(duration(1, 10), duration(1, 20))).isEqualTo(duration(2, 30));
assertThat(Durations.add(duration(1, 999999999), duration(1, 2))).isEqualTo(duration(3, 1));
assertThat(Durations.add(duration(1, 999999999), duration(1, 1))).isEqualTo(duration(3, 0));
assertThat(Durations.add(duration(1, 999999999), duration(-2, -1))).isEqualTo(duration(0, -2));
assertThat(Durations.add(duration(1, 999999999), duration(-2, 0))).isEqualTo(duration(0, -1));
assertThat(Durations.add(duration(-1, -10), duration(-1, -20))).isEqualTo(duration(-2, -30));
assertThat(Durations.add(duration(-1, -999999999), duration(-1, -2)))
.isEqualTo(duration(-3, -1));
assertThat(Durations.add(duration(-1, -999999999), duration(-1, -1)))
.isEqualTo(duration(-3, 0));
assertThat(Durations.add(duration(-1, -999999999), duration(2, 1))).isEqualTo(duration(0, 2));
assertThat(Durations.add(duration(-1, -999999999), duration(2, 0))).isEqualTo(duration(0, 1));
}
@Test
public void testSubtract() {
assertThat(Durations.subtract(duration(3, 2), duration(1, 1))).isEqualTo(duration(2, 1));
assertThat(Durations.subtract(duration(3, 10), duration(1, 10))).isEqualTo(duration(2, 0));
assertThat(Durations.subtract(duration(3, 1), duration(1, 2)))
.isEqualTo(duration(1, 999999999));
assertThat(Durations.subtract(duration(3, 2), duration(4, 1)))
.isEqualTo(duration(0, -999999999));
assertThat(Durations.subtract(duration(1, 1), duration(3, 2))).isEqualTo(duration(-2, -1));
assertThat(Durations.subtract(duration(1, 10), duration(3, 10))).isEqualTo(duration(-2, 0));
assertThat(Durations.subtract(duration(1, 2), duration(3, 1)))
.isEqualTo(duration(-1, -999999999));
assertThat(Durations.subtract(duration(4, 1), duration(3, 2)))
.isEqualTo(duration(0, 999999999));
}
@Test
public void testComparator() {
assertThat(Durations.comparator().compare(duration(3, 2), duration(3, 2))).isEqualTo(0);
assertThat(Durations.comparator().compare(duration(0, 0), duration(0, 0))).isEqualTo(0);
assertThat(Durations.comparator().compare(duration(3, 1), duration(1, 1))).isGreaterThan(0);
assertThat(Durations.comparator().compare(duration(3, 2), duration(3, 1))).isGreaterThan(0);
assertThat(Durations.comparator().compare(duration(1, 1), duration(3, 1))).isLessThan(0);
assertThat(Durations.comparator().compare(duration(3, 1), duration(3, 2))).isLessThan(0);
assertThat(Durations.comparator().compare(duration(-3, -1), duration(-1, -1))).isLessThan(0);
assertThat(Durations.comparator().compare(duration(-3, -2), duration(-3, -1))).isLessThan(0);
assertThat(Durations.comparator().compare(duration(-1, -1), duration(-3, -1))).isGreaterThan(0);
assertThat(Durations.comparator().compare(duration(-3, -1), duration(-3, -2))).isGreaterThan(0);
assertThat(Durations.comparator().compare(duration(-10, -1), duration(1, 1))).isLessThan(0);
assertThat(Durations.comparator().compare(duration(0, -1), duration(0, 1))).isLessThan(0);
assertThat(Durations.comparator().compare(duration(0x80000000L, 0), duration(0, 0)))
.isGreaterThan(0);
assertThat(Durations.comparator().compare(duration(0xFFFFFFFF00000000L, 0), duration(0, 0)))
.isLessThan(0);
Duration duration0 = duration(-50, -500);
Duration duration1 = duration(-50, -400);
Duration duration2 = duration(50, 500);
Duration duration3 = duration(100, 20);
Duration duration4 = duration(100, 50);
Duration duration5 = duration(100, 150);
Duration duration6 = duration(150, 40);
List<Duration> durations =
Lists.newArrayList(
duration5, duration3, duration1, duration4, duration6, duration2, duration0, duration3);
Collections.sort(durations, Durations.comparator());
assertThat(durations)
.containsExactly(
duration0, duration1, duration2, duration3, duration3, duration4, duration5, duration6)
.inOrder();
}
@Test
public void testCompare() {
assertThat(Durations.compare(duration(3, 2), duration(3, 2))).isEqualTo(0);
assertThat(Durations.compare(duration(0, 0), duration(0, 0))).isEqualTo(0);
assertThat(Durations.compare(duration(3, 1), duration(1, 1))).isGreaterThan(0);
assertThat(Durations.compare(duration(3, 2), duration(3, 1))).isGreaterThan(0);
assertThat(Durations.compare(duration(1, 1), duration(3, 1))).isLessThan(0);
assertThat(Durations.compare(duration(3, 1), duration(3, 2))).isLessThan(0);
assertThat(Durations.compare(duration(-3, -1), duration(-1, -1))).isLessThan(0);
assertThat(Durations.compare(duration(-3, -2), duration(-3, -1))).isLessThan(0);
assertThat(Durations.compare(duration(-1, -1), duration(-3, -1))).isGreaterThan(0);
assertThat(Durations.compare(duration(-3, -1), duration(-3, -2))).isGreaterThan(0);
assertThat(Durations.compare(duration(-10, -1), duration(1, 1))).isLessThan(0);
assertThat(Durations.compare(duration(0, -1), duration(0, 1))).isLessThan(0);
assertThat(Durations.compare(duration(0x80000000L, 0), duration(0, 0))).isGreaterThan(0);
assertThat(Durations.compare(duration(0xFFFFFFFF00000000L, 0), duration(0, 0))).isLessThan(0);
}
@Test
public void testOverflows() throws Exception {
try {
Durations.toNanos(duration(315576000000L, 999999999));
assertWithMessage("Expected an ArithmeticException to be thrown").fail();
} catch (ArithmeticException expected) {
}
try {
Durations.add(Durations.MAX_VALUE, Durations.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.subtract(Durations.MIN_VALUE, Durations.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toNanos(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toMicros(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toMillis(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toSeconds(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toNanos(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toMicros(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toMillis(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.toSeconds(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
assertThat(Durations.toString(Durations.fromNanos(Long.MAX_VALUE)))
.isEqualTo("9223372036.854775807s");
try {
Durations.fromMicros(Long.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.fromMillis(Long.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.fromSeconds(Long.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
assertThat(Durations.toString(Durations.fromNanos(Long.MIN_VALUE)))
.isEqualTo("-9223372036.854775808s");
try {
Durations.fromMicros(Long.MIN_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.fromMillis(Long.MIN_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
try {
Durations.fromSeconds(Long.MIN_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
static Duration duration(long seconds, int nanos) {
return Durations.checkValid(
Durations.checkValid(Duration.newBuilder().setSeconds(seconds).setNanos(nanos)));
}
}

View File

@@ -0,0 +1,398 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.common.truth.Truth.assertThat;
import com.google.protobuf.DynamicMessage;
import com.google.protobuf.Message;
import com.google.protobuf.UninitializedMessageException;
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestRequiredMessage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class FieldMaskTreeTest {
@Test
public void testAddFieldPath() throws Exception {
FieldMaskTree tree = new FieldMaskTree();
assertThat(tree.toString()).isEmpty();
tree.addFieldPath("");
assertThat(tree.toString()).isEmpty();
// New branch.
tree.addFieldPath("foo");
assertThat(tree.toString()).isEqualTo("foo");
// Redundant path.
tree.addFieldPath("foo");
assertThat(tree.toString()).isEqualTo("foo");
// New branch.
tree.addFieldPath("bar.baz");
assertThat(tree.toString()).isEqualTo("bar.baz,foo");
// Redundant sub-path.
tree.addFieldPath("foo.bar");
assertThat(tree.toString()).isEqualTo("bar.baz,foo");
// New branch from a non-root node.
tree.addFieldPath("bar.quz");
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz,foo");
// A path that matches several existing sub-paths.
tree.addFieldPath("bar");
assertThat(tree.toString()).isEqualTo("bar,foo");
}
@Test
public void testMergeFromFieldMask() throws Exception {
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz,foo");
tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
assertThat(tree.toString()).isEqualTo("bar,foo");
}
@Test
public void testRemoveFieldPath() throws Exception {
String initialTreeString = "bar.baz,bar.quz.bar,foo";
FieldMaskTree tree;
// Empty path.
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
tree.removeFieldPath("");
assertThat(tree.toString()).isEqualTo(initialTreeString);
// Non-exist sub-path of an existing leaf.
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
tree.removeFieldPath("foo.bar");
assertThat(tree.toString()).isEqualTo(initialTreeString);
// Non-exist path.
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
tree.removeFieldPath("bar.foo");
assertThat(tree.toString()).isEqualTo(initialTreeString);
// Match an existing leaf node -> remove leaf node.
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
tree.removeFieldPath("foo");
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz.bar");
// Match sub-path of an existing leaf node -> recursive removal.
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
tree.removeFieldPath("bar.quz.bar");
assertThat(tree.toString()).isEqualTo("bar.baz,foo");
// Match a non-leaf node -> remove all children.
tree = new FieldMaskTree(FieldMaskUtil.fromString(initialTreeString));
tree.removeFieldPath("bar");
assertThat(tree.toString()).isEqualTo("foo");
}
@Test
public void testRemoveFromFieldMask() throws Exception {
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
assertThat(tree.toString()).isEqualTo("bar.baz,bar.quz,foo");
tree.removeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
assertThat(tree.toString()).isEqualTo("foo");
}
@Test
public void testIntersectFieldPath() throws Exception {
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
FieldMaskTree result = new FieldMaskTree();
// Empty path.
tree.intersectFieldPath("", result);
assertThat(result.toString()).isEmpty();
// Non-exist path.
tree.intersectFieldPath("quz", result);
assertThat(result.toString()).isEmpty();
// Sub-path of an existing leaf.
tree.intersectFieldPath("foo.bar", result);
assertThat(result.toString()).isEqualTo("foo.bar");
// Match an existing leaf node.
tree.intersectFieldPath("foo", result);
assertThat(result.toString()).isEqualTo("foo");
// Non-exist path.
tree.intersectFieldPath("bar.foo", result);
assertThat(result.toString()).isEqualTo("foo");
// Match a non-leaf node.
tree.intersectFieldPath("bar", result);
assertThat(result.toString()).isEqualTo("bar.baz,bar.quz,foo");
}
@Test
public void testMerge() throws Exception {
testMergeImpl(true);
testMergeImpl(false);
testMergeRequire(false);
testMergeRequire(true);
}
private void merge(
FieldMaskTree tree,
Message source,
Message.Builder builder,
FieldMaskUtil.MergeOptions options,
boolean useDynamicMessage)
throws Exception {
if (useDynamicMessage) {
Message.Builder newBuilder =
DynamicMessage.newBuilder(source.getDescriptorForType())
.mergeFrom(builder.buildPartial().toByteArray());
tree.merge(
DynamicMessage.newBuilder(source.getDescriptorForType())
.mergeFrom(source.toByteArray())
.build(),
newBuilder,
options);
builder.clear();
builder.mergeFrom(newBuilder.buildPartial());
} else {
tree.merge(source, builder, options);
}
}
private void testMergeRequire(boolean useDynamicMessage) throws Exception {
TestRequired value = TestRequired.newBuilder().setA(4321).setB(8765).setC(233333).build();
TestRequiredMessage source = TestRequiredMessage.newBuilder().setRequiredMessage(value).build();
FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
TestRequiredMessage.Builder builder = TestRequiredMessage.newBuilder();
merge(
new FieldMaskTree().addFieldPath("required_message.a"),
source,
builder,
options,
useDynamicMessage);
assertThat(builder.hasRequiredMessage()).isTrue();
assertThat(builder.getRequiredMessage().hasA()).isTrue();
assertThat(builder.getRequiredMessage().hasB()).isFalse();
assertThat(builder.getRequiredMessage().hasC()).isFalse();
merge(
new FieldMaskTree().addFieldPath("required_message.b").addFieldPath("required_message.c"),
source,
builder,
options,
useDynamicMessage);
try {
assertThat(source).isEqualTo(builder.build());
} catch (UninitializedMessageException e) {
throw new AssertionError("required field isn't set", e);
}
}
private void testMergeImpl(boolean useDynamicMessage) throws Exception {
TestAllTypes value =
TestAllTypes.newBuilder()
.setOptionalInt32(1234)
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
.addRepeatedInt32(4321)
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
.build();
NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(value)
.setChild(NestedTestAllTypes.newBuilder().setPayload(value))
.build();
// Now we have a message source with the following structure:
// [root] -+- payload -+- optional_int32
// | +- optional_nested_message
// | +- repeated_int32
// | +- repeated_nested_message
// |
// +- child --- payload -+- optional_int32
// +- optional_nested_message
// +- repeated_int32
// +- repeated_nested_message
FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
// Test merging with an empty FieldMask.
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().addRepeatedInt32(1000);
merge(new FieldMaskTree(), source, builder, options, useDynamicMessage);
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedInt32(1000);
assertThat(builder.build()).isEqualTo(expected.build());
// Test merging each individual field.
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalInt32(1234);
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("payload.optional_nested_message"),
source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedInt32(4321);
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("payload.repeated_nested_message"),
source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(
new FieldMaskTree().addFieldPath("child.payload.optional_int32"),
source,
builder,
options,
useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(
new FieldMaskTree().addFieldPath("child.payload.optional_nested_message"),
source,
builder,
options,
useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected
.getChildBuilder()
.getPayloadBuilder()
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("child.payload.repeated_int32"),
source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
assertThat(builder.build()).isEqualTo(expected.build());
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message"),
source, builder, options, useDynamicMessage);
expected = NestedTestAllTypes.newBuilder();
expected
.getChildBuilder()
.getPayloadBuilder()
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
assertThat(builder.build()).isEqualTo(expected.build());
// Test merging all fields.
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("child").addFieldPath("payload"),
source, builder, options, useDynamicMessage);
assertThat(builder.build()).isEqualTo(source);
// Test repeated options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().addRepeatedInt32(1000);
merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
source, builder, options, useDynamicMessage);
// Default behavior is to append repeated fields.
assertThat(builder.getPayload().getRepeatedInt32Count()).isEqualTo(2);
assertThat(builder.getPayload().getRepeatedInt32(0)).isEqualTo(1000);
assertThat(builder.getPayload().getRepeatedInt32(1)).isEqualTo(4321);
// Change to replace repeated fields.
options.setReplaceRepeatedFields(true);
merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
source, builder, options, useDynamicMessage);
assertThat(builder.getPayload().getRepeatedInt32Count()).isEqualTo(1);
assertThat(builder.getPayload().getRepeatedInt32(0)).isEqualTo(4321);
// Test message options.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
merge(new FieldMaskTree().addFieldPath("payload"),
source, builder, options, useDynamicMessage);
// Default behavior is to merge message fields.
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(1234);
assertThat(builder.getPayload().getOptionalUint32()).isEqualTo(2000);
// Test merging unset message fields.
NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("payload"),
clearedSource, builder, options, useDynamicMessage);
assertThat(builder.hasPayload()).isFalse();
// Skip a message field if they are unset in both source and target.
builder = NestedTestAllTypes.newBuilder();
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
clearedSource, builder, options, useDynamicMessage);
assertThat(builder.hasPayload()).isFalse();
// Change to replace message fields.
options.setReplaceMessageFields(true);
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
merge(new FieldMaskTree().addFieldPath("payload"),
source, builder, options, useDynamicMessage);
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(1234);
assertThat(builder.getPayload().getOptionalUint32()).isEqualTo(0);
// Test merging unset message fields.
builder = NestedTestAllTypes.newBuilder();
builder.getPayloadBuilder().setOptionalInt32(1000);
builder.getPayloadBuilder().setOptionalUint32(2000);
merge(new FieldMaskTree().addFieldPath("payload"),
clearedSource, builder, options, useDynamicMessage);
assertThat(builder.hasPayload()).isFalse();
// Test merging unset primitive fields.
builder = source.toBuilder();
builder.getPayloadBuilder().clearOptionalInt32();
NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build();
builder = source.toBuilder();
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
assertThat(builder.getPayload().hasOptionalInt32()).isTrue();
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(0);
// Change to clear unset primitive fields.
options.setReplacePrimitiveFields(true);
builder = source.toBuilder();
merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
assertThat(builder.hasPayload()).isTrue();
assertThat(builder.getPayload().hasOptionalInt32()).isFalse();
}
}

View File

@@ -0,0 +1,315 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.FieldMask;
import protobuf_unittest.UnittestProto.NestedTestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link FieldMaskUtil}. */
@RunWith(JUnit4.class)
public class FieldMaskUtilTest {
@Test
public void testIsValid() throws Exception {
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload")).isTrue();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist")).isFalse();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32")).isTrue();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_int32")).isTrue();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message"))
.isTrue();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message"))
.isTrue();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.nonexist")).isFalse();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("payload")))
.isTrue();
assertThat(
FieldMaskUtil.isValid(NestedTestAllTypes.class, FieldMaskUtil.fromString("nonexist")))
.isFalse();
assertThat(
FieldMaskUtil.isValid(
NestedTestAllTypes.class, FieldMaskUtil.fromString("payload,nonexist")))
.isFalse();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "payload")).isTrue();
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.getDescriptor(), "nonexist")).isFalse();
assertThat(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("payload")))
.isTrue();
assertThat(
FieldMaskUtil.isValid(
NestedTestAllTypes.getDescriptor(), FieldMaskUtil.fromString("nonexist")))
.isFalse();
assertThat(
FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_nested_message.bb"))
.isTrue();
// Repeated fields cannot have sub-paths.
assertThat(
FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.repeated_nested_message.bb"))
.isFalse();
// Non-message fields cannot have sub-paths.
assertThat(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload.optional_int32.bb"))
.isFalse();
}
@Test
public void testToString() throws Exception {
assertThat(FieldMaskUtil.toString(FieldMask.getDefaultInstance())).isEmpty();
FieldMask mask = FieldMask.newBuilder().addPaths("foo").build();
assertThat(FieldMaskUtil.toString(mask)).isEqualTo("foo");
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar").build();
assertThat(FieldMaskUtil.toString(mask)).isEqualTo("foo,bar");
// Empty field paths are ignored.
mask =
FieldMask.newBuilder()
.addPaths("")
.addPaths("foo")
.addPaths("")
.addPaths("bar")
.addPaths("")
.build();
assertThat(FieldMaskUtil.toString(mask)).isEqualTo("foo,bar");
}
@Test
public void testFromString() throws Exception {
FieldMask mask = FieldMaskUtil.fromString("");
assertThat(mask.getPathsCount()).isEqualTo(0);
mask = FieldMaskUtil.fromString("foo");
assertThat(mask.getPathsCount()).isEqualTo(1);
assertThat(mask.getPaths(0)).isEqualTo("foo");
mask = FieldMaskUtil.fromString("foo,bar.baz");
assertThat(mask.getPathsCount()).isEqualTo(2);
assertThat(mask.getPaths(0)).isEqualTo("foo");
assertThat(mask.getPaths(1)).isEqualTo("bar.baz");
// Empty field paths are ignore.
mask = FieldMaskUtil.fromString(",foo,,bar,");
assertThat(mask.getPathsCount()).isEqualTo(2);
assertThat(mask.getPaths(0)).isEqualTo("foo");
assertThat(mask.getPaths(1)).isEqualTo("bar");
// Check whether the field paths are valid if a class parameter is provided.
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload");
try {
mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, "payload,nonexist");
assertWithMessage("Exception is expected.").fail();
} catch (IllegalArgumentException e) {
// Expected.
}
}
@Test
public void testFromFieldNumbers() throws Exception {
FieldMask mask = FieldMaskUtil.fromFieldNumbers(TestAllTypes.class);
assertThat(mask.getPathsCount()).isEqualTo(0);
mask =
FieldMaskUtil.fromFieldNumbers(
TestAllTypes.class, TestAllTypes.OPTIONAL_INT32_FIELD_NUMBER);
assertThat(mask.getPathsCount()).isEqualTo(1);
assertThat(mask.getPaths(0)).isEqualTo("optional_int32");
mask =
FieldMaskUtil.fromFieldNumbers(
TestAllTypes.class,
TestAllTypes.OPTIONAL_INT32_FIELD_NUMBER,
TestAllTypes.OPTIONAL_INT64_FIELD_NUMBER);
assertThat(mask.getPathsCount()).isEqualTo(2);
assertThat(mask.getPaths(0)).isEqualTo("optional_int32");
assertThat(mask.getPaths(1)).isEqualTo("optional_int64");
try {
int invalidFieldNumber = 1000;
mask = FieldMaskUtil.fromFieldNumbers(TestAllTypes.class, invalidFieldNumber);
assertWithMessage("Exception is expected.").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testToJsonString() throws Exception {
FieldMask mask = FieldMask.getDefaultInstance();
assertThat(FieldMaskUtil.toJsonString(mask)).isEmpty();
mask = FieldMask.newBuilder().addPaths("foo").build();
assertThat(FieldMaskUtil.toJsonString(mask)).isEqualTo("foo");
mask = FieldMask.newBuilder().addPaths("foo.bar_baz").addPaths("").build();
assertThat(FieldMaskUtil.toJsonString(mask)).isEqualTo("foo.barBaz");
mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar_baz").build();
assertThat(FieldMaskUtil.toJsonString(mask)).isEqualTo("foo,barBaz");
}
@Test
public void testFromJsonString() throws Exception {
FieldMask mask = FieldMaskUtil.fromJsonString("");
assertThat(mask.getPathsCount()).isEqualTo(0);
mask = FieldMaskUtil.fromJsonString("foo");
assertThat(mask.getPathsCount()).isEqualTo(1);
assertThat(mask.getPaths(0)).isEqualTo("foo");
mask = FieldMaskUtil.fromJsonString("foo.barBaz");
assertThat(mask.getPathsCount()).isEqualTo(1);
assertThat(mask.getPaths(0)).isEqualTo("foo.bar_baz");
mask = FieldMaskUtil.fromJsonString("foo,barBaz");
assertThat(mask.getPathsCount()).isEqualTo(2);
assertThat(mask.getPaths(0)).isEqualTo("foo");
assertThat(mask.getPaths(1)).isEqualTo("bar_baz");
}
@Test
public void testFromStringList() throws Exception {
FieldMask mask =
FieldMaskUtil.fromStringList(
NestedTestAllTypes.class, ImmutableList.of("payload.repeated_nested_message", "child"));
assertThat(mask)
.isEqualTo(
FieldMask.newBuilder()
.addPaths("payload.repeated_nested_message")
.addPaths("child")
.build());
mask =
FieldMaskUtil.fromStringList(
NestedTestAllTypes.getDescriptor(),
ImmutableList.of("payload.repeated_nested_message", "child"));
assertThat(mask)
.isEqualTo(
FieldMask.newBuilder()
.addPaths("payload.repeated_nested_message")
.addPaths("child")
.build());
mask =
FieldMaskUtil.fromStringList(ImmutableList.of("payload.repeated_nested_message", "child"));
assertThat(mask)
.isEqualTo(
FieldMask.newBuilder()
.addPaths("payload.repeated_nested_message")
.addPaths("child")
.build());
}
@Test
public void testUnion() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios.
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar");
FieldMask result = FieldMaskUtil.union(mask1, mask2);
assertThat(FieldMaskUtil.toString(result)).isEqualTo("bar,foo");
}
@Test
public void testUnion_usingVarArgs() throws Exception {
FieldMask mask1 = FieldMaskUtil.fromString("foo");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar.quz");
FieldMask mask3 = FieldMaskUtil.fromString("bar.quz");
FieldMask mask4 = FieldMaskUtil.fromString("bar");
FieldMask result = FieldMaskUtil.union(mask1, mask2, mask3, mask4);
assertThat(FieldMaskUtil.toString(result)).isEqualTo("bar,foo");
}
@Test
public void testSubstract() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testRemoveFieldPath} to cover all scenarios.
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar");
FieldMask result = FieldMaskUtil.subtract(mask1, mask2);
assertThat(FieldMaskUtil.toString(result)).isEqualTo("foo");
}
@Test
public void testSubstract_usingVarArgs() throws Exception {
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz.bar");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar.baz.quz");
FieldMask mask3 = FieldMaskUtil.fromString("bar.quz");
FieldMask mask4 = FieldMaskUtil.fromString("foo,bar.baz");
FieldMask result = FieldMaskUtil.subtract(mask1, mask2, mask3, mask4);
assertThat(FieldMaskUtil.toString(result)).isEmpty();
}
@Test
public void testIntersection() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testIntersectFieldPath} to cover all scenarios.
FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz");
FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar");
FieldMask result = FieldMaskUtil.intersection(mask1, mask2);
assertThat(FieldMaskUtil.toString(result)).isEqualTo("bar.baz,bar.quz,foo.bar");
}
@Test
public void testMerge() throws Exception {
// Only test a simple case here and expect
// {@link FieldMaskTreeTest#testMerge} to cover all scenarios.
NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234))
.build();
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
FieldMaskUtil.merge(FieldMaskUtil.fromString("payload"), source, builder);
assertThat(builder.getPayload().getOptionalInt32()).isEqualTo(1234);
}
@Test
public void testTrim() throws Exception {
NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(
TestAllTypes.newBuilder()
.setOptionalInt32(1234)
.setOptionalString("1234")
.setOptionalBool(true))
.build();
FieldMask mask =
FieldMaskUtil.fromStringList(
ImmutableList.of("payload.optional_int32", "payload.optional_string"));
NestedTestAllTypes actual = FieldMaskUtil.trim(mask, source);
assertThat(actual)
.isEqualTo(
NestedTestAllTypes.newBuilder()
.setPayload(
TestAllTypes.newBuilder().setOptionalInt32(1234).setOptionalString("1234"))
.build());
}
}

View File

@@ -0,0 +1,65 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.common.truth.Truth.assertThat;
import com.google.protobuf.Struct;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class StructsTest {
@Test
public void test1pair_constructsObject() throws Exception {
Struct.Builder expected = Struct.newBuilder();
JsonFormat.parser().merge("{\"k1\": 1}", expected);
assertThat(Structs.of("k1", Values.of(1))).isEqualTo(expected.build());
}
@Test
public void test2pair_constructsObject() throws Exception {
Struct.Builder expected = Struct.newBuilder();
JsonFormat.parser().merge("{\"k1\": 1, \"k2\": 2}", expected);
assertThat(Structs.of("k1", Values.of(1), "k2", Values.of(2))).isEqualTo(expected.build());
}
@Test
public void test3pair_constructsObject() throws Exception {
Struct.Builder expected = Struct.newBuilder();
JsonFormat.parser().merge("{\"k1\": 1, \"k2\": 2, \"k3\": 3}", expected);
assertThat(Structs.of("k1", Values.of(1), "k2", Values.of(2), "k3", Values.of(3)))
.isEqualTo(expected.build());
}
}

View File

@@ -0,0 +1,843 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.protobuf.util.DurationsTest.duration;
import com.google.common.collect.Lists;
import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Timestamps}. */
@RunWith(JUnit4.class)
public class TimestampsTest {
private static final int MILLIS_PER_SECOND = 1000;
private static final long MILLIS = 1409130915111L;
private static final long SECONDS = MILLIS / MILLIS_PER_SECOND;
private static final long MICROS = MILLIS * 1000;
private static final long NANOS = MICROS * 1000;
@SuppressWarnings("ConstantOverflow")
private static final long MAX_VALUE = Long.MAX_VALUE * MILLIS_PER_SECOND + MILLIS_PER_SECOND;
@SuppressWarnings("ConstantOverflow")
private static final long MIN_VALUE = Long.MIN_VALUE * MILLIS_PER_SECOND;
private static final Timestamp TIMESTAMP = timestamp(1409130915, 111000000);
private static final Timestamp ZERO_TIMESTAMP = timestamp(0, 0);
private static final Timestamp ONE_OF_TIMESTAMP = timestamp(-1, 999000000);
private static final Timestamp INVALID_MAX =
Timestamp.newBuilder().setSeconds(Long.MAX_VALUE).setNanos(Integer.MAX_VALUE).build();
private static final Timestamp INVALID_MIN =
Timestamp.newBuilder().setSeconds(Long.MIN_VALUE).setNanos(Integer.MIN_VALUE).build();
@Test
public void testMinMaxAreValid() {
assertThat(Timestamps.isValid(Timestamps.MAX_VALUE)).isTrue();
assertThat(Timestamps.isValid(Timestamps.MIN_VALUE)).isTrue();
}
@Test
public void testIsValid_false() {
assertThat(Timestamps.isValid(0L, -1)).isFalse();
assertThat(Timestamps.isValid(1L, -1)).isFalse();
assertThat(Timestamps.isValid(1L, (int) Timestamps.NANOS_PER_SECOND)).isFalse();
assertThat(Timestamps.isValid(-62135596801L, 0)).isFalse();
assertThat(Timestamps.isValid(253402300800L, 0)).isFalse();
}
@Test
public void testIsValid_true() {
assertThat(Timestamps.isValid(0L, 0)).isTrue();
assertThat(Timestamps.isValid(1L, 0)).isTrue();
assertThat(Timestamps.isValid(1L, 1)).isTrue();
assertThat(Timestamps.isValid(42L, 0)).isTrue();
assertThat(Timestamps.isValid(42L, 42)).isTrue();
assertThat(Timestamps.isValid(-62135596800L, 0)).isTrue();
assertThat(Timestamps.isValid(-62135596800L, 1)).isTrue();
assertThat(Timestamps.isValid(62135596799L, 1)).isTrue();
assertThat(Timestamps.isValid(253402300799L, 0)).isTrue();
assertThat(Timestamps.isValid(253402300798L, 1)).isTrue();
assertThat(Timestamps.isValid(253402300798L, (int) (Timestamps.NANOS_PER_SECOND - 1))).isTrue();
}
@Test
public void testTimestampStringFormat() throws Exception {
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
assertThat(start.getSeconds()).isEqualTo(Timestamps.TIMESTAMP_SECONDS_MIN);
assertThat(start.getNanos()).isEqualTo(0);
assertThat(end.getSeconds()).isEqualTo(Timestamps.TIMESTAMP_SECONDS_MAX);
assertThat(end.getNanos()).isEqualTo(999999999);
assertThat(Timestamps.toString(start)).isEqualTo("0001-01-01T00:00:00Z");
assertThat(Timestamps.toString(end)).isEqualTo("9999-12-31T23:59:59.999999999Z");
Timestamp value = Timestamps.parse("1970-01-01T00:00:00Z");
assertThat(value.getSeconds()).isEqualTo(0);
assertThat(value.getNanos()).isEqualTo(0);
value = Timestamps.parseUnchecked("1970-01-01T00:00:00Z");
assertThat(value.getSeconds()).isEqualTo(0);
assertThat(value.getNanos()).isEqualTo(0);
// Test negative timestamps.
value = Timestamps.parse("1969-12-31T23:59:59.999Z");
assertThat(value.getSeconds()).isEqualTo(-1);
// Nano part is in the range of [0, 999999999] for Timestamp.
assertThat(value.getNanos()).isEqualTo(999000000);
value = Timestamps.parseUnchecked("1969-12-31T23:59:59.999Z");
assertThat(value.getSeconds()).isEqualTo(-1);
// Nano part is in the range of [0, 999999999] for Timestamp.
assertThat(value.getNanos()).isEqualTo(999000000);
// Test that 3, 6, or 9 digits are used for the fractional part.
value = Timestamp.newBuilder().setNanos(10).build();
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.000000010Z");
value = Timestamp.newBuilder().setNanos(10000).build();
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.000010Z");
value = Timestamp.newBuilder().setNanos(10000000).build();
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T00:00:00.010Z");
// Test that parsing accepts timezone offsets.
value = Timestamps.parse("1970-01-01T00:00:00.010+08:00");
assertThat(Timestamps.toString(value)).isEqualTo("1969-12-31T16:00:00.010Z");
value = Timestamps.parse("1970-01-01T00:00:00.010-08:00");
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T08:00:00.010Z");
value = Timestamps.parseUnchecked("1970-01-01T00:00:00.010+08:00");
assertThat(Timestamps.toString(value)).isEqualTo("1969-12-31T16:00:00.010Z");
value = Timestamps.parseUnchecked("1970-01-01T00:00:00.010-08:00");
assertThat(Timestamps.toString(value)).isEqualTo("1970-01-01T08:00:00.010Z");
}
private volatile boolean stopParsingThreads = false;
private volatile String errorMessage = "";
private class ParseTimestampThread extends Thread {
private final String[] strings;
private final Timestamp[] values;
public ParseTimestampThread(String[] strings, Timestamp[] values) {
this.strings = strings;
this.values = values;
}
@Override
public void run() {
int index = 0;
while (!stopParsingThreads) {
Timestamp result;
try {
result = Timestamps.parse(strings[index]);
} catch (ParseException e) {
errorMessage = "Failed to parse timestamp: " + strings[index];
break;
}
if (result.getSeconds() != values[index].getSeconds()
|| result.getNanos() != values[index].getNanos()) {
errorMessage =
"Actual result: " + result.toString() + ", expected: " + values[index].toString();
break;
}
index = (index + 1) % strings.length;
}
}
}
@Test
public void testTimestampConcurrentParsing() throws Exception {
String[] timestampStrings =
new String[] {
"0001-01-01T00:00:00Z",
"9999-12-31T23:59:59.999999999Z",
"1970-01-01T00:00:00Z",
"1969-12-31T23:59:59.999Z",
};
Timestamp[] timestampValues = new Timestamp[timestampStrings.length];
for (int i = 0; i < timestampStrings.length; i++) {
timestampValues[i] = Timestamps.parse(timestampStrings[i]);
}
final int threadCount = 16;
final int runningTime = 5000; // in milliseconds.
final List<Thread> threads = new ArrayList<>();
stopParsingThreads = false;
errorMessage = "";
for (int i = 0; i < threadCount; i++) {
Thread thread = new ParseTimestampThread(timestampStrings, timestampValues);
thread.start();
threads.add(thread);
}
Thread.sleep(runningTime);
stopParsingThreads = true;
for (Thread thread : threads) {
thread.join();
}
assertThat(errorMessage).isEmpty();
}
@Test
public void testTimestampInvalidFormatValueTooSmall() throws Exception {
try {
// Value too small.
Timestamp value =
Timestamp.newBuilder().setSeconds(Timestamps.TIMESTAMP_SECONDS_MIN - 1).build();
Timestamps.toString(value);
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testTimestampInvalidFormatValueTooLarge() throws Exception {
try {
// Value too large.
Timestamp value =
Timestamp.newBuilder().setSeconds(Timestamps.TIMESTAMP_SECONDS_MAX + 1).build();
Timestamps.toString(value);
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testTimestampInvalidFormatNanosTooSmall() throws Exception {
try {
// Invalid nanos value.
Timestamp value = Timestamp.newBuilder().setNanos(-1).build();
Timestamps.toString(value);
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testTimestampInvalidFormatNanosTooLarge() throws Exception {
try {
// Invalid nanos value.
Timestamp value = Timestamp.newBuilder().setNanos(1000000000).build();
Timestamps.toString(value);
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testTimestampInvalidFormatDateTooSmall() {
try {
Timestamps.parse("0000-01-01T00:00:00Z");
Assert.fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
assertThat(expected).hasCauseThat().isNotNull();
}
try {
Timestamps.parseUnchecked("0000-01-01T00:00:00Z");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampInvalidFormatDateTooLarge() {
try {
Timestamps.parse("10000-01-01T00:00:00Z");
Assert.fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
}
try {
Timestamps.parseUnchecked("10000-01-01T00:00:00Z");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampInvalidFormatMissingT() {
try {
Timestamps.parse("1970-01-01 00:00:00Z");
Assert.fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
}
try {
Timestamps.parseUnchecked("1970-01-01 00:00:00Z");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampInvalidFormatMissingZ() {
try {
Timestamps.parse("1970-01-01T00:00:00");
assertWithMessage("ParseException is expected.").fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
}
try {
Timestamps.parseUnchecked("1970-01-01T00:00:00");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampInvalidOffset() {
try {
Timestamps.parse("1970-01-01T00:00:00+0000");
assertWithMessage("ParseException is expected.").fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
}
try {
Timestamps.parseUnchecked("1970-01-01T00:00:00+0000");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampInvalidTrailingText() {
try {
Timestamps.parse("1970-01-01T00:00:00Z0");
assertWithMessage("ParseException is expected.").fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
}
try {
Timestamps.parseUnchecked("1970-01-01T00:00:00Z0");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampInvalidNanoSecond() {
try {
Timestamps.parse("1970-01-01T00:00:00.ABCZ");
assertWithMessage("ParseException is expected.").fail();
} catch (ParseException expected) {
Assert.assertNotNull(expected.getMessage());
}
try {
Timestamps.parseUnchecked("1970-01-01T00:00:00.ABCZ");
assertWithMessage("IllegalArgumentException is expected.").fail();
} catch (IllegalArgumentException expected) {
Assert.assertNotNull(expected.getMessage());
}
}
@Test
public void testTimestampConversion() throws Exception {
Timestamp timestamp = Timestamps.parse("1970-01-01T00:00:01.111111111Z");
assertThat(Timestamps.toNanos(timestamp)).isEqualTo(1111111111);
assertThat(Timestamps.toMicros(timestamp)).isEqualTo(1111111);
assertThat(Timestamps.toMillis(timestamp)).isEqualTo(1111);
assertThat(Timestamps.toSeconds(timestamp)).isEqualTo(1);
timestamp = Timestamps.fromNanos(1111111111);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111111111Z");
timestamp = Timestamps.fromMicros(1111111);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111111Z");
timestamp = Timestamps.fromMillis(1111);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z");
timestamp = Timestamps.fromSeconds(1);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01Z");
timestamp = Timestamps.parse("1969-12-31T23:59:59.111111111Z");
assertThat(Timestamps.toNanos(timestamp)).isEqualTo(-888888889);
assertThat(Timestamps.toMicros(timestamp)).isEqualTo(-888889);
assertThat(Timestamps.toMillis(timestamp)).isEqualTo(-889);
assertThat(Timestamps.toSeconds(timestamp)).isEqualTo(-1);
timestamp = Timestamps.fromNanos(-888888889);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111111111Z");
timestamp = Timestamps.fromMicros(-888889);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111111Z");
timestamp = Timestamps.fromMillis(-889);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59.111Z");
timestamp = Timestamps.fromSeconds(-1);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:59Z");
}
@Test
public void testFromDate() {
Date date = new Date(1111);
Timestamp timestamp = Timestamps.fromDate(date);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z");
}
@Test
public void testFromDate_after9999CE() {
// protobuf still requires Java 7 so no java.time :-(
Calendar calendar = Calendar.getInstance();
calendar.clear(); // avoid random number of milliseconds
calendar.setTimeZone(TimeZone.getTimeZone("GMT-0"));
calendar.set(20000, Calendar.OCTOBER, 20, 5, 4, 3);
Date date = calendar.getTime();
try {
Timestamps.fromDate(date);
Assert.fail("should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid.");
}
}
@Test
public void testFromDate_beforeYear1() {
// protobuf still requires Java 7 so no java.time :-(
Calendar calendar = Calendar.getInstance();
calendar.clear(); // avoid random number of milliseconds
calendar.setTimeZone(TimeZone.getTimeZone("GMT-0"));
calendar.set(-32, Calendar.OCTOBER, 20, 5, 4, 3);
Date date = calendar.getTime();
try {
Timestamps.fromDate(date);
Assert.fail("should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid.");
}
}
@Test
public void testFromDate_after2262CE() {
// protobuf still requires Java 7 so no java.time :-(
Calendar calendar = Calendar.getInstance();
calendar.clear(); // avoid random number of milliseconds
calendar.setTimeZone(TimeZone.getTimeZone("GMT-0"));
calendar.set(2299, Calendar.OCTOBER, 20, 5, 4, 3);
Date date = calendar.getTime();
Timestamp timestamp = Timestamps.fromDate(date);
assertThat(Timestamps.toString(timestamp)).isEqualTo("2299-10-20T05:04:03Z");
}
/* Timestamp only stores integral seconds in the Date parent class and stores the nanosecond
* adjustment in the Timestamp class. */
@Test
public void testFromSqlTimestampSubMillisecondPrecision() {
java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(1111);
sqlTimestamp.setNanos(sqlTimestamp.getNanos() + 234567);
Timestamp timestamp = Timestamps.fromDate(sqlTimestamp);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111234567Z");
}
@Test
public void testFromSqlTimestamp() {
Date date = new java.sql.Timestamp(1111);
Timestamp timestamp = Timestamps.fromDate(date);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1970-01-01T00:00:01.111Z");
}
@Test
public void testFromSqlTimestamp_beforeEpoch() {
Date date = new java.sql.Timestamp(-1111);
Timestamp timestamp = Timestamps.fromDate(date);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:58.889Z");
}
@Test
public void testFromSqlTimestamp_beforeEpochWholeSecond() {
Date date = new java.sql.Timestamp(-2000);
Timestamp timestamp = Timestamps.fromDate(date);
assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:58Z");
}
@Test
public void testTimeOperations() throws Exception {
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
Duration duration = Timestamps.between(start, end);
assertThat(Durations.toString(duration)).isEqualTo("315537897599.999999999s");
Timestamp value = Timestamps.add(start, duration);
assertThat(value).isEqualTo(end);
value = Timestamps.subtract(end, duration);
assertThat(value).isEqualTo(start);
duration = Timestamps.between(end, start);
assertThat(Durations.toString(duration)).isEqualTo("-315537897599.999999999s");
value = Timestamps.add(end, duration);
assertThat(value).isEqualTo(start);
value = Timestamps.subtract(start, duration);
assertThat(value).isEqualTo(end);
}
@Test
public void testComparator() {
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 2))).isEqualTo(0);
assertThat(Timestamps.compare(timestamp(0, 0), timestamp(0, 0))).isEqualTo(0);
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(1, 1))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 1))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(1, 1), timestamp(3, 1))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(3, 2))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-1, 1))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-3, 2), timestamp(-3, 3))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-1, 1), timestamp(-3, 1))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-3, 2))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-10, 1), timestamp(1, 1))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(0, 1), timestamp(0, 1))).isEqualTo(0);
assertThat(Timestamps.compare(timestamp(0x80000000L, 0), timestamp(0, 0))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(0xFFFFFFFF00000000L, 0), timestamp(0, 0)))
.isLessThan(0);
Timestamp timestamp0 = timestamp(-50, 400);
Timestamp timestamp1 = timestamp(-50, 500);
Timestamp timestamp2 = timestamp(50, 500);
Timestamp timestamp3 = timestamp(100, 20);
Timestamp timestamp4 = timestamp(100, 50);
Timestamp timestamp5 = timestamp(100, 150);
Timestamp timestamp6 = timestamp(150, 40);
List<Timestamp> timestamps =
Lists.newArrayList(
timestamp5,
timestamp3,
timestamp1,
timestamp4,
timestamp6,
timestamp2,
timestamp0,
timestamp3);
Collections.sort(timestamps, Timestamps.comparator());
assertThat(timestamps)
.containsExactly(
timestamp0,
timestamp1,
timestamp2,
timestamp3,
timestamp3,
timestamp4,
timestamp5,
timestamp6)
.inOrder();
}
@Test
public void testCompare() {
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 2))).isEqualTo(0);
assertThat(Timestamps.compare(timestamp(0, 0), timestamp(0, 0))).isEqualTo(0);
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(1, 1))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(3, 2), timestamp(3, 1))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(1, 1), timestamp(3, 1))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(3, 1), timestamp(3, 2))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-1, 1))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-3, 2), timestamp(-3, 3))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-1, 1), timestamp(-3, 1))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(-3, 1), timestamp(-3, 2))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(-10, 1), timestamp(1, 1))).isLessThan(0);
assertThat(Timestamps.compare(timestamp(0, 1), timestamp(0, 1))).isEqualTo(0);
assertThat(Timestamps.compare(timestamp(0x80000000L, 0), timestamp(0, 0))).isGreaterThan(0);
assertThat(Timestamps.compare(timestamp(0xFFFFFFFF00000000L, 0), timestamp(0, 0)))
.isLessThan(0);
}
@Test
public void testOverflowsArithmeticException() throws Exception {
try {
Timestamps.toNanos(Timestamps.parse("9999-12-31T23:59:59.999999999Z"));
assertWithMessage("Expected an ArithmeticException to be thrown").fail();
} catch (ArithmeticException expected) {
}
}
@Test
public void testPositiveOverflow() {
try {
Timestamps.add(Timestamps.MAX_VALUE, Durations.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testNegativeOverflow() {
try {
Timestamps.subtract(Timestamps.MIN_VALUE, Durations.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMaxNanosecondsOverflow() {
try {
Timestamps.toNanos(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMaxMicrosecondsOverflow() {
try {
Timestamps.toMicros(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMaxMillisecondsOverflow() {
try {
Timestamps.toMillis(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMaxSecondsOverflow() {
try {
Timestamps.toSeconds(INVALID_MAX);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMinNanosecondsOverflow() {
try {
Timestamps.toNanos(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMicrosecondsMinOverflow() {
try {
Timestamps.toMicros(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testInvalidMinMillisecondsOverflow() {
try {
Timestamps.toMillis(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testOverInvalidMinSecondsflow() {
try {
Timestamps.toSeconds(INVALID_MIN);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testMaxNanosecondsConversion() {
assertThat(Timestamps.toString(Timestamps.fromNanos(Long.MAX_VALUE)))
.isEqualTo("2262-04-11T23:47:16.854775807Z");
}
@Test
public void testIllegalArgumentExceptionForMaxMicroseconds() {
try {
Timestamps.fromMicros(Long.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testIllegalArgumentExceptionForMaxMilliseconds() {
try {
Durations.fromMillis(Long.MAX_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testMinNanosecondsConversion() {
assertThat(Timestamps.toString(Timestamps.fromNanos(Long.MIN_VALUE)))
.isEqualTo("1677-09-21T00:12:43.145224192Z");
}
@Test
public void testIllegalArgumentExceptionForMinMicroseconds() {
try {
Timestamps.fromMicros(Long.MIN_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testIllegalArgumentExceptionForMinMilliseconds() {
try {
Timestamps.fromMillis(Long.MIN_VALUE);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testConvertFromSeconds() {
assertThat(Timestamps.fromSeconds(SECONDS).getSeconds()).isEqualTo(TIMESTAMP.getSeconds());
assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromSeconds(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromSeconds(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
}
@Test
public void testConvertFromMillis() {
assertThat(Timestamps.fromMillis(MILLIS)).isEqualTo(TIMESTAMP);
assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromMillis(-1)).isEqualTo(ONE_OF_TIMESTAMP);
assertThat(Timestamps.fromMillis(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromMillis(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
}
@Test
public void testConvertFromMicros() {
assertThat(Timestamps.fromMicros(MICROS)).isEqualTo(TIMESTAMP);
assertThat(Timestamps.EPOCH).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromMicros(-1000)).isEqualTo(ONE_OF_TIMESTAMP);
assertThat(Timestamps.fromMicros(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromMicros(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
}
@Test
public void testConvertToSeconds() {
assertThat(Timestamps.toSeconds(TIMESTAMP)).isEqualTo(SECONDS);
assertThat(Timestamps.toSeconds(ZERO_TIMESTAMP)).isEqualTo(0);
assertThat(Timestamps.toSeconds(ONE_OF_TIMESTAMP)).isEqualTo(-1);
}
@Test
public void testConvertToMillis() {
assertThat(Timestamps.toMillis(TIMESTAMP)).isEqualTo(MILLIS);
assertThat(Timestamps.toMillis(ZERO_TIMESTAMP)).isEqualTo(0);
assertThat(Timestamps.toMillis(ONE_OF_TIMESTAMP)).isEqualTo(-1);
}
@Test
public void testConvertToMicros() {
assertThat(Timestamps.toMicros(TIMESTAMP)).isEqualTo(MICROS);
assertThat(Timestamps.toMicros(ZERO_TIMESTAMP)).isEqualTo(0);
assertThat(Timestamps.toMicros(ONE_OF_TIMESTAMP)).isEqualTo(-1000);
}
@Test
public void testConvertFromMillisAboveTimestampMaxLimit() {
long timestampMaxSeconds = 253402300799L;
try {
Timestamps.fromMillis((timestampMaxSeconds + 1) * MILLIS_PER_SECOND);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testConvertFromMillisBelowTimestampMaxLimit() {
long timestampMinSeconds = -62135596800L;
try {
Timestamps.fromMillis((timestampMinSeconds - 1) * MILLIS_PER_SECOND);
assertWithMessage("Expected an IllegalArgumentException to be thrown").fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testConvertFromNanos() {
assertThat(Timestamps.fromNanos(NANOS)).isEqualTo(TIMESTAMP);
assertThat(Timestamps.fromNanos(0)).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromNanos(-1000000)).isEqualTo(ONE_OF_TIMESTAMP);
assertThat(Timestamps.fromNanos(MAX_VALUE)).isEqualTo(ZERO_TIMESTAMP);
assertThat(Timestamps.fromNanos(MIN_VALUE)).isEqualTo(ZERO_TIMESTAMP);
}
@Test
public void testConvertToNanos() {
assertThat(Timestamps.toNanos(TIMESTAMP)).isEqualTo(NANOS);
assertThat(Timestamps.toNanos(ZERO_TIMESTAMP)).isEqualTo(0);
assertThat(Timestamps.toNanos(ONE_OF_TIMESTAMP)).isEqualTo(-1000000);
}
@Test
public void testAdd() {
assertThat(Timestamps.add(timestamp(1, 10), duration(1, 20))).isEqualTo(timestamp(2, 30));
assertThat(Timestamps.add(timestamp(1, 10), duration(-1, -11)))
.isEqualTo(timestamp(-1, 999999999));
assertThat(Timestamps.add(timestamp(10, 10), duration(-1, -11)))
.isEqualTo(timestamp(8, 999999999));
assertThat(Timestamps.add(timestamp(1, 1), duration(1, 999999999))).isEqualTo(timestamp(3, 0));
assertThat(Timestamps.add(timestamp(1, 2), duration(1, 999999999))).isEqualTo(timestamp(3, 1));
}
@Test
public void testSubtractDuration() {
assertThat(Timestamps.subtract(timestamp(1, 10), duration(-1, -20)))
.isEqualTo(timestamp(2, 30));
assertThat(Timestamps.subtract(timestamp(1, 10), duration(1, 11)))
.isEqualTo(timestamp(-1, 999999999));
assertThat(Timestamps.subtract(timestamp(10, 10), duration(1, 11)))
.isEqualTo(timestamp(8, 999999999));
assertThat(Timestamps.subtract(timestamp(1, 1), duration(-1, -999999999)))
.isEqualTo(timestamp(3, 0));
assertThat(Timestamps.subtract(timestamp(1, 2), duration(-1, -999999999)))
.isEqualTo(timestamp(3, 1));
}
static Timestamp timestamp(long seconds, int nanos) {
return Timestamps.checkValid(
Timestamps.checkValid(Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos)));
}
}

View File

@@ -0,0 +1,112 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.util;
import static com.google.common.truth.Truth.assertThat;
import com.google.protobuf.ListValue;
import com.google.protobuf.NullValue;
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class ValuesTest {
@Test
public void testOfNull_IsNullValue() throws Exception {
assertThat(Values.ofNull())
.isEqualTo(Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build());
}
@Test
public void testOfBoolean_ConstructsValue() {
assertThat(Values.of(true)).isEqualTo(Value.newBuilder().setBoolValue(true).build());
assertThat(Values.of(false)).isEqualTo(Value.newBuilder().setBoolValue(false).build());
}
@Test
public void testOfNumeric_ConstructsValue() {
assertThat(Values.of(100)).isEqualTo(Value.newBuilder().setNumberValue(100).build());
assertThat(Values.of(1000L)).isEqualTo(Value.newBuilder().setNumberValue(1000).build());
assertThat(Values.of(1000.23f)).isEqualTo(Value.newBuilder().setNumberValue(1000.23f).build());
assertThat(Values.of(10000.23)).isEqualTo(Value.newBuilder().setNumberValue(10000.23).build());
}
@Test
public void testOfString_ConstructsValue() {
assertThat(Values.of("")).isEqualTo(Value.newBuilder().setStringValue("").build());
assertThat(Values.of("foo")).isEqualTo(Value.newBuilder().setStringValue("foo").build());
}
@Test
public void testOfStruct_ConstructsValue() {
Struct.Builder builder = Struct.newBuilder();
builder.putFields("a", Values.of("a"));
builder.putFields("b", Values.of("b"));
assertThat(Values.of(builder.build()))
.isEqualTo(Value.newBuilder().setStructValue(builder.build()).build());
}
@Test
public void testOfListValue_ConstructsInstance() {
ListValue.Builder builder = ListValue.newBuilder();
builder.addValues(Values.of(1));
builder.addValues(Values.of(2));
assertThat(Values.of(builder.build()))
.isEqualTo(Value.newBuilder().setListValue(builder.build()).build());
}
@Test
public void testOfIterable_ReturnsTheValue() {
ListValue.Builder builder = ListValue.newBuilder();
builder.addValues(Values.of(1));
builder.addValues(Values.of(2));
builder.addValues(Values.of(true));
builder.addValues(Value.newBuilder().setListValue(builder.build()).build());
List<Value> list = new ArrayList<>();
list.add(Values.of(1));
list.add(Values.of(2));
list.add(Values.of(true));
List<Value> copyList = new ArrayList<>(list);
list.add(Values.of(copyList));
assertThat(Values.of(list)).isEqualTo(Value.newBuilder().setListValue(builder).build());
assertThat(Values.of(new ArrayList<Value>()))
.isEqualTo(Value.newBuilder().setListValue(ListValue.getDefaultInstance()).build());
}
}

View File

@@ -0,0 +1,190 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package json_test;
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
option java_package = "com.google.protobuf.util.proto";
option java_outer_classname = "JsonTestProto";
message TestAllTypes {
enum NestedEnum {
FOO = 0;
BAR = 1;
BAZ = 2;
}
enum AliasedEnum {
option allow_alias = true;
ALIAS_FOO = 0;
ALIAS_BAR = 1;
ALIAS_BAZ = 2;
QUX = 2;
qux = 2;
bAz = 2;
}
message NestedMessage {
int32 value = 1;
}
int32 optional_int32 = 1;
int64 optional_int64 = 2;
uint32 optional_uint32 = 3;
uint64 optional_uint64 = 4;
sint32 optional_sint32 = 5;
sint64 optional_sint64 = 6;
fixed32 optional_fixed32 = 7;
fixed64 optional_fixed64 = 8;
sfixed32 optional_sfixed32 = 9;
sfixed64 optional_sfixed64 = 10;
float optional_float = 11;
double optional_double = 12;
bool optional_bool = 13;
string optional_string = 14;
bytes optional_bytes = 15;
NestedMessage optional_nested_message = 18;
NestedEnum optional_nested_enum = 21;
AliasedEnum optional_aliased_enum = 52;
// Repeated
repeated int32 repeated_int32 = 31;
repeated int64 repeated_int64 = 32;
repeated uint32 repeated_uint32 = 33;
repeated uint64 repeated_uint64 = 34;
repeated sint32 repeated_sint32 = 35;
repeated sint64 repeated_sint64 = 36;
repeated fixed32 repeated_fixed32 = 37;
repeated fixed64 repeated_fixed64 = 38;
repeated sfixed32 repeated_sfixed32 = 39;
repeated sfixed64 repeated_sfixed64 = 40;
repeated float repeated_float = 41;
repeated double repeated_double = 42;
repeated bool repeated_bool = 43;
repeated string repeated_string = 44;
repeated bytes repeated_bytes = 45;
repeated NestedMessage repeated_nested_message = 48;
repeated NestedEnum repeated_nested_enum = 51;
}
message TestOneof {
oneof oneof_field {
int32 oneof_int32 = 1;
TestAllTypes.NestedMessage oneof_nested_message = 2;
google.protobuf.NullValue oneof_null_value = 3;
}
}
message TestMap {
// Instead of testing all combinations (too many), we only make sure all
// valid types have been used at least in one field as key and in one
// field as value.
map<int32, int32> int32_to_int32_map = 1;
map<int64, int32> int64_to_int32_map = 2;
map<uint32, int32> uint32_to_int32_map = 3;
map<uint64, int32> uint64_to_int32_map = 4;
map<sint32, int32> sint32_to_int32_map = 5;
map<sint64, int32> sint64_to_int32_map = 6;
map<fixed32, int32> fixed32_to_int32_map = 7;
map<fixed64, int32> fixed64_to_int32_map = 8;
map<sfixed32, int32> sfixed32_to_int32_map = 9;
map<sfixed64, int32> sfixed64_to_int32_map = 10;
map<bool, int32> bool_to_int32_map = 11;
map<string, int32> string_to_int32_map = 12;
map<int32, int64> int32_to_int64_map = 101;
map<int32, uint32> int32_to_uint32_map = 102;
map<int32, uint64> int32_to_uint64_map = 103;
map<int32, sint32> int32_to_sint32_map = 104;
map<int32, sint64> int32_to_sint64_map = 105;
map<int32, fixed32> int32_to_fixed32_map = 106;
map<int32, fixed64> int32_to_fixed64_map = 107;
map<int32, sfixed32> int32_to_sfixed32_map = 108;
map<int32, sfixed64> int32_to_sfixed64_map = 109;
map<int32, float> int32_to_float_map = 110;
map<int32, double> int32_to_double_map = 111;
map<int32, bool> int32_to_bool_map = 112;
map<int32, string> int32_to_string_map = 113;
map<int32, bytes> int32_to_bytes_map = 114;
map<int32, TestAllTypes.NestedMessage> int32_to_message_map = 115;
map<int32, TestAllTypes.NestedEnum> int32_to_enum_map = 116;
}
message TestWrappers {
google.protobuf.Int32Value int32_value = 1;
google.protobuf.UInt32Value uint32_value = 2;
google.protobuf.Int64Value int64_value = 3;
google.protobuf.UInt64Value uint64_value = 4;
google.protobuf.FloatValue float_value = 5;
google.protobuf.DoubleValue double_value = 6;
google.protobuf.BoolValue bool_value = 7;
google.protobuf.StringValue string_value = 8;
google.protobuf.BytesValue bytes_value = 9;
}
message TestTimestamp {
google.protobuf.Timestamp timestamp_value = 1;
}
message TestDuration {
google.protobuf.Duration duration_value = 1;
}
message TestFieldMask {
google.protobuf.FieldMask field_mask_value = 1;
}
message TestStruct {
google.protobuf.Struct struct_value = 1;
google.protobuf.Value value = 2;
google.protobuf.ListValue list_value = 3;
}
message TestAny {
google.protobuf.Any any_value = 1;
map<string, google.protobuf.Any> any_map = 2;
}
message TestCustomJsonName {
int32 value = 1 [json_name = "@value"];
}
message TestRecursive {
int32 value = 1;
TestRecursive nested = 2;
}