1 /* 2 * Copyright 2020 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.auto.value.extension.serializable.processor; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static com.google.common.truth.Truth8.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.auto.value.AutoValue; 23 import com.google.auto.value.extension.memoized.Memoized; 24 import com.google.auto.value.extension.serializable.SerializableAutoValue; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableMap; 27 import com.google.common.testing.SerializableTester; 28 import java.io.ByteArrayOutputStream; 29 import java.io.NotSerializableException; 30 import java.io.ObjectOutputStream; 31 import java.io.Serializable; 32 import java.util.Optional; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.JUnit4; 36 37 @RunWith(JUnit4.class) 38 public final class SerializableAutoValueExtensionTest { 39 private static final String A = "a"; 40 private static final int B = 1; 41 private static final String C = "c"; 42 private static final int D = 2; 43 44 @SerializableAutoValue 45 @AutoValue 46 abstract static class DummySerializableAutoValue implements Serializable { 47 // Primitive fields a()48 abstract String a(); 49 b()50 abstract int b(); 51 52 // Optional fields optionalC()53 abstract Optional<String> optionalC(); 54 optionalD()55 abstract Optional<Integer> optionalD(); 56 builder()57 static DummySerializableAutoValue.Builder builder() { 58 return new AutoValue_SerializableAutoValueExtensionTest_DummySerializableAutoValue.Builder(); 59 } 60 61 @AutoValue.Builder 62 abstract static class Builder { setA(String value)63 abstract DummySerializableAutoValue.Builder setA(String value); 64 setB(int value)65 abstract DummySerializableAutoValue.Builder setB(int value); 66 setOptionalC(String value)67 abstract DummySerializableAutoValue.Builder setOptionalC(String value); 68 setOptionalD(int value)69 abstract DummySerializableAutoValue.Builder setOptionalD(int value); 70 build()71 abstract DummySerializableAutoValue build(); 72 } 73 } 74 75 @Test allFieldsAreSet_noEmpty()76 public void allFieldsAreSet_noEmpty() { 77 DummySerializableAutoValue autoValue = 78 DummySerializableAutoValue.builder() 79 .setA(A) 80 .setB(B) 81 .setOptionalC(C) 82 .setOptionalD(D) 83 .build(); 84 85 assertThat(autoValue.a()).isEqualTo(A); 86 assertThat(autoValue.b()).isEqualTo(B); 87 assertThat(autoValue.optionalC()).hasValue(C); 88 assertThat(autoValue.optionalD()).hasValue(D); 89 } 90 91 @Test allFieldsAreSet_withMixedEmpty()92 public void allFieldsAreSet_withMixedEmpty() { 93 DummySerializableAutoValue autoValue = 94 DummySerializableAutoValue.builder().setA(A).setB(B).setOptionalC(C).build(); 95 96 assertThat(autoValue.a()).isEqualTo(A); 97 assertThat(autoValue.b()).isEqualTo(B); 98 assertThat(autoValue.optionalC()).hasValue(C); 99 assertThat(autoValue.optionalD()).isEmpty(); 100 } 101 102 @Test allFieldsAreSet_withEmpty()103 public void allFieldsAreSet_withEmpty() { 104 DummySerializableAutoValue autoValue = 105 DummySerializableAutoValue.builder().setA(A).setB(B).build(); 106 107 assertThat(autoValue.a()).isEqualTo(A); 108 assertThat(autoValue.b()).isEqualTo(B); 109 assertThat(autoValue.optionalC()).isEmpty(); 110 assertThat(autoValue.optionalD()).isEmpty(); 111 } 112 113 @Test allFieldsAreSerialized_noEmpty()114 public void allFieldsAreSerialized_noEmpty() { 115 DummySerializableAutoValue autoValue = 116 DummySerializableAutoValue.builder() 117 .setA(A) 118 .setB(B) 119 .setOptionalC(C) 120 .setOptionalD(D) 121 .build(); 122 123 DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 124 125 assertThat(actualAutoValue).isEqualTo(autoValue); 126 } 127 128 @Test allFieldsAreSerialized_withEmpty()129 public void allFieldsAreSerialized_withEmpty() { 130 DummySerializableAutoValue autoValue = 131 DummySerializableAutoValue.builder().setA(A).setB(B).build(); 132 133 DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 134 135 assertThat(actualAutoValue).isEqualTo(autoValue); 136 } 137 138 @Test allFieldsAreSerialized_withMixedEmpty()139 public void allFieldsAreSerialized_withMixedEmpty() { 140 DummySerializableAutoValue autoValue = 141 DummySerializableAutoValue.builder().setA(A).setB(B).setOptionalC(C).build(); 142 143 DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 144 145 assertThat(actualAutoValue).isEqualTo(autoValue); 146 } 147 148 @SerializableAutoValue 149 @AutoValue 150 abstract static class PrefixSerializableAutoValue implements Serializable { 151 // Primitive fields getA()152 abstract String getA(); 153 isB()154 abstract boolean isB(); 155 156 // Optional fields getC()157 abstract Optional<String> getC(); 158 getD()159 abstract Optional<Boolean> getD(); 160 builder()161 static PrefixSerializableAutoValue.Builder builder() { 162 return new AutoValue_SerializableAutoValueExtensionTest_PrefixSerializableAutoValue.Builder(); 163 } 164 165 @AutoValue.Builder 166 abstract static class Builder { a(String value)167 abstract PrefixSerializableAutoValue.Builder a(String value); 168 b(boolean value)169 abstract PrefixSerializableAutoValue.Builder b(boolean value); 170 c(String value)171 abstract PrefixSerializableAutoValue.Builder c(String value); 172 d(boolean value)173 abstract PrefixSerializableAutoValue.Builder d(boolean value); 174 build()175 abstract PrefixSerializableAutoValue build(); 176 } 177 } 178 179 @Test allPrefixFieldsAreSerialized_noEmpty()180 public void allPrefixFieldsAreSerialized_noEmpty() { 181 PrefixSerializableAutoValue autoValue = 182 PrefixSerializableAutoValue.builder().a("A").b(true).c("C").d(false).build(); 183 184 PrefixSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 185 186 assertThat(actualAutoValue).isEqualTo(autoValue); 187 } 188 189 @Test allPrefixFieldsAreSerialized_WithEmpty()190 public void allPrefixFieldsAreSerialized_WithEmpty() { 191 PrefixSerializableAutoValue autoValue = 192 PrefixSerializableAutoValue.builder().a("A").b(true).build(); 193 194 PrefixSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 195 196 assertThat(actualAutoValue).isEqualTo(autoValue); 197 } 198 199 @SerializableAutoValue 200 @AutoValue 201 abstract static class NotSerializable { create()202 static NotSerializable create() { 203 return new AutoValue_SerializableAutoValueExtensionTest_NotSerializable(Optional.of("A")); 204 } 205 optionalA()206 abstract Optional<String> optionalA(); 207 } 208 209 @Test missingImplementsSerializableThrowsException()210 public void missingImplementsSerializableThrowsException() throws Exception { 211 NotSerializable autoValue = NotSerializable.create(); 212 ByteArrayOutputStream bo = new ByteArrayOutputStream(); 213 ObjectOutputStream so = new ObjectOutputStream(bo); 214 215 assertThrows(NotSerializableException.class, () -> so.writeObject(autoValue)); 216 } 217 218 @AutoValue 219 abstract static class NotSerializableNoAnnotation implements Serializable { create()220 static NotSerializableNoAnnotation create() { 221 return new AutoValue_SerializableAutoValueExtensionTest_NotSerializableNoAnnotation( 222 Optional.of("A")); 223 } 224 optionalA()225 abstract Optional<String> optionalA(); 226 } 227 228 @Test missingSerializableAutoValueAnnotationThrowsException()229 public void missingSerializableAutoValueAnnotationThrowsException() throws Exception { 230 NotSerializableNoAnnotation autoValue = NotSerializableNoAnnotation.create(); 231 ByteArrayOutputStream bo = new ByteArrayOutputStream(); 232 ObjectOutputStream so = new ObjectOutputStream(bo); 233 234 assertThrows(NotSerializableException.class, () -> so.writeObject(autoValue)); 235 } 236 237 @SerializableAutoValue 238 @AutoValue 239 // Technically all type parameters should extend serializable, but for the purposes of testing, 240 // only one type parameter is bounded. 241 abstract static class HasTypeParameters<T extends Serializable, S> implements Serializable { a()242 abstract T a(); 243 optionalB()244 abstract Optional<S> optionalB(); 245 builder()246 static <T extends Serializable, S> Builder<T, S> builder() { 247 return new AutoValue_SerializableAutoValueExtensionTest_HasTypeParameters.Builder<>(); 248 } 249 250 @AutoValue.Builder 251 abstract static class Builder<T extends Serializable, S> { setA(T value)252 abstract Builder<T, S> setA(T value); 253 setOptionalB(S value)254 abstract Builder<T, S> setOptionalB(S value); 255 build()256 abstract HasTypeParameters<T, S> build(); 257 } 258 } 259 260 @Test typeParameterizedFieldsAreSet_noEmpty()261 public void typeParameterizedFieldsAreSet_noEmpty() { 262 HasTypeParameters<String, Integer> autoValue = 263 HasTypeParameters.<String, Integer>builder().setA(A).setOptionalB(B).build(); 264 265 assertThat(autoValue.a()).isEqualTo(A); 266 assertThat(autoValue.optionalB()).hasValue(B); 267 } 268 269 @Test typeParameterizedFieldsAreSet_withEmpty()270 public void typeParameterizedFieldsAreSet_withEmpty() { 271 HasTypeParameters<String, Integer> autoValue = 272 HasTypeParameters.<String, Integer>builder().setA(A).build(); 273 274 assertThat(autoValue.a()).isEqualTo(A); 275 assertThat(autoValue.optionalB()).isEmpty(); 276 } 277 278 @Test typeParameterizedFieldsAreSerializable_noEmpty()279 public void typeParameterizedFieldsAreSerializable_noEmpty() { 280 HasTypeParameters<String, Integer> autoValue = 281 HasTypeParameters.<String, Integer>builder().setA(A).setOptionalB(B).build(); 282 283 HasTypeParameters<String, Integer> actualAutoValue = SerializableTester.reserialize(autoValue); 284 285 assertThat(actualAutoValue).isEqualTo(autoValue); 286 } 287 288 @Test typeParameterizedFieldsAreSerializable_withEmpty()289 public void typeParameterizedFieldsAreSerializable_withEmpty() { 290 HasTypeParameters<String, Integer> autoValue = 291 HasTypeParameters.<String, Integer>builder().setA(A).build(); 292 293 HasTypeParameters<String, Integer> actualAutoValue = SerializableTester.reserialize(autoValue); 294 295 assertThat(actualAutoValue).isEqualTo(autoValue); 296 } 297 298 @SerializableAutoValue 299 @AutoValue 300 abstract static class ImmutableListSerializableAutoValue implements Serializable { payload()301 abstract ImmutableList<Optional<String>> payload(); 302 builder()303 static ImmutableListSerializableAutoValue.Builder builder() { 304 return new AutoValue_SerializableAutoValueExtensionTest_ImmutableListSerializableAutoValue 305 .Builder(); 306 } 307 308 @AutoValue.Builder 309 abstract static class Builder { setPayload( ImmutableList<Optional<String>> payload)310 abstract ImmutableListSerializableAutoValue.Builder setPayload( 311 ImmutableList<Optional<String>> payload); 312 build()313 abstract ImmutableListSerializableAutoValue build(); 314 } 315 } 316 317 @Test immutableList_emptyListSerialized()318 public void immutableList_emptyListSerialized() { 319 ImmutableListSerializableAutoValue autoValue = 320 ImmutableListSerializableAutoValue.builder().setPayload(ImmutableList.of()).build(); 321 322 ImmutableListSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 323 324 assertThat(actualAutoValue).isEqualTo(autoValue); 325 } 326 327 @Test immutableList_allFieldsSetAndSerialized()328 public void immutableList_allFieldsSetAndSerialized() { 329 ImmutableListSerializableAutoValue autoValue = 330 ImmutableListSerializableAutoValue.builder() 331 .setPayload(ImmutableList.of(Optional.of("a1"), Optional.of("a2"))) 332 .build(); 333 334 ImmutableListSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 335 336 assertThat(actualAutoValue).isEqualTo(autoValue); 337 } 338 339 @SerializableAutoValue 340 @AutoValue 341 abstract static class ImmutableMapSerializableAutoValue implements Serializable { a()342 abstract ImmutableMap<Optional<String>, String> a(); 343 b()344 abstract ImmutableMap<String, Optional<String>> b(); 345 builder()346 static ImmutableMapSerializableAutoValue.Builder builder() { 347 return new AutoValue_SerializableAutoValueExtensionTest_ImmutableMapSerializableAutoValue 348 .Builder(); 349 } 350 351 @AutoValue.Builder 352 abstract static class Builder { setA( ImmutableMap<Optional<String>, String> a)353 abstract ImmutableMapSerializableAutoValue.Builder setA( 354 ImmutableMap<Optional<String>, String> a); 355 setB( ImmutableMap<String, Optional<String>> b)356 abstract ImmutableMapSerializableAutoValue.Builder setB( 357 ImmutableMap<String, Optional<String>> b); 358 build()359 abstract ImmutableMapSerializableAutoValue build(); 360 } 361 } 362 363 @Test immutableMap_emptyMapSerialized()364 public void immutableMap_emptyMapSerialized() { 365 ImmutableMapSerializableAutoValue autoValue = 366 ImmutableMapSerializableAutoValue.builder() 367 .setA(ImmutableMap.of()) 368 .setB(ImmutableMap.of()) 369 .build(); 370 371 ImmutableMapSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 372 373 assertThat(actualAutoValue).isEqualTo(autoValue); 374 } 375 376 @Test immutableMap_allFieldsSetAndSerialized()377 public void immutableMap_allFieldsSetAndSerialized() { 378 ImmutableMapSerializableAutoValue autoValue = 379 ImmutableMapSerializableAutoValue.builder() 380 .setA(ImmutableMap.of(Optional.of("key"), "value")) 381 .setB(ImmutableMap.of("key", Optional.of("value"))) 382 .build(); 383 384 ImmutableMapSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); 385 386 assertThat(actualAutoValue).isEqualTo(autoValue); 387 } 388 389 @SerializableAutoValue 390 @AutoValue 391 abstract static class MultiplePropertiesSameType implements Serializable { a()392 abstract String a(); 393 b()394 abstract String b(); 395 builder()396 static MultiplePropertiesSameType.Builder builder() { 397 return new AutoValue_SerializableAutoValueExtensionTest_MultiplePropertiesSameType.Builder(); 398 } 399 400 @AutoValue.Builder 401 abstract static class Builder { setA(String value)402 abstract MultiplePropertiesSameType.Builder setA(String value); 403 setB(String value)404 abstract MultiplePropertiesSameType.Builder setB(String value); 405 build()406 abstract MultiplePropertiesSameType build(); 407 } 408 } 409 410 @Test multiplePropertiesSameType_allFieldsSerialized()411 public void multiplePropertiesSameType_allFieldsSerialized() { 412 MultiplePropertiesSameType autoValue = 413 MultiplePropertiesSameType.builder().setA("A").setB("B").build(); 414 415 MultiplePropertiesSameType actualAutoValue = SerializableTester.reserialize(autoValue); 416 417 assertThat(actualAutoValue).isEqualTo(autoValue); 418 } 419 420 /** 421 * Type that may result in nested lambdas in the generated code. Including this allows us to 422 * verify that we handle those correctly, in particular not reusing a lambda parameter name in 423 * another lambda nested inside the first one. 424 */ 425 @SerializableAutoValue 426 @AutoValue 427 abstract static class ComplexType implements Serializable { a()428 abstract ImmutableMap<String, ImmutableMap<String, Optional<String>>> a(); 429 builder()430 static ComplexType.Builder builder() { 431 return new AutoValue_SerializableAutoValueExtensionTest_ComplexType.Builder(); 432 } 433 434 @AutoValue.Builder 435 abstract static class Builder { setA( ImmutableMap<String, ImmutableMap<String, Optional<String>>> a)436 abstract ComplexType.Builder setA( 437 ImmutableMap<String, ImmutableMap<String, Optional<String>>> a); 438 build()439 abstract ComplexType build(); 440 } 441 } 442 443 @Test complexType()444 public void complexType() { 445 ImmutableMap<String, ImmutableMap<String, Optional<String>>> map = 446 ImmutableMap.of("foo", ImmutableMap.of("bar", Optional.of("baz"))); 447 ComplexType complexType = ComplexType.builder().setA(map).build(); 448 449 ComplexType reserialized = SerializableTester.reserialize(complexType); 450 451 assertThat(reserialized).isEqualTo(complexType); 452 } 453 454 /** 455 * Type that uses both {@code @SerializableAutoValue} and {@code @Memoized}, showing that the two 456 * extensions work correctly together. 457 */ 458 @SerializableAutoValue 459 @AutoValue 460 abstract static class SerializeMemoize implements Serializable { number()461 abstract Optional<Integer> number(); 462 private transient int methodCount; 463 464 @Memoized negate()465 Optional<Integer> negate() { 466 methodCount++; 467 return number().map(n -> -n); 468 } 469 builder()470 static SerializeMemoize.Builder builder() { 471 return new AutoValue_SerializableAutoValueExtensionTest_SerializeMemoize.Builder(); 472 } 473 474 @AutoValue.Builder 475 abstract static class Builder { setNumber(Optional<Integer> number)476 abstract Builder setNumber(Optional<Integer> number); build()477 abstract SerializeMemoize build(); 478 } 479 } 480 481 @Test serializeMemoize()482 public void serializeMemoize() { 483 SerializeMemoize instance1 = SerializeMemoize.builder().setNumber(Optional.of(17)).build(); 484 assertThat(instance1.methodCount).isEqualTo(0); 485 assertThat(instance1.negate()).hasValue(-17); 486 assertThat(instance1.methodCount).isEqualTo(1); 487 assertThat(instance1.negate()).hasValue(-17); 488 assertThat(instance1.methodCount).isEqualTo(1); 489 SerializeMemoize instance2 = SerializableTester.reserialize(instance1); 490 assertThat(instance2.methodCount).isEqualTo(0); 491 assertThat(instance2.negate()).hasValue(-17); 492 assertThat(instance2.methodCount).isEqualTo(1); 493 assertThat(instance2.negate()).hasValue(-17); 494 assertThat(instance2.methodCount).isEqualTo(1); 495 496 } 497 } 498