Skip to content

Commit

Permalink
Merge pull request #36 from ls1intum/chore/add-tests
Browse files Browse the repository at this point in the history
`Chore`: Add unit tests for architectural and AOP analysis
  • Loading branch information
MarkusPaulsen authored Dec 6, 2024
2 parents e1910f7 + 8e21062 commit 0c8fa61
Show file tree
Hide file tree
Showing 28 changed files with 1,020 additions and 24 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
<artifactId>junit-platform-launcher</artifactId>
<version>${junit-platform-version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<!-- Logging framework -->
<dependency>
<groupId>ch.qos.logback</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class FileHandlerConstants {
public static final Path ARCHUNIT_COMMAND_EXECUTION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "command-execution-methods.txt");
public static final Path ARCHUNIT_THREAD_MANIPULATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "thread-manipulation-methods.txt");
public static final Path ARCHUNIT_SERIALIZATION_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "serializable-methods.txt");
public static final Path ARCHUNIT_CLASSLOADER_METHODS = FileTools.resolveOnResources("templates", "architecture" , "java", "archunit", "methods", "classloader-methods.txt");
//</editor-fold>

//<editor-fold desc="WALA Methods">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package de.tum.cit.ase.ares.api.architecture.java.archunit;

//<editor-fold desc="Imports">

import com.tngtech.archunit.core.domain.JavaClasses;
import de.tum.cit.ase.ares.api.architecture.java.JavaArchitecturalTestCaseSupported;
import de.tum.cit.ase.ares.api.policy.SecurityPolicy.PackagePermission;
Expand Down Expand Up @@ -51,7 +50,6 @@ private JavaArchUnitSecurityTestCase(@Nonnull Builder builder) {
this.javaArchitectureTestCaseSupported = builder.javaArchitectureTestCaseSupported;
this.allowedPackages = builder.allowedPackages;
}

//</editor-fold>

//<editor-fold desc="Tool methods">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,11 @@ public boolean test(JavaClass javaClass) {
FileHandlerConstants.ARCHUNIT_SERIALIZATION_METHODS
);
//</editor-fold>

//<editor-fold desc="ClassLoader related rule">
public static final ArchRule NO_CLASSES_SHOULD_USE_CLASSLOADERS = createNoClassShouldHaveMethodRule(
"uses ClassLoaders",
FileHandlerConstants.ARCHUNIT_CLASSLOADER_METHODS
);
//</editor-fold>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
java.lang.ClassLoader
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ java.lang.Class.getDeclaringClass()
java.lang.ClassLoader.getParent()
java.lang.ClassLoader.getSystemClassLoader()
java.lang.ClassLoader.getPlatformClassLoader()
java.lang.ClassLoader
java.lang.Module.addReads(java.lang.Module)
java.lang.Module.getResourceAsStream(java.lang.String)
java.lang.Module.addExports(java.lang.String, java.lang.Module)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ java.util.concurrent.DelayQueue.take()
java.util.concurrent.ExecutorService.close()
java.util.concurrent.ForkJoinPool.managedBlock(java.util.concurrent.ForkJoinPool$ManagedBlocker)
java.util.concurrent.ForkJoinPool.close()
java.util.concurrent.ForkJoinPool
java.util.concurrent.ForkJoinTask.inForkJoinPool()
java.util.concurrent.ForkJoinTask.fork()
java.util.concurrent.ForkJoinTask.getPool()
Expand Down
124 changes: 124 additions & 0 deletions src/test/java/de/tum/cit/ase/ares/api/aop/JavaAOPModeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package de.tum.cit.ase.ares.api.aop;

import de.tum.cit.ase.ares.api.aop.java.JavaAOPMode;
import de.tum.cit.ase.ares.api.util.FileTools;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class JavaAOPModeTest {

private static final int SETUP_OPTIONS_COUNT = 2;
/**
* Expected number of files to copy for instrumentation mode.
* This includes:
* - [List the specific files or types of files expected]
*/
private static final int INSTRUMENTATION_FILES_COUNT = 13;
private static final int INSTRUMENTATION_VALUES_COUNT = 12;

/**
* Expected number of files to copy for AspectJ mode.
* This includes:
* - [List the specific files or types of files expected]
*/
private static final int ASPECTJ_FILES_COUNT = 2;
private static final int ASPECTJ_VALUES_COUNT = 2;

private static String TEST_PACKAGE = "com.example";
private static String TEST_MAIN_CLASS = "MainClass";
private static String[] EXPECTED_ARRAY = {"mocked", "array"};

private JavaAOPMode instrumentationMode;
private JavaAOPMode aspectjMode;

@BeforeEach
void setUp() {
instrumentationMode = JavaAOPMode.INSTRUMENTATION;
aspectjMode = JavaAOPMode.ASPECTJ;
}

@Test
void testEnumValues() {
JavaAOPMode[] modes = JavaAOPMode.values();
assertEquals(SETUP_OPTIONS_COUNT, modes.length);
assertTrue(Arrays.asList(modes).contains(JavaAOPMode.INSTRUMENTATION));
assertTrue(Arrays.asList(modes).contains(JavaAOPMode.ASPECTJ));
}

@Test
void testFilesToCopy_InstrumentationMode() {
try (MockedStatic<FileTools> mockedFileTools = mockStatic(FileTools.class)) {
mockedFileTools
.when(() -> FileTools.resolveOnResources(any(String[].class)))
.thenReturn(mock(Path.class));
instrumentationMode.filesToCopy();
mockedFileTools
.verify(() -> FileTools.resolveOnResources(any(String[].class)),
times(INSTRUMENTATION_FILES_COUNT)
);
}
}

@Test
void testFilesToCopy_AspectJMode() {
try (MockedStatic<FileTools> mockedFileTools = mockStatic(FileTools.class)) {
mockedFileTools
.when(() -> FileTools.resolveOnResources(any(String[].class)))
.thenReturn(mock(Path.class));
aspectjMode.filesToCopy();
mockedFileTools
.verify(() -> FileTools.resolveOnResources(any(String[].class)),
times(ASPECTJ_FILES_COUNT)
);
}
}

@Test
void testFileValues_InstrumentationMode() {
try (MockedStatic<FileTools> mockedFileTools = mockStatic(FileTools.class)) {
mockedFileTools
.when(() -> FileTools.generatePackageNameArray(anyString(), anyInt()))
.thenReturn(EXPECTED_ARRAY);
instrumentationMode.fileValues(TEST_PACKAGE, TEST_MAIN_CLASS);
mockedFileTools
.verify(() -> FileTools.generatePackageNameArray(anyString(), anyInt()),
times(INSTRUMENTATION_VALUES_COUNT)
);
}
}

@Test
void testFileValues_AspectJMode() {
try (MockedStatic<FileTools> mockedFileTools = mockStatic(FileTools.class)) {
mockedFileTools
.when(() -> FileTools.generatePackageNameArray(anyString(), anyInt()))
.thenReturn(EXPECTED_ARRAY);
aspectjMode.fileValues(TEST_PACKAGE, TEST_MAIN_CLASS);
mockedFileTools
.verify(() -> FileTools.generatePackageNameArray(anyString(), anyInt()),
times(ASPECTJ_VALUES_COUNT)
);
}
}

@Test
void testReset() {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> settingsClass = Class.forName("de.tum.cit.ase.ares.api.aop.java.JavaSecurityTestCaseSettings", true, classLoader);
Method resetMethod = settingsClass.getDeclaredMethod("reset");
resetMethod.setAccessible(true);
resetMethod.invoke(null);
} catch (Exception e) {
fail("Exception should not have been thrown: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package de.tum.cit.ase.ares.api.aop;

import de.tum.cit.ase.ares.api.aop.java.JavaSecurityTestCaseSettings;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.junit.jupiter.api.Assertions.*;

class JavaSecurityTestCaseSettingsTest {

@Test
void testConstructorThrowsException() {
try {
Constructor<JavaSecurityTestCaseSettings> constructor = JavaSecurityTestCaseSettings.class.getDeclaredConstructor();
constructor.setAccessible(true);
constructor.newInstance();
fail("Expected SecurityException to be thrown");
} catch (InvocationTargetException e) {
assertInstanceOf(SecurityException.class, e.getCause());
assertEquals("Ares Security Error (Reason: Ares-Code; Stage: Creation): JavaSecurityTestCaseSettings is a utility class and should not be instantiated.", e.getCause().getMessage());
} catch (Exception e) {
fail("Unexpected exception: " + e);
}
}

@Test
void testResetMethod() {
try {
Field aopModeField = JavaSecurityTestCaseSettings.class.getDeclaredField("aopMode");
Field allowedListedClassesField = JavaSecurityTestCaseSettings.class.getDeclaredField("allowedListedClasses");
Field portsAllowedToBeConnectedToField = JavaSecurityTestCaseSettings.class.getDeclaredField("portsAllowedToBeConnectedTo");

aopModeField.setAccessible(true);
allowedListedClassesField.setAccessible(true);
portsAllowedToBeConnectedToField.setAccessible(true);

aopModeField.set(null, "test");
allowedListedClassesField.set(null, new String[]{"testClass"});
portsAllowedToBeConnectedToField.set(null, new int[]{8080});

Method resetMethod = JavaSecurityTestCaseSettings.class.getDeclaredMethod("reset");
resetMethod.setAccessible(true);
resetMethod.invoke(null);

assertNull(aopModeField.get(null));
assertNull(allowedListedClassesField.get(null));
assertNull(portsAllowedToBeConnectedToField.get(null));

Field pathsAllowedToBeReadField = JavaSecurityTestCaseSettings.class.getDeclaredField("pathsAllowedToBeRead");
pathsAllowedToBeReadField.setAccessible(true);
assertNull(pathsAllowedToBeReadField.get(null));

Field pathsAllowedToBeOverwrittenField = JavaSecurityTestCaseSettings.class.getDeclaredField("pathsAllowedToBeOverwritten");
pathsAllowedToBeOverwrittenField.setAccessible(true);
assertNull(pathsAllowedToBeOverwrittenField.get(null));

} catch (Exception e) {
fail("Unexpected exception: " + e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package de.tum.cit.ase.ares.api.aop;

import de.tum.cit.ase.ares.api.aop.java.JavaSecurityTestCase;
import de.tum.cit.ase.ares.api.aop.java.JavaSecurityTestCaseSupported;
import de.tum.cit.ase.ares.api.policy.SecurityPolicy.ResourceAccesses;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class JavaSecurityTestCaseTest {

private JavaSecurityTestCase javaSecurityTestCase;
private ResourceAccesses resourceAccesses;

@BeforeEach
void setUp() {
JavaSecurityTestCaseSupported supported = JavaSecurityTestCaseSupported.FILESYSTEM_INTERACTION;
resourceAccesses = mock(ResourceAccesses.class);
javaSecurityTestCase = new JavaSecurityTestCase(supported, resourceAccesses);
}

@Test
void testWriteAOPSecurityTestCase() {
String result = javaSecurityTestCase.writeAOPSecurityTestCase("INSTRUMENTATION");
assertEquals("", result);
}

@Test
void testWriteAOPSecurityTestCaseFile() {
List<String> allowedListedClasses = List.of("TestClass");
List<JavaSecurityTestCase> javaSecurityTestCases = List.of(javaSecurityTestCase);

String result = JavaSecurityTestCase.writeAOPSecurityTestCaseFile(
"INSTRUMENTATION",
"de.tum.cit",
allowedListedClasses,
javaSecurityTestCases
);

assertTrue(result.contains("private static String aopMode"));
assertTrue(result.contains("private static String restrictedPackage"));
assertTrue(result.contains("private static String[] allowedListedClasses"));
}

@Test
void testExecuteAOPSecurityTestCase() {
try (MockedStatic<JavaSecurityTestCase> mockedStatic = mockStatic(JavaSecurityTestCase.class)) {
javaSecurityTestCase.executeAOPSecurityTestCase("INSTRUMENTATION");
mockedStatic.verify(() -> JavaSecurityTestCase.setJavaAdviceSettingValue(anyString(), any(), eq("INSTRUMENTATION")), atLeastOnce());
}
}

@Test
void testGetPermittedFilePaths() throws Exception {
Method method = JavaSecurityTestCase.class.getDeclaredMethod("getPermittedFilePaths", String.class);
method.setAccessible(true);
List<String> filePaths = (List<String>) method.invoke(javaSecurityTestCase, "read");
assertEquals(filePaths.size(), 0);
}

@Test
void testGenerateAdviceSettingValue() throws Exception {
Method method = JavaSecurityTestCase.class.getDeclaredMethod("generateAdviceSettingValue", String.class, String.class, Object.class);
method.setAccessible(true);
String result = (String) method.invoke(null, "String", "testAdvice", "testValue");
assertEquals("private static String testAdvice = \"testValue\";\n", result);
result = (String) method.invoke(null, "String[]", "testAdviceArray", List.of("value1", "value2"));
assertEquals("private static String[] testAdviceArray = new String[] {\"value1\", \"value2\"};\n", result);
InvocationTargetException thrown = assertThrows(InvocationTargetException.class, () -> {
method.invoke(null, "UnknownType", "testAdvice", "value");
});
assertEquals(SecurityException.class, thrown.getCause().getClass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package de.tum.cit.ase.ares.api.aop.instrumentation.advice;

import de.tum.cit.ase.ares.api.aop.java.instrumentation.advice.JavaInstrumentationAdviceToolbox;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import java.lang.reflect.Method;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class JavaInstrumentationAdviceToolboxTest {


@Test
void testCheckFileSystemInteraction_AllowedInteraction() {
try (MockedStatic<JavaInstrumentationAdviceToolbox> mockedToolbox = mockStatic(JavaInstrumentationAdviceToolbox.class)) {
Method getValueFromSettings = JavaInstrumentationAdviceToolbox.class.getDeclaredMethod("getValueFromSettings", String.class);
getValueFromSettings.setAccessible(true);

mockedToolbox.when(() -> getValueFromSettings.invoke(null, "aopMode")).thenReturn("INSTRUMENTATION");
mockedToolbox.when(() -> getValueFromSettings.invoke(null, "restrictedPackage")).thenReturn("de.tum.cit.ase");
mockedToolbox.when(() -> getValueFromSettings.invoke(null, "allowedListedClasses")).thenReturn(new String[]{"de.tum.cit.ase.safe"});
mockedToolbox.when(() -> getValueFromSettings.invoke(null, "pathsAllowedToBeRead")).thenReturn(new String[]{"/allowed/path"});

assertDoesNotThrow(() -> JavaInstrumentationAdviceToolbox.checkFileSystemInteraction(
"read",
"de.tum.cit.ase.safe.FileReader",
"readFile",
"(Ljava/lang/String;)V",
null,
new Object[]{"/allowed/path"}
));
} catch (Exception e) {
fail("Exception should not have been thrown: " + e.getMessage());
}
}

@Test
void testLocalizeFallback() {
String key = "security.advice.test.key";
String result = JavaInstrumentationAdviceToolbox.localize(key, "arg1", "arg2");
key = "!security.advice.test.key!";
assertEquals(key, result);
}
}
Loading

0 comments on commit 0c8fa61

Please sign in to comment.