Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhauled the end to end tests and added the test code from progpedia #1279

Merged
merged 22 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fa9e0ce
Overhauled the end to end tests and added the test code from progpedia.
TwoOfTwelve Sep 5, 2023
cc8d9fd
Added missing result files for end to end tests
TwoOfTwelve Sep 5, 2023
04f2e87
Merge branch 'develop' into feature/endToEndTests-Rework
dfuchss Sep 13, 2023
9428119
Fixed test results
TwoOfTwelve Sep 14, 2023
a4a7cca
Reduced epsilon value for end to end tests.
TwoOfTwelve Sep 27, 2023
3a496fe
Generate expected test values for the progpedia dataset.
tsaglam Oct 4, 2023
4190ccc
Merge branch 'develop' into feature/endToEndTests-Rework
tsaglam Oct 4, 2023
fd9d1b1
Added token output for end to end tests.
TwoOfTwelve Oct 10, 2023
6a2745b
Improved end to end test output.
TwoOfTwelve Oct 10, 2023
5407b18
Added strict order for submissions in comparison.
TwoOfTwelve Oct 10, 2023
f6a07ba
Fixed sonarcloud issues.
TwoOfTwelve Oct 10, 2023
659e7c7
Implemented timurs suggestions.
TwoOfTwelve Oct 25, 2023
7774c0c
Moved progpedia data into a zip file.
TwoOfTwelve Oct 31, 2023
cc6e49f
Fixed spotless issues concerning zips.
TwoOfTwelve Oct 31, 2023
58c62fc
spotless.
TwoOfTwelve Oct 31, 2023
bbf8e06
Merge branch 'develop' into feature/endToEndTests-Rework
TwoOfTwelve Oct 31, 2023
0f86c4b
Fixed sonarcloud issues.
TwoOfTwelve Oct 31, 2023
8d26824
Improved language in test descriptions.
TwoOfTwelve Oct 31, 2023
510aef3
Improved handling of io streams in end to end tests.
TwoOfTwelve Oct 31, 2023
038d8e3
Added logging when permissions cannot be set on temp directory.
TwoOfTwelve Nov 13, 2023
4667032
Spotless
TwoOfTwelve Nov 18, 2023
0c41141
Fixed sonarcloud issues.
TwoOfTwelve Nov 18, 2023
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
1 change: 0 additions & 1 deletion endtoend-testing/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
<groupId>de.jplag</groupId>
<artifactId>cli</artifactId>
<version>${revision}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.jplag</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@ private TestDirectoryConstants() {
}

/**
* Base path to the saved results
* Base path to the resources directory
*/
public static final Path BASE_PATH_TO_RESULT_JSON = Path.of("src", "test", "resources", "results");
public static final Path BASE_PATH_TO_RESOURCES = Path.of("src", "test", "resources");

/**
* Base path to the endToEnd testing resources
* Base path to the saved results
*/
public static final Path BASE_PATH_TO_LANGUAGE_RESOURCES = Path.of("src", "test", "resources", "languageTestFiles");
public static final Path BASE_PATH_TO_RESULT_JSON = BASE_PATH_TO_RESOURCES.resolve(Path.of("results"));

/**
* Create the complete path to the submission files. Here the temporary system path is extended with the
* "SUBMISSION_DIRECTORY_NAME", which is predefined in this class.
* Base path to the data set descriptors
*/
public static final Path TEMPORARY_SUBMISSION_DIRECTORY_NAME = Path.of("target", "testing-directory-submission");
public static final Path BASE_PATH_TO_DATA_SET_DESCRIPTORS = BASE_PATH_TO_RESOURCES.resolve(Path.of("dataSets"));
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
package de.jplag.endtoend.helper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
* Helper class to perform all necessary operations or functions on files or folders.
*/
public class FileHelper {
private static final int ZIP_THRESHOLD_ENTRIES = 100000;
private static final int ZIP_THRESHOLD_SIZE = 1000000000;
private static final double ZIP_THRESHOLD_RATIO = 10;
private static final String ZIP_BOMB_ERROR_MESSAGE = "Refusing to unzip file (%s), because it seems to be a fork bomb";

private FileHelper() {
// private constructor to prevent instantiation
Expand Down Expand Up @@ -52,4 +62,65 @@ public static void createFileIfItDoesNotExist(File file) throws IOException {
private static String createNewIOExceptionStringForFileOrFOlderCreation(File file) {
return "The file/folder at the location [" + file.toString() + "] could not be created!";
}
}

/**
* Unzips a given zip file into a given directory.
* @param zip The zip file to extract
* @param targetDirectory The target directory
* @throws IOException If io operations go wrong
*/
public static void unzip(File zip, File targetDirectory) throws IOException {
try (ZipFile zipFile = new ZipFile(zip)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
Fixed Show fixed Hide fixed

long totalSizeArchive = 0;
long totalEntriesArchive = 0;
Fixed Show fixed Hide fixed

while (entries.hasMoreElements()) {
totalEntriesArchive++;
Fixed Show fixed Hide fixed

ZipEntry entry = entries.nextElement();
File unzippedFile = new File(targetDirectory, entry.getName()).getCanonicalFile();

if (unzippedFile.getAbsolutePath().startsWith(targetDirectory.getAbsolutePath())) {
if (entry.isDirectory()) {
unzippedFile.mkdirs();
} else {
unzippedFile.getParentFile().mkdirs();
totalSizeArchive += extractZipElement(entry, zipFile, zip, unzippedFile);
}
}

if (totalSizeArchive > ZIP_THRESHOLD_SIZE) {
throw new IllegalStateException(String.format(ZIP_BOMB_ERROR_MESSAGE, zip.getAbsolutePath()));
}
if (totalEntriesArchive > ZIP_THRESHOLD_ENTRIES) {
throw new IllegalStateException(String.format(ZIP_BOMB_ERROR_MESSAGE, zip.getAbsolutePath()));
}
}
}
}

private static long extractZipElement(ZipEntry entry, ZipFile zipFile, File zip, File target) throws IOException {
long totalSizeEntry = 0;

try (InputStream inputStream = zipFile.getInputStream(entry)) {
try (OutputStream outputStream = new FileOutputStream(target)) {
byte[] buffer = new byte[2048];
int count;
while ((count = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, count);

totalSizeEntry += count;

double compressionRate = (double) totalSizeEntry / entry.getCompressedSize();
if (compressionRate > ZIP_THRESHOLD_RATIO) {
throw new IllegalStateException(String.format(ZIP_BOMB_ERROR_MESSAGE, zip.getAbsolutePath()));
}
}
}
}

return totalSizeEntry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.jplag.endtoend.helper;

import java.io.IOException;

import de.jplag.Language;
import de.jplag.cli.LanguageLoader;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

/**
* Deserialized a language from a json file
*/
public class LanguageDeserializer extends JsonDeserializer<Language> {
@Override
public Language deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String name = jsonParser.getText();
return LanguageLoader.getLanguage(name).orElseThrow(() -> new IllegalStateException(String.format("Language %s not found.", name)));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package de.jplag.endtoend.helper;

import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import de.jplag.JPlagComparison;
import de.jplag.Language;
import de.jplag.Submission;
import de.jplag.endtoend.constants.TestDirectoryConstants;

/**
* Helper class to perform all necessary additional functions for the endToEnd tests.
Expand All @@ -28,30 +25,8 @@ private TestSuiteHelper() {
* @return unique identifier for test case recognition
*/
public static String getTestIdentifier(JPlagComparison jPlagComparison) {
return List.of(jPlagComparison.firstSubmission(), jPlagComparison.secondSubmission()).stream().map(Submission::getRoot)
return Stream.of(jPlagComparison.firstSubmission(), jPlagComparison.secondSubmission()).map(Submission::getRoot)
.map(FileHelper::getFileNameWithoutFileExtension).sorted().collect(Collectors.joining("-"));

}

/**
* Returns the file pointing to the directory of the submissions for the given language and result json. The result
* json's name is expected to be equal to the test suite identifier.
* @param language is the language for the tests
* @param resultJSON is the json containing the expected values
* @return returns the directory of the submissions
*/
public static File getSubmissionDirectory(Language language, File resultJSON) {
return getSubmissionDirectory(language, FileHelper.getFileNameWithoutFileExtension(resultJSON));
}

/**
* Returns the file pointing to the directory of the submissions for the given language and test suite identifier as
* described in the Readme.md.
* @param language is the langauge for the tests
* @param testSuiteIdentifier is the test suite identifier of the tests
* @return returns the directory of the submissions
*/
public static File getSubmissionDirectory(Language language, String testSuiteIdentifier) {
return TestDirectoryConstants.BASE_PATH_TO_LANGUAGE_RESOURCES.resolve(language.getIdentifier()).resolve(testSuiteIdentifier).toFile();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package de.jplag.endtoend.helper;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.apache.commons.lang3.SystemUtils;

import de.jplag.endtoend.model.DataSet;

public class UnzipManager {
private final Map<DataSet, File> unzippedFiles;
private static UnzipManager instance;
private final Logger logger = Logger.getLogger("Unzip Manager");

private static synchronized UnzipManager getInstance() {
if (instance == null) {
instance = new UnzipManager();
}

return instance;
}

public static File unzipOrCache(DataSet dataSet, File zip) throws IOException {
return getInstance().unzipOrCacheInternal(dataSet, zip);
}

private UnzipManager() {
this.unzippedFiles = new HashMap<>();
}

private File unzipOrCacheInternal(DataSet dataSet, File zip) throws IOException {
if (!unzippedFiles.containsKey(dataSet)) {
File target;

if (SystemUtils.IS_OS_UNIX) {
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
target = Files.createTempDirectory(zip.getName(), attr).toFile();
} else {
target = Files.createTempDirectory(zip.getName()).toFile();
if (!(target.setReadable(true, true) && target.setWritable(true, true) && target.setExecutable(true, true))) {
logger.warning("Could not set permissions for temp directory (" + target.getAbsolutePath() + ").");
}
}

FileHelper.unzip(zip, target);
this.unzippedFiles.put(dataSet, target);
}

return this.unzippedFiles.get(dataSet);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package de.jplag.endtoend.model;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

/**
* Identifier for a comparison. The order of the names does not matter
* @param firstName The first name
* @param secondName The second name
*/
public record ComparisonIdentifier(String firstName, String secondName) {
private static final String INVALID_LINE_ERROR_MESSAGE = "Comparison identifier file (%s) has an invalid line: %s";

@Override
public boolean equals(Object o) {
if (!(o instanceof ComparisonIdentifier other)) {
return false;
}

return (firstName.equals(other.firstName) && secondName.equals(other.secondName))
|| (secondName.equals(other.firstName) && firstName.equals(other.secondName));
}

@Override
public int hashCode() {
return firstName.hashCode() + secondName.hashCode();
}

/**
* Loads the identifiers stored in a csv (semicolon separated) file.
* @param file The file to load
* @return The comparisons in the file
*/
public static Set<ComparisonIdentifier> loadIdentifiersFromFile(File file, String delimiter) {
try (Scanner scanner = new Scanner(file)) {
Set<ComparisonIdentifier> identifiers = new HashSet<>();
while (scanner.hasNextLine()) {
String[] parts = scanner.nextLine().split(delimiter);
if (parts.length != 2) {
throw new IllegalStateException(String.format(INVALID_LINE_ERROR_MESSAGE, file.getAbsolutePath(), String.join(delimiter, parts)));
}
identifiers.add(new ComparisonIdentifier(parts[0], parts[1]));
TwoOfTwelve marked this conversation as resolved.
Show resolved Hide resolved
}
return identifiers;
} catch (FileNotFoundException e) {
throw new IllegalStateException(String.format("Comparisons could not be loaded for %s.", file.getName()), e);
}
}

@Override
public String toString() {
return firstName + " - " + secondName;
}
}
Loading
Loading