Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# CLAUDE.md - ModifiableVariable Project Guide

## Build Commands

- Build: `mvn clean install`
- Run all tests: `mvn test`
- Run single test: `mvn test -Dtest=ClassName#methodName` (e.g., `mvn test -Dtest=ModifiableVariableTest#testRandomBigIntegerModification`)
- Run with code coverage: `mvn clean install -P coverage`
- Format code: `mvn spotless:apply`

## Code Style

- Uses Google Java Style (AOSP variant) for formatting
- Indentation: 4 spaces (no tabs)
- Line endings: GIT_ATTRIBUTES
- License header required on all files

## Imports

- Unused imports should be removed
- Import order is managed by spotless

## Type System

- Java 21 is required
- Generic types should be properly specified (e.g., `ModifiableVariable<E>`)
- JAXB annotations used for XML serialization

## Naming Conventions

- CamelCase for class names
- camelCase for methods and variables
- Classes implementing modifications follow pattern: `[Type][Operation]Modification`
- Factory classes follow pattern: `[Type]ModificationFactory`

## Error Handling

- Proper exception handling with appropriate exception types
- FileConfigurationException for configuration-related errors

Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlTransient;
import java.io.Serializable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class VariableModification<E> {
public abstract class VariableModification<E> implements Serializable {

protected static final Logger LOGGER = LogManager.getLogger(VariableModification.class);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* ModifiableVariable - A Variable Concept for Runtime Modifications
*
* Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0
*/
package de.rub.nds.modifiablevariable;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;

import org.junit.jupiter.api.Test;

public class FileConfigurationExceptionTest {

@Test
public void testDefaultConstructor() {
FileConfigurationException exception = new FileConfigurationException();

assertNull(exception.getMessage());
assertNull(exception.getCause());
}

@Test
public void testConstructorWithException() {
IllegalArgumentException cause = new IllegalArgumentException("Test cause");
FileConfigurationException exception = new FileConfigurationException(cause);

assertNotNull(exception.getMessage());
assertSame(cause, exception.getCause());
}

@Test
public void testConstructorWithMessageAndException() {
String message = "Test error message";
IllegalArgumentException cause = new IllegalArgumentException("Test cause");
FileConfigurationException exception = new FileConfigurationException(message, cause);

assertEquals(message, exception.getMessage());
assertSame(cause, exception.getCause());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* ModifiableVariable - A Variable Concept for Runtime Modifications
*
* Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0
*/
package de.rub.nds.modifiablevariable;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import de.rub.nds.modifiablevariable.integer.ModifiableInteger;
import de.rub.nds.modifiablevariable.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.List;
import org.junit.jupiter.api.Test;

public class HoldsModifiableVariableTest {

// Test class with HoldsModifiableVariable annotations
private static class TestClass {
@HoldsModifiableVariable private ModifiableVariableHolder holder;

@HoldsModifiableVariable private List<ModifiableInteger> integerList;

// No annotation
private ModifiableVariableHolder regularHolder;
}

@Test
public void testAnnotationPresence() throws NoSuchFieldException {
Field holderField = TestClass.class.getDeclaredField("holder");
HoldsModifiableVariable annotation =
holderField.getAnnotation(HoldsModifiableVariable.class);

assertNotNull(annotation);
}

@Test
public void testListFieldAnnotation() throws NoSuchFieldException {
Field listField = TestClass.class.getDeclaredField("integerList");
HoldsModifiableVariable annotation = listField.getAnnotation(HoldsModifiableVariable.class);

assertNotNull(annotation);
}

@Test
public void testNoAnnotation() throws NoSuchFieldException {
Field regularField = TestClass.class.getDeclaredField("regularHolder");
HoldsModifiableVariable annotation =
regularField.getAnnotation(HoldsModifiableVariable.class);

assertNull(annotation);
}

@Test
public void testFindAnnotatedFields() {
// Use ReflectionHelper to find fields with the annotation
List<Field> fields = ReflectionHelper.getFieldsUpTo(TestClass.class, Object.class, null);

int annotatedCount = 0;
for (Field field : fields) {
if (field.isAnnotationPresent(HoldsModifiableVariable.class)) {
annotatedCount++;
}
}

// We should find 2 annotated fields: holder and integerList
assertEquals(2, annotatedCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* ModifiableVariable - A Variable Concept for Runtime Modifications
*
* Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0
*/
package de.rub.nds.modifiablevariable;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import de.rub.nds.modifiablevariable.bytearray.ModifiableByteArray;
import de.rub.nds.modifiablevariable.integer.ModifiableInteger;
import de.rub.nds.modifiablevariable.string.ModifiableString;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Random;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class ModifiableVariableHolderTest {

private TestModifiableVariableHolder holder;

// Simple implementation of ModifiableVariableHolder for testing
private static class TestModifiableVariableHolder extends ModifiableVariableHolder {
private ModifiableInteger intValue;
private ModifiableString stringValue;
private ModifiableByteArray byteArrayValue;
private byte[] regularByteArray;
private String regularString;
private NestedHolder nestedHolder;

public TestModifiableVariableHolder() {
intValue = new ModifiableInteger();
stringValue = new ModifiableString();
byteArrayValue = new ModifiableByteArray();
regularByteArray = new byte[] {0x01, 0x02, 0x03};
regularString = "Regular String";
nestedHolder = new NestedHolder();
}
}

private static class NestedHolder extends ModifiableVariableHolder {
private ModifiableInteger nestedInt;

public NestedHolder() {
nestedInt = new ModifiableInteger();
nestedInt.setOriginalValue(42);
}
}

@BeforeEach
public void setUp() {
holder = new TestModifiableVariableHolder();
holder.intValue.setOriginalValue(123);
holder.stringValue.setOriginalValue("Test String");
holder.byteArrayValue.setOriginalValue(new byte[] {0x04, 0x05, 0x06});
}

@Test
public void testGetAllModifiableVariableFields() {
List<Field> fields = holder.getAllModifiableVariableFields();

// Should find 3 fields: intValue, stringValue, byteArrayValue
assertEquals(3, fields.size());

// Verify field names
boolean foundIntValue = false;
boolean foundStringValue = false;
boolean foundByteArrayValue = false;

for (Field field : fields) {
switch (field.getName()) {
case "intValue":
foundIntValue = true;
break;
case "stringValue":
foundStringValue = true;
break;
case "byteArrayValue":
foundByteArrayValue = true;
break;
}
}

assertTrue(foundIntValue);
assertTrue(foundStringValue);
assertTrue(foundByteArrayValue);
}

@Test
public void testGetRandomModifiableVariableField() {
// Use a fixed seed for reproducible test
Random random = new Random(123);

Field field = holder.getRandomModifiableVariableField(random);
assertNotNull(field);
assertTrue(ModifiableVariable.class.isAssignableFrom(field.getType()));
}

@Test
public void testGetAllModifiableVariableHolders() {
List<ModifiableVariableHolder> holders = holder.getAllModifiableVariableHolders();

// Should only contain the holder itself by default
assertEquals(1, holders.size());
assertEquals(holder, holders.get(0));
}

@Test
public void testGetRandomModifiableVariableHolder() {
// Use a fixed seed for reproducible test
Random random = new Random(123);

ModifiableVariableHolder randomHolder = holder.getRandomModifiableVariableHolder(random);
assertNotNull(randomHolder);
assertEquals(holder, randomHolder); // Since only one holder in the list
}

@Test
public void testReset() {
// Apply a modification to test reset
VariableModification<Integer> modification =
new VariableModification<Integer>() {
@Override
protected Integer modifyImplementationHook(Integer input) {
return input + 100;
}

@Override
public VariableModification<Integer> createCopy() {
// stub
return null;
}
};

holder.intValue.setModifications(modification);
assertEquals(223, holder.intValue.getValue().intValue());

// Reset and check that modifications are removed
holder.reset();

// Original value should be null but modification should remain
assertNull(holder.intValue.getOriginalValue());
assertNotNull(holder.intValue.getModifications());
}

@Test
public void testGetExtendedString() {
String result = holder.getExtendedString();
System.out.println("Extended string content: " + result);

// The structure of the string can vary by platform/environment, so make minimal assertions
assertTrue(result.contains("TestModifiableVariableHolder"));

// Don't check specific integer value format
assertTrue(result.contains("intValue"));

// Check other values with less specific formatting requirements
assertTrue(result.contains("stringValue"));
assertTrue(result.contains("Test String"));

// We only know the bytes will be there somewhere in hex format
assertTrue(result.contains("04"));
assertTrue(result.contains("05"));
assertTrue(result.contains("06"));
assertTrue(result.contains("01"));
assertTrue(result.contains("02"));
assertTrue(result.contains("03"));

// Check for presence of other fields without exact formatting
assertTrue(result.contains("regularString"));
assertTrue(result.contains("Regular String"));
assertTrue(result.contains("nestedHolder"));
assertTrue(result.contains("NestedHolder"));
assertTrue(result.contains("nestedInt"));
}
}
Loading