LCOV - code coverage report
Current view: top level - proto/testing - SerializedClassSubject.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 33 35 94.3 %
Date: 2022-11-19 15:00:39 Functions: 12 12 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2018 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.proto.testing;
      16             : 
      17             : import static com.google.common.collect.ImmutableMap.toImmutableMap;
      18             : import static com.google.common.truth.Fact.simpleFact;
      19             : import static com.google.common.truth.Truth.assertAbout;
      20             : 
      21             : import com.google.common.truth.FailureMetadata;
      22             : import com.google.common.truth.Subject;
      23             : import java.lang.reflect.Field;
      24             : import java.lang.reflect.Method;
      25             : import java.lang.reflect.Modifier;
      26             : import java.lang.reflect.Type;
      27             : import java.util.Arrays;
      28             : import java.util.Map;
      29             : import org.apache.commons.lang3.reflect.FieldUtils;
      30             : 
      31             : /**
      32             :  * Subject about classes that are serialized into persistent caches or indices.
      33             :  *
      34             :  * <p>Hand-written {@link com.google.gerrit.server.cache.serialize.CacheSerializer CacheSerializer}
      35             :  * and {@link com.google.gerrit.entities.converter.ProtoConverter ProtoConverter} implementations
      36             :  * depend on the exact representation of the data stored in a class, so it is important to verify
      37             :  * any assumptions about the structure of the serialized classes. This class contains assertions
      38             :  * about serialized classes, and should be used for every class that has a custom serializer
      39             :  * implementation.
      40             :  *
      41             :  * <p>Changing fields of a serialized class (or abstract methods, in the case of {@code @AutoValue}
      42             :  * classes) will likely require changes to the serializer implementation, and may require bumping
      43             :  * the {@link com.google.gerrit.server.cache.PersistentCacheBinding#version(int) version} in the
      44             :  * cache binding, in case the representation has changed in such a way that old serialized data
      45             :  * becomes unreadable.
      46             :  *
      47             :  * <p>Changes to a serialized class such as adding or removing fields generally requires a change to
      48             :  * the hand-written serializer. Usually, serializer implementations should be written in such a way
      49             :  * that new fields are considered optional, and won't require bumping the version.
      50             :  */
      51             : public class SerializedClassSubject extends Subject {
      52             :   public static SerializedClassSubject assertThatSerializedClass(Class<?> actual) {
      53             :     // This formulation fails in Eclipse 4.7.3a with "The type
      54             :     // SerializedClassSubject does not define SerializedClassSubject() that is
      55             :     // applicable here", due to
      56             :     // https://bugs.eclipse.org/bugs/show_bug.cgi?id=534694 or a similar bug:
      57             :     // return assertAbout(SerializedClassSubject::new).that(actual);
      58           4 :     Subject.Factory<SerializedClassSubject, Class<?>> factory =
      59           4 :         (m, a) -> new SerializedClassSubject(m, a);
      60           4 :     return assertAbout(factory).that(actual);
      61             :   }
      62             : 
      63             :   private final Class<?> clazz;
      64             : 
      65             :   private SerializedClassSubject(FailureMetadata metadata, Class<?> clazz) {
      66           4 :     super(metadata, clazz);
      67           4 :     this.clazz = clazz;
      68           4 :   }
      69             : 
      70             :   public void isAbstract() {
      71           3 :     isNotNull();
      72           3 :     if (!Modifier.isAbstract(clazz.getModifiers())) {
      73           0 :       failWithActual(simpleFact("expected class to be abstract"));
      74             :     }
      75           3 :   }
      76             : 
      77             :   public void isConcrete() {
      78           3 :     isNotNull();
      79           3 :     if (Modifier.isAbstract(clazz.getModifiers())) {
      80           0 :       failWithActual(simpleFact("expected class to be concrete"));
      81             :     }
      82           3 :   }
      83             : 
      84             :   public void hasFields(Map<String, Type> expectedFields) {
      85           3 :     isConcrete();
      86           3 :     check("fields()")
      87           3 :         .that(
      88           3 :             FieldUtils.getAllFieldsList(clazz).stream()
      89           3 :                 .filter(f -> !Modifier.isStatic(f.getModifiers()))
      90           3 :                 .collect(toImmutableMap(Field::getName, Field::getGenericType)))
      91           3 :         .containsExactlyEntriesIn(expectedFields);
      92           3 :   }
      93             : 
      94             :   public void hasAutoValueMethods(Map<String, Type> expectedMethods) {
      95             :     // Would be nice if we could check clazz is an @AutoValue, but the retention is not RUNTIME.
      96           3 :     isAbstract();
      97           3 :     check("noArgumentAbstractMethods()")
      98           3 :         .that(
      99           3 :             Arrays.stream(clazz.getDeclaredMethods())
     100           3 :                 .filter(m -> !Modifier.isStatic(m.getModifiers()))
     101           3 :                 .filter(m -> Modifier.isAbstract(m.getModifiers()))
     102           3 :                 .filter(m -> m.getParameters().length == 0)
     103           3 :                 .collect(toImmutableMap(Method::getName, Method::getGenericReturnType)))
     104           3 :         .isEqualTo(expectedMethods);
     105           3 :   }
     106             : 
     107             :   public void extendsClass(Type superclassType) {
     108           1 :     isNotNull();
     109           1 :     check("getGenericSuperclass()").that(clazz.getGenericSuperclass()).isEqualTo(superclassType);
     110           1 :   }
     111             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750