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

Issue 856: refactoring of how filenames are chosen for Detailed Summary #894

Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions src/main/java/network/brightspots/rcv/AuditableFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

final class AuditableFile extends File {
public AuditableFile(String pathname) {
super(pathname);
}

public AuditableFile(Path pathname) {
super(pathname.toAbsolutePath().toString());
}

public void finalizeAndHash() {
String hash = Utils.bytesToHex(FileUtils.getHashBytes(this, "SHA-512"));

Expand Down
272 changes: 156 additions & 116 deletions src/main/java/network/brightspots/rcv/ResultsWriter.java

Large diffs are not rendered by default.

101 changes: 59 additions & 42 deletions src/test/java/network/brightspots/rcv/TabulatorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import network.brightspots.rcv.ResultsWriter.ResultFile;
import network.brightspots.rcv.ResultsWriter.ResultType;
import network.brightspots.rcv.Tabulator.TabulationAbortedException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
Expand Down Expand Up @@ -187,11 +190,16 @@ private static boolean fileCompareLineByLine(String path1, String path2) {
return result;
}

// given stem and suffix returns path to file in test asset folder
private static Path getTestDirectory(String stem) {
return Paths.get(System.getProperty("user.dir"), TEST_ASSET_FOLDER, stem);
}

// given stem and suffix returns path to file in test asset folder
private static String getTestFilePath(String stem, String suffix) {
return Paths.get(System.getProperty("user.dir"), TEST_ASSET_FOLDER, stem, stem + suffix)
.toAbsolutePath()
.toString();
Path directory = getTestDirectory(stem);
String filename = stem + suffix;
return Paths.get(directory.toString(), filename).toAbsolutePath().toString();
}

private static void runTabulationTest(String testStem) {
Expand Down Expand Up @@ -224,7 +232,7 @@ private static void runTabulationTest(String stem, String expectedException,

if (config.isMultiSeatSequentialWinnerTakesAllEnabled()) {
for (int i = 1; i <= config.getNumberOfWinners(); i++) {
compareFiles(config, stem, timestampString, Integer.toString(i));
compareFiles(config, stem, timestampString, i);
}
} else {
compareFiles(config, stem, timestampString, null);
Expand All @@ -233,14 +241,12 @@ private static void runTabulationTest(String stem, String expectedException,
int numSlicedFilesChecked = 0;
for (ContestConfig.TabulateBySlice slice : config.enabledSlices()) {
for (String sliceName : session.loadSliceNamesFromCvrs(slice, config)) {
String outputTypeJson = ResultsWriter.sanitizeStringForOutput(
String.format("%s_%s_summary", sliceName, slice.toLowerString()));
String outputTypeCsv = ResultsWriter.sanitizeStringForOutput(
String.format("%s_%s_extended_summary", sliceName, slice.toLowerString()));
if (compareFiles(config, stem, outputTypeJson, ".json", timestampString, null, true)) {
ResultFile resultFileJson = new ResultFile(ResultType.DETAILED_JSON, slice, sliceName);
ResultFile resultFileCsv = new ResultFile(ResultType.DETAILED_CSV, slice, sliceName);
if (compareFiles(config, stem, resultFileJson, timestampString, null, true)) {
numSlicedFilesChecked++;
}
if (compareFiles(config, stem, outputTypeCsv, ".csv", timestampString, null, true)) {
if (compareFiles(config, stem, resultFileCsv, timestampString, null, true)) {
numSlicedFilesChecked++;
}
}
Expand All @@ -259,7 +265,7 @@ private static void runConvertToCdfTest(String stem) {

String timestampString = session.getTimestampString();
ContestConfig config = ContestConfig.loadContestConfig(configPath);
compareFiles(config, stem, "cvr_cdf", ".json", timestampString, null, false);
compareFiles(config, stem, ResultType.CDF, timestampString, null, false);

cleanOutputFolder(session);
}
Expand Down Expand Up @@ -322,38 +328,50 @@ private static void cleanOutputFolder(TabulatorSession session) {
}

private static void compareFiles(
ContestConfig config, String stem, String timestampString, String sequentialId) {
compareFiles(config, stem, "summary", ".json", timestampString, sequentialId, false);
compareFiles(config, stem, "extended_summary", ".csv", timestampString, sequentialId, false);
ContestConfig config, String stem, String timestampString, Integer sequentialId) {
compareFiles(config, stem, ResultType.DETAILED_JSON, timestampString, sequentialId, false);
compareFiles(config, stem, ResultType.DETAILED_CSV, timestampString, sequentialId, false);
compareExtendedSummaryToSummary(config, timestampString, sequentialId);
if (config.isGenerateCdfJsonEnabled()) {
compareFiles(config, stem, "cvr_cdf", ".json", timestampString, sequentialId, false);
compareFiles(config, stem, ResultType.CDF, timestampString, sequentialId, false);
}
}

/**
* Helper comparison for non-slice files.
*/
private static boolean compareFiles(
ContestConfig config,
String stem,
ResultType resultType,
String timestampString,
Integer sequentialId,
boolean onlyCheckIfExpectedFileExists) {
ResultFile actualResultFile = new ResultFile(resultType);
return compareFiles(
config,
stem,
actualResultFile,
timestampString,
sequentialId,
onlyCheckIfExpectedFileExists);
}

/**
* Returns whether the files were compared at all.
* If they were compared and not equal, the test will fail.
*/
private static boolean compareFiles(
ContestConfig config,
String stem,
String outputType,
String extension,
ResultFile actualResultFile,
String timestampString,
String sequentialId,
Integer sequentialId,
boolean onlyCheckIfExpectedFileExists) {
String actualOutputPath =
ResultsWriter.getOutputFilePath(
config.getOutputDirectory(), outputType, timestampString, sequentialId)
+ extension;
String expectedPath =
getTestFilePath(
stem,
ResultsWriter.sequentialSuffixForOutputPath(sequentialId)
+ "_expected_"
+ outputType
+ extension);
String actualOutputPath = actualResultFile.getPath(
config.getOutputDirectory(), timestampString, sequentialId).toAbsolutePath().toString();
String expectedPath = actualResultFile.getPath(getTestDirectory(stem).toString(),
stem, "expected", sequentialId).toString();

Logger.info("Comparing files:\nGenerated: %s\nReference: %s", actualOutputPath, expectedPath);
boolean didCompare = true;
Expand All @@ -375,38 +393,37 @@ private static boolean compareFiles(
* extended file except for the inactive ballot breakdown.
*/
private static void compareExtendedSummaryToSummary(
ContestConfig config, String timestampString, String sequentialId) {
String summaryPath = ResultsWriter.getOutputFilePath(
config.getOutputDirectory(), "summary", timestampString, sequentialId)
+ ".csv";
String extendedPath = ResultsWriter.getOutputFilePath(
config.getOutputDirectory(), "extended_summary", timestampString, sequentialId)
+ ".csv";
ContestConfig config, String timestampString, Integer sequentialId) {
String dir = config.getOutputDirectory();
String summaryPath = new ResultFile(ResultType.SUMMARY_CSV).getPath(
dir, timestampString, sequentialId).toAbsolutePath().toString();
String detailedPath = new ResultFile(ResultType.DETAILED_CSV).getPath(
dir, timestampString, sequentialId).toAbsolutePath().toString();

try (BufferedReader brSummary = new BufferedReader(new FileReader(summaryPath, UTF_8));
BufferedReader brExtended = new BufferedReader(new FileReader(extendedPath, UTF_8))) {
BufferedReader brDetailed = new BufferedReader(new FileReader(detailedPath, UTF_8))) {
while (true) {
String lineExtended = brExtended.readLine();
String lineDetailed = brDetailed.readLine();
// If the extended file has reached its end, then the non-extended file must have too
if (lineExtended == null) {
if (lineDetailed == null) {
assertNull(brSummary.readLine(), "Extended file is missing a line");
return;
}

// If the extended file should be excluded, continue without moving the file pointer
// in the non-extended file. For now, there's only one type of row excluded, and they
// happen to all start with "Inactive Ballots by"
if (lineExtended.startsWith("Inactive Ballots by")) {
if (lineDetailed.startsWith("Inactive Ballots by")) {
continue;
}

// This line should be equal in both files. Ensure the line exists and they're equal
// in both files.
String lineSummary = brSummary.readLine();
assertNotNull(lineSummary, "Summary file is missing a line");
if (!lineSummary.equals(lineExtended)) {
if (!lineSummary.equals(lineDetailed)) {
fail("Line differes in extended vs non-extended CSV: \n%s\n%s".formatted(
lineSummary, lineExtended));
lineSummary, lineDetailed));
}
}
} catch (FileNotFoundException exception) {
Expand Down
Loading