Class ClassWrapper<T>

java.lang.Object
io.github.valentinherrmann.levenshtein.Wrapper<T>
io.github.valentinherrmann.levenshtein.ClassWrapper<T>
Type Parameters:
T - the type of the class being wrapped
Direct Known Subclasses:
GenericClassWrapper

public abstract class ClassWrapper<T> extends Wrapper<T>
Wrapper for classes that verifies their existence, name, modifiers, superclass, and interfaces using Levenshtein distance for fuzzy matching. Provides methods to instantiate objects and retrieve attribute, method, and constructor wrappers.

Abstract classes and interfaces are instantiated through a Byte Buddy subclass; concrete classes always through their own constructor (so final classes and getClass()-based equals work).

  • Field Details

    • obj

      protected T obj
      A default instance of the class for testing purposes.
  • Constructor Details

    • ClassWrapper

      public ClassWrapper(String expectedName, String expectedPackage, ClassWrapper<?> superClassWrapper, ClassWrapper<?>[] interfaceWrappers, String... modifiers)
      Constructs a new ClassWrapper with superclass and interface expectations.
      Parameters:
      expectedName - the expected (simple or binary nested, e.g. Outer$Inner) name of the class
      expectedPackage - the expected package name ("" for the default package)
      superClassWrapper - the expected superclass wrapper (null if extends Object)
      interfaceWrappers - the expected interface wrappers (null or empty if none)
      modifiers - the expected modifiers (e.g., "public", "abstract")
    • ClassWrapper

      protected ClassWrapper(Class<T> knownClass)
      Constructs a wrapper for a class that is already available via reflection.
      Parameters:
      knownClass - the class to wrap
    • ClassWrapper

      public ClassWrapper(String expectedName, String expectedPackage, String... modifiers)
      Constructs a new ClassWrapper without superclass or interface expectations.
      Parameters:
      expectedName - the expected name of the class
      expectedPackage - the expected package name
      modifiers - the expected modifiers (e.g., "public", "abstract")
  • Method Details

    • reference

      public static <V> ClassWrapper<V> reference(String expectedName, String expectedPackage)
      Creates a wrapper that only refers to a student class by its expected name, for use as the type of an attribute, a return value or a parameter, e.g. new AttributeWrapper<>(this, "drummer", ClassWrapper.reference("Drummer", "band"), "private"). This works even if the full wrapper of that class is not created yet (classes that refer to each other). The class is looked up like any other class (deviating names are accepted, see LevenshteinSettings.getClassNameDeviationThreshold()), but only when the member using it is checked. The reference has no members and no default instance; it is equal to the full wrapper of the same class.
      Type Parameters:
      V - the type of the class
      Parameters:
      expectedName - the expected (simple or binary nested) name of the class
      expectedPackage - the expected package name ("" for the default package)
      Returns:
      the reference
    • verifyExistence

      public void verifyExistence(boolean throwAssertion)
      Description copied from class: Wrapper
      Verifies that the wrapped element exists in the actual class. Subclasses must implement this to perform element-specific existence checks.
      Specified by:
      verifyExistence in class Wrapper<T>
      Parameters:
      throwAssertion - if true, throws an AssertJ assertion failure when the element is missing. Usually set to true for behavioral tests, and false for structural tests to allow further deviation analysis.
    • implicitModifiers

      protected int implicitModifiers()
      Interfaces are implicitly abstract, enums final (or abstract if a constant has a body), records final; nested interfaces, enums and records as well as types declared in an interface are implicitly static.
      Overrides:
      implicitModifiers in class Wrapper<T>
      Returns:
      the mask of implicit modifier bits
    • usability

      protected WrapperProperty.Existence usability()
      A class can be used in behavioral tests as soon as it was found, even if e.g. its superclass deviates.
      Overrides:
      usability in class Wrapper<T>
      Returns:
      the existence state relevant for using the element
    • getClazz

      public Class<T> getClazz()
      Retrieves the actual class found via reflection. Verifies existence without throwing an assertion.
      Returns:
      the actual class, or null if not found
    • getExpectedPackage

      public String getExpectedPackage()
      Returns:
      the expected package of the class
    • findWithDeviation

      protected void findWithDeviation()
      Searches for the class in the expected package, using LevenshteinSettings.getClassNameDeviationThreshold() for deviating names.
      Specified by:
      findWithDeviation in class Wrapper<T>
    • verifySuperClass

      public void verifySuperClass()
      Verifies that the actual superclass matches or is compatible with the expected superclass. Updates the superClassWrapper's existence state based on exact match, indirect inheritance, or mismatch. The implicit superclass of a record (Record) or an enum (Enum) counts as no superclass, like Object.
    • verifyInterfaces

      public void verifyInterfaces()
      Verifies that the actual interfaces match the expected interfaces. Expected interfaces are compared by the classes their wrappers resolved (so a deviating interface name still counts). An interface that is only inherited indirectly, or additional interfaces, are deviations. Updates the interfaceWrappers' existence state accordingly.
    • getObj

      public abstract Object getObj(boolean forceNew)
      Returns the default instance of the class represented by this ClassWrapper. Concrete wrapper subclasses implement it by calling getObj(boolean, ConstructorWrapper, Object...) with a default constructor and arguments. Abstract classes and interfaces are instantiated through a Byte Buddy subclass, concrete classes through their own constructor.
      Parameters:
      forceNew - whether to force creation of a new instance instead of reusing the cached one
      Returns:
      an instance of the wrapped class
    • getObj

      public Object getObj()
      Returns the (possibly cached) default instance of the class.
      Returns:
      an instance of the wrapped class which might have been cached
    • newObj

      public Object newObj()
      Creates a new default instance of the class (never cached from earlier calls).
      Returns:
      a new instance of the wrapped class
    • getObj

      public T getObj(boolean forceNew, ConstructorWrapper<?> constructorWrapper, Object... constructorArgs)
      Returns an instance of the class created with a specific constructor. A cached instance is only reused if it was created with the same constructor and arguments (or set via setCachedObj(Object)) and forceNew is false. Abstract classes and interfaces are instantiated through a Byte Buddy subclass.

      Pass a single array argument as (Object) array: otherwise Java spreads an object array (e.g. String[]) into several arguments. A wrong number of arguments is reported as IllegalArgumentException.

      Parameters:
      forceNew - whether to force creation of a new instance
      constructorWrapper - the constructor wrapper to use for instantiation (null: no-argument constructor)
      constructorArgs - the arguments to pass to the constructor
      Returns:
      an instance of the wrapped class
    • setCachedObj

      public void setCachedObj(Object newObj)
      Sets the cached default instance, e.g. to an object created in a test. It is returned by getObj(boolean, ConstructorWrapper, Object...) until forceNew is requested.
      Parameters:
      newObj - the instance to cache (null clears the cache)
    • getDynamicSubclassObj

      public Object getDynamicSubclassObj(ConstructorWrapper<?> constructorWrapper, Object... constructorArgs)
      Creates a dynamic subclass instance using ByteBuddy for abstract classes and interfaces. This allows testing abstract classes by creating concrete implementations at runtime. Abstract methods of the instance throw AbstractMethodError when called.

      Pass a single array argument as (Object) array: otherwise Java spreads an object array into several arguments. A wrong number of arguments is reported as IllegalArgumentException.

      Parameters:
      constructorWrapper - the constructor to imitate (null for the no-argument constructor)
      constructorArgs - the arguments to pass to the constructor
      Returns:
      a new instance of the dynamic subclass
    • mock

      public Mock<T> mock()
      Starts a Mock of the class: an object whose methods the test can replace and whose calls are recorded, created without running a constructor of the class. Use it to keep tests independent, e.g. when the method under test calls another student method, or needs an object of another student class.
      Mock<?> guitarist = guitaristWrapper.mock()
              .override(guitaristWrapper.calculateLoudness, args -> (int) args[0] * 7);
      Object g = guitarist.obj();
      
      A missing class fails with the usual message; a final class, record, enum or sealed class fails with a readable message.
      Returns:
      a new mock
      See Also:
    • newObjOverriding

      public Object newObjOverriding(Map<MethodWrapper<?,?>, Function<Object[],Object>> overrides)
      Creates an object of a subclass of the wrapped class in which each given method returns the result of its function. No constructor of the class runs, so attributes keep their default values. Methods that are not overridden run the student's code. Same as mock().override(...)...obj(); use mock() to check the calls as well.
      Parameters:
      overrides - the methods to override (of this class, its superclasses or interfaces) and their functions, which get the arguments and return the result
      Returns:
      the new object
      See Also:
    • newObjWithoutConstructor

      public Object newObjWithoutConstructor()
      Creates an object of the wrapped class without running any of its constructors (attributes keep their default values 0, false, null; field initializers do not run either). A missing or broken constructor then cannot fail the test. Concrete classes (also final ones) are instantiated directly; abstract classes and interfaces through the subclass of a Mock without overrides. The cached default instance (getObj()) is not changed.
      Returns:
      the new object
    • getAttributeWrappers

      public List<Wrapper<T>> getAttributeWrappers()
      Retrieves all AttributeWrapper fields defined in this ClassWrapper subclass. Uses reflection on the wrapper (not on student code) to find all fields of type AttributeWrapper.
      Returns:
      a list of attribute wrappers for this class
    • getMethodWrappers

      public List<Wrapper<T>> getMethodWrappers()
      Retrieves all MethodWrapper fields defined in this ClassWrapper subclass.
      Returns:
      a list of method wrappers for this class
    • getConstructorWrappers

      public List<Wrapper<T>> getConstructorWrappers()
      Retrieves all ConstructorWrapper fields defined in this ClassWrapper subclass.
      Returns:
      a list of constructor wrappers for this class
    • parseExistence

      protected void parseExistence()
      Description copied from class: Wrapper
      Parses and updates the existence state based on all properties. Subclasses must implement this to define how to aggregate property states.
      Specified by:
      parseExistence in class Wrapper<T>
    • expectedToString

      public String expectedToString()
      Description copied from class: Wrapper
      Returns a string representation of the expected element signature. Subclasses must implement this to provide element-specific formatting.
      Specified by:
      expectedToString in class Wrapper<T>
      Returns:
      a string showing the expected element details
    • actualToString

      public String actualToString()
      Description copied from class: Wrapper
      Returns a string representation of the actual element signature found. Subclasses must implement this to provide element-specific formatting.
      Specified by:
      actualToString in class Wrapper<T>
      Returns:
      a string showing the actual element details
    • equals

      public boolean equals(Object other)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Description copied from class: Wrapper

      Returns a string representation of this wrapped element showing its existence state. The returned string is formatted to be shown to users, indicating whether the element matches exactly, deviates, is missing, or is unchecked. Reuses Wrapper.expectedToString() and Wrapper.actualToString() for representing the two states.

      For WrapperProperty.Existence.EXACT matches, shows expected (= actual) values.

      For WrapperProperty.Existence.DEVIATES, shows both expected and actual values.

      For WrapperProperty.Existence.MISSING, shows what was expected.

      Overrides:
      toString in class Wrapper<T>
      Returns:
      a formatted string describing the element's state
    • testGetter

      public void testGetter(AttributeWrapper<?,?> attribute, MethodWrapper<?,?> getter)
      Tests that a getter method returns the value of an attribute. Same as testGettersSetters(AttributeWrapper, MethodWrapper[]) with only a getter.
      Parameters:
      attribute - the attribute wrapper to test
      getter - the getter method wrapper to test
    • testGettersSetters

      public void testGettersSetters(AttributeWrapper<?,?> attribute, MethodWrapper<?,?>... gettersSetters)
      Tests the getter and/or setter of an attribute without conditions, i.e. the setter stores every value and the getter returns the stored value. The role of each method wrapper follows from its parameters (none: getter, one: setter). With generated sample values (at least five per type), up to three subtests run, each on a new instance: setter -> attribute, attribute -> getter (values injected via reflection) and setter -> getter. If one fails, the test fails with a message describing all three. The cached default instance (getObj()) is not changed.
      Parameters:
      attribute - the attribute the getter and setter belong to
      gettersSetters - a getter (no parameters) and/or a setter (one parameter)
      Throws:
      IllegalArgumentException - if the method wrappers are neither one getter nor one setter
    • testGettersSetters

      public <V> void testGettersSetters(AttributeWrapper<?,?> attribute, Predicate<V> setterCondition, MethodWrapper<?,?>... gettersSetters)
      Like testGettersSetters(AttributeWrapper, MethodWrapper[]), but the setter must only store values for which setterCondition holds. For other values it must keep the old value or throw an exception. A condition that throws itself (e.g. a NullPointerException for null) marks the value invalid.
      Type Parameters:
      V - the type of values the condition is written for
      Parameters:
      attribute - the attribute the getter and setter belong to
      setterCondition - the condition for values the setter accepts, e.g. (Double p) -> p >= 0
      gettersSetters - a setter (one parameter) and optionally a getter (no parameters)
    • testGettersSetters

      public <V> void testGettersSetters(AttributeWrapper<?,?> attribute, List<V> sampleValues, MethodWrapper<?,?>... gettersSetters)
      Like testGettersSetters(AttributeWrapper, MethodWrapper[]), but with own sample values instead of generated ones (required for types without generated values, e.g. own classes).
      Type Parameters:
      V - the type of the sample values
      Parameters:
      attribute - the attribute the getter and setter belong to
      sampleValues - the values to set (use several different ones to catch hard-coded values)
      gettersSetters - a getter (no parameters) and/or a setter (one parameter)
    • testGettersSetters

      public <V> void testGettersSetters(AttributeWrapper<?,?> attribute, List<V> sampleValues, Predicate<V> setterCondition, MethodWrapper<?,?>... gettersSetters)
      Type Parameters:
      V - the type of the sample values
      Parameters:
      attribute - the attribute the getter and setter belong to
      sampleValues - the values to set, valid and invalid ones
      setterCondition - the condition for values the setter accepts
      gettersSetters - a setter (one parameter) and optionally a getter (no parameters)