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

Feature/allow single submissions #1852

Closed
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
24 changes: 0 additions & 24 deletions core/src/main/java/de/jplag/JPlag.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package de.jplag;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -111,26 +108,5 @@ private static void checkForConfigurationConsistency(JPlagOptions options) throw
if (options.normalize() && !options.language().supportsNormalization()) {
logger.error(String.format("The language %s cannot be used with normalization.", options.language().getName()));
}

List<String> duplicateNames = getDuplicateSubmissionFolderNames(options);
if (duplicateNames.size() > 0) {
throw new RootDirectoryException(String.format("Duplicate root directory names found: %s", String.join(", ", duplicateNames)));
}
}

private static List<String> getDuplicateSubmissionFolderNames(JPlagOptions options) {
List<String> duplicateNames = new ArrayList<>();
Set<String> alreadyFoundNames = new HashSet<>();
for (File file : options.submissionDirectories()) {
if (!alreadyFoundNames.add(file.getName())) {
duplicateNames.add(file.getName());
}
}
for (File file : options.oldSubmissionDirectories()) {
if (!alreadyFoundNames.add(file.getName())) {
duplicateNames.add(file.getName());
}
}
return duplicateNames;
}
}
102 changes: 98 additions & 4 deletions core/src/main/java/de/jplag/SubmissionSetBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -79,10 +81,13 @@ public SubmissionSet buildSubmissionSet() throws ExitException {
submissionFiles.addAll(listSubmissionFiles(submissionDirectory, false));
}

Set<File> allRootDirectories = submissionFiles.stream().map(SubmissionFileData::root).collect(Collectors.toSet());
Map<File, String> rootDirectoryNamePrefixesMapper = getRootDirectoryNamesPrefixesMapper(allRootDirectories);

ProgressBar progressBar = ProgressBarLogger.createProgressBar(ProgressBarType.LOADING, submissionFiles.size());
Map<File, Submission> foundSubmissions = new HashMap<>();
for (SubmissionFileData submissionFile : submissionFiles) {
processSubmissionFile(submissionFile, multipleRoots, foundSubmissions);
processSubmissionFile(submissionFile, multipleRoots, rootDirectoryNamePrefixesMapper, foundSubmissions);
progressBar.step();
}
progressBar.dispose();
Expand All @@ -102,6 +107,92 @@ public SubmissionSet buildSubmissionSet() throws ExitException {
return new SubmissionSet(submissions, baseCodeSubmission.orElse(null), options);
}

private static String[] getCanonicalPathComponents(File path) {
try {
return path.getCanonicalPath().split(File.separator.equals("\\") ? "\\\\" : File.separator);
} catch (Exception e) {
throw new RuntimeException("Error getting canonical path", e);
}
}

public static File getCommonAncestor(File firstPath, File secondPath) {
String[] firstComponents = getCanonicalPathComponents(firstPath);
String[] secondComponents = getCanonicalPathComponents(secondPath);

int minLength = Math.min(firstComponents.length, secondComponents.length);
int commonLength = 0;

for (int i = 0; i < minLength; i++) {
if (firstComponents[i].equals(secondComponents[i])) {
commonLength++;
} else {
break;
}
}

if (commonLength == 0) {
return null;
}

StringBuilder commonPath = new StringBuilder(firstComponents[0]);
for (int i = 1; i < commonLength; i++) {
commonPath.append(File.separator).append(firstComponents[i]);
}

return new File(commonPath.toString());
}

private String findCommonPathPrefix(List<File> canonicalPaths) {
if (canonicalPaths == null) {
return "";
}

File prefix = canonicalPaths.getFirst();
for (int i = 1; i < canonicalPaths.size(); i++) {
prefix = getCommonAncestor(prefix, canonicalPaths.get(i));
}

return prefix == null ? null : prefix.toString();
}

private String getPathPrefix(File path, String commonPrefix) {
String result = path.toString().substring(commonPrefix.length());
return result.startsWith(File.separator) ? result.substring(1) : result;
}

private Map<File, String> getRootDirectoryNamesPrefixesMapper(Set<File> allRootDirectories) {
Map<String, List<File>> conflicts = getRootDirectoryNameConflicts(allRootDirectories);

Map<File, String> result = new HashMap<>();
conflicts.forEach((name, paths) -> {
if (paths.size() > 1) {
String commonPrefix = findCommonPathPrefix(paths);
for (File path : paths) {
result.put(path, getPathPrefix(path, commonPrefix));
}
} else {
result.put(paths.getFirst(), "");
}
});

return result;
}

private static Map<String, List<File>> getRootDirectoryNameConflicts(Set<File> allRootDirectories) {
Map<String, List<File>> conflicts = new HashMap<>();

for (File rootDir : allRootDirectories) {
String roodDirName = rootDir.getName();
if (conflicts.containsKey(roodDirName)) {
conflicts.get(roodDirName).add(rootDir);
} else {
conflicts.put(roodDirName, Stream.of(rootDir).collect(Collectors.toList()));
}
}

return conflicts;
}

/**
* Verify that the given root directories exist and have no duplicate entries.
*/
Expand Down Expand Up @@ -231,14 +322,17 @@ private Submission processSubmission(String submissionName, File submissionFile,
return new Submission(submissionName, submissionFile, isNew, parseFilesRecursively(submissionFile), options.language());
}

private void processSubmissionFile(SubmissionFileData file, boolean multipleRoots, Map<File, Submission> foundSubmissions) throws ExitException {
private void processSubmissionFile(SubmissionFileData file, boolean multipleRoots, Map<File, String> rootDirectoryNamePrefixesMapper,
Map<File, Submission> foundSubmissions) throws ExitException {
String errorMessage = isExcludedEntry(file.submissionFile());
if (errorMessage != null) {
logger.error(errorMessage);
}

String rootDirectoryPrefix = multipleRoots ? (file.root().getName() + File.separator) : "";
String submissionName = rootDirectoryPrefix + file.submissionFile().getName();
String rootDirectoryPrefix = rootDirectoryNamePrefixesMapper.get(file.root());
rootDirectoryPrefix = rootDirectoryPrefix.isEmpty() && multipleRoots ? file.root().getName() : rootDirectoryPrefix;
String submissionName = rootDirectoryPrefix.isEmpty() ? file.submissionFile().getName()
: rootDirectoryPrefix + File.separator + file.submissionFile().getName();
Submission submission = processSubmission(submissionName, file.submissionFile(), file.isNew());
foundSubmissions.put(submission.getRoot(), submission);
}
Expand Down
29 changes: 28 additions & 1 deletion core/src/test/java/de/jplag/InvalidSubmissionTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package de.jplag;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.io.File;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand All @@ -18,6 +20,31 @@ class InvalidSubmissionTest extends TestBase {

private static final String SAMPLE_NAME = "InvalidSubmissions";

@Test
@DisplayName("test that it works with a multiple roots")
void testMultipleRootsSubmission() throws ExitException {
List<String> submissions = new ArrayList<>();
submissions.add(getBasePath("basecode/A"));
submissions.add(getBasePath("basecode/B"));
submissions.add(getBasePath("basecode/base"));
JPlagResult result = runJPlag(submissions, it -> it);
List<String> results = result.getSubmissions().getSubmissions().stream().map(Submission::getName).sorted().toList();
assertEquals(results.get(0), "A" + File.separator + "TerrainType.java");
assertEquals(results.get(1), "B" + File.separator + "TerrainType.java");
assertEquals(results.get(2), "base" + File.separator + "TerrainType.java");
}

@Test
@DisplayName("test that it works with a single submission")
void testSingleSubmission() throws ExitException, FileNotFoundException {
String submission = "basecode/A";
Set<File> oldSubmissions = new HashSet<>();
oldSubmissions.add(new File(getBasePath("basecode/B")));
oldSubmissions.add(new File(getBasePath("basecode/base")));
JPlagResult result = runJPlag(submission, it -> it.withOldSubmissionDirectories(oldSubmissions));
assertEquals(result.getAllComparisons().size(), 2);
}

/**
* Tests if invalid submissions are correctly filtered, leading to no valid submissions. The debug options lead to the
* invalid submissions being stored.
Expand Down
4 changes: 4 additions & 0 deletions core/src/test/java/de/jplag/RootFolderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.io.File;
import java.util.List;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -66,11 +67,14 @@ void testDisjunctNewAndOldRootDirectories() throws ExitException {
assertEquals(numberOfExpectedComparison, result.getAllComparisons().size());
}

@Disabled
@Test
@DisplayName("test multiple overlapping root directories, one marked with as old")
void testOverlappingNewAndOldDirectoriesOverlap() throws ExitException {
List<String> newDirectories = List.of(getBasePath(ROOT_2));
List<String> oldDirectories = List.of(getBasePath(ROOT_2));
// This is not actually valid. The fact that one folder is in the old ones and that one is in the old directories
// shouldn't bring any problem, in that it will removed by SubmissionSetBuilder>>checkForNonOverlappingRootDirectories
assertThrows(RootDirectoryException.class, () -> runJPlag(newDirectories, oldDirectories, it -> it));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package de.jplag.reporting.reportobject;

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

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.stream.Stream;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -37,6 +38,23 @@ void testCreateAndSaveReportWithBasecode() throws ExitException, IOException {
assertTrue(isArchive(testZip));
}

@Test
void testWithSameNameSubmissions() throws ExitException, IOException {
File submission1 = new File(BASE_PATH, "basecode/A");
File submission2 = new File(BASE_PATH, "basecode/B");
File submission3 = new File(BASE_PATH, "basecode-sameNameOfSubdirectoryAndRootdirectory/A");
File submission4 = new File(BASE_PATH, "basecode-sameNameOfSubdirectoryAndRootdirectory/B");
List<String> submissions = Stream.of(submission1, submission2, submission3, submission4).map(File::toString).toList();
JPlagResult result = runJPlag(submissions, it -> it.withBaseCodeSubmissionDirectory(new File(BASE_PATH, BASECODE_BASE)));
File testZip = File.createTempFile("result", ".zip");

ReportObjectFactory reportObjectFactory = new ReportObjectFactory(testZip);
reportObjectFactory.createAndSaveReport(result);

assertNotNull(result);
assertTrue(isArchive(testZip));
}

/**
* Checks if the given file is a valid archive
* @param file The file to check
Expand Down