Skip to content

Commit

Permalink
Merge branch 'main' into emmercm/20230915-labels
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm authored Sep 15, 2023
2 parents eb224fd + 686a6a3 commit 23d788a
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 34 deletions.
39 changes: 20 additions & 19 deletions src/igir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default class Igir {
throw new Error('No DAT contains parent/clone information, cannot process --single');
}

const datsToWrittenRoms = new Map<DAT, Map<Parent, File[]>>();
const datsToWrittenFiles = new Map<DAT, File[]>();
const romOutputDirs: string[] = [];
const movedRomsToDelete: File[] = [];
const datsStatuses: DATStatus[] = [];
Expand Down Expand Up @@ -109,20 +109,22 @@ export default class Igir {
const movedRoms = await new CandidateWriter(this.options, progressBar)
.write(filteredDat, parentsToCandidates);
movedRomsToDelete.push(...movedRoms);
const writtenRoms = [...parentsToCandidates.entries()]
.reduce((map, [parent, releaseCandidates]) => {
// For each Parent, find what rom Files were written
const parentWrittenRoms = releaseCandidates
.flatMap((releaseCandidate) => releaseCandidate.getRomsWithFiles())
.map((romWithFiles) => romWithFiles.getOutputFile());
map.set(parent, parentWrittenRoms);
return map;
}, new Map<Parent, File[]>());
datsToWrittenRoms.set(filteredDat, writtenRoms);
const writtenRoms = [...parentsToCandidates.values()]
.flatMap((releaseCandidates) => releaseCandidates)
.flatMap((releaseCandidate) => releaseCandidate
.getRomsWithFiles()
.map((romWithFiles) => romWithFiles.getOutputFile()));
datsToWrittenFiles.set(filteredDat, writtenRoms);

// Write a fixdat
await new FixdatCreator(this.options, progressBar)
const fixdatPath = await new FixdatCreator(this.options, progressBar)
.write(filteredDat, parentsToCandidates);
if (fixdatPath) {
datsToWrittenFiles.set(filteredDat, [
...(datsToWrittenFiles.get(filteredDat) ?? []),
await File.fileOf(fixdatPath),
]);
}

// Write the output report
const datStatus = await new StatusGenerator(this.options, progressBar)
Expand All @@ -147,10 +149,10 @@ export default class Igir {
datProcessProgressBar.delete();

// Delete moved ROMs
await this.deleteMovedRoms(roms, movedRomsToDelete, datsToWrittenRoms);
await this.deleteMovedRoms(roms, movedRomsToDelete, datsToWrittenFiles);

// Clean the output directories
const cleanedOutputFiles = await this.processOutputCleaner(romOutputDirs, datsToWrittenRoms);
const cleanedOutputFiles = await this.processOutputCleaner(romOutputDirs, datsToWrittenFiles);

// Generate the report
await this.processReportGenerator(roms, cleanedOutputFiles, datsStatuses);
Expand Down Expand Up @@ -270,31 +272,30 @@ export default class Igir {
private async deleteMovedRoms(
rawRomFiles: File[],
movedRomsToDelete: File[],
datsToWrittenRoms: Map<DAT, Map<Parent, File[]>>,
datsToWrittenFiles: Map<DAT, File[]>,
): Promise<void> {
if (!movedRomsToDelete.length) {
return;
}

const progressBar = await this.logger.addProgressBar('Deleting moved files');
const deletedFilePaths = await new MovedROMDeleter(progressBar)
.delete(rawRomFiles, movedRomsToDelete, datsToWrittenRoms);
.delete(rawRomFiles, movedRomsToDelete, datsToWrittenFiles);
await progressBar.doneItems(deletedFilePaths.length, 'moved file', 'deleted');
await progressBar.freeze();
}

private async processOutputCleaner(
dirsToClean: string[],
datsToWrittenRoms: Map<DAT, Map<Parent, File[]>>,
datsToWrittenFiles: Map<DAT, File[]>,
): Promise<string[]> {
if (!this.options.shouldWrite() || !this.options.shouldClean()) {
return [];
}

const progressBar = await this.logger.addProgressBar('Cleaning output directory');
const uniqueDirsToClean = dirsToClean.reduce(ArrayPoly.reduceUnique(), []);
const writtenFilesToExclude = [...datsToWrittenRoms.values()]
.flatMap((parentsToFiles) => [...parentsToFiles.values()])
const writtenFilesToExclude = [...datsToWrittenFiles.values()]
.flatMap((files) => files);
const filesCleaned = await new DirectoryCleaner(this.options, progressBar)
.clean(uniqueDirsToClean, writtenFilesToExclude);
Expand Down
10 changes: 4 additions & 6 deletions src/modules/movedRomDeleter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import fsPoly from '../polyfill/fsPoly.js';
import ArchiveEntry from '../types/files/archives/archiveEntry.js';
import File from '../types/files/file.js';
import DAT from '../types/logiqx/dat.js';
import Parent from '../types/logiqx/parent.js';
import Module from './module.js';

/**
Expand All @@ -25,7 +24,7 @@ export default class MovedROMDeleter extends Module {
async delete(
inputRoms: File[],
movedRoms: File[],
datsToWrittenRoms: Map<DAT, Map<Parent, File[]>>,
datsToWrittenFiles: Map<DAT, File[]>,
): Promise<string[]> {
if (!movedRoms.length) {
return [];
Expand All @@ -39,7 +38,7 @@ export default class MovedROMDeleter extends Module {

const filePathsToDelete = MovedROMDeleter.filterOutWrittenFiles(
fullyConsumedFiles,
datsToWrittenRoms,
datsToWrittenFiles,
);

await this.progressBar.setSymbol(ProgressBarSymbol.DELETING);
Expand Down Expand Up @@ -128,10 +127,9 @@ export default class MovedROMDeleter extends Module {
*/
private static filterOutWrittenFiles(
movedRoms: string[],
datsToWrittenRoms: Map<DAT, Map<Parent, File[]>>,
datsToWrittenFiles: Map<DAT, File[]>,
): string[] {
const writtenFilePaths = new Set([...datsToWrittenRoms.values()]
.flatMap((parentsToFiles) => [...parentsToFiles.values()])
const writtenFilePaths = new Set([...datsToWrittenFiles.values()]
.flatMap((files) => files)
.map((file) => file.getFilePath()));

Expand Down
18 changes: 17 additions & 1 deletion test/igir.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ describe('with explicit DATs', () => {
])('should generate a fixdat when writing: %s', async (command) => {
await copyFixturesToTemp(async (inputTemp, outputTemp) => {
const result = await runIgir({
commands: [command],
commands: [command, 'clean'],
dat: [path.join(inputTemp, 'dats')],
input: [path.join(inputTemp, 'roms')],
output: outputTemp,
Expand Down Expand Up @@ -691,6 +691,22 @@ describe('with inferred DATs', () => {
});
});

it('should move to the same directory', async () => {
await copyFixturesToTemp(async (inputTemp, outputTemp) => {
const inputDir = path.join(inputTemp, 'roms', 'raw');
const inputBefore = await walkWithCrc(inputDir, inputDir);

await runIgir({
commands: ['move', 'test'],
input: [inputDir],
output: inputDir,
});

await expect(walkWithCrc(inputDir, inputDir)).resolves.toEqual(inputBefore);
await expect(walkWithCrc(inputTemp, outputTemp)).resolves.toHaveLength(0);
});
});

it('should move, extract, and test', async () => {
await copyFixturesToTemp(async (inputTemp, outputTemp) => {
const result = await runIgir({
Expand Down
12 changes: 12 additions & 0 deletions test/modules/candidateWriter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,18 @@ describe('zip', () => {
});
});

it('should not move if tested zip has wrong number of entries', () => {
// TODO(cemmer)
});

it('should not move if tested zip is missing an entry', () => {
// TODO(cemmer)
});

it('should not move if tested zip has an entry with an unexpected checksum', () => {
// TODO(cemmer)
});

test.each([
// Control group of un-headered files
['raw/empty.rom', 'empty.rom', '00000000'],
Expand Down
13 changes: 5 additions & 8 deletions test/modules/movedRomDeleter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,11 @@ describe('should delete archives', () => {
.flatMap((releaseCandidate) => releaseCandidate.getRomsWithFiles())
.map((romWithFiles) => romWithFiles.getInputFile());

const parentsToWrittenRoms = new Map([...parentsToCandidates.entries()]
.map(([parent, releaseCandidates]) => {
const writtenRoms = releaseCandidates
.flatMap((releaseCandidate) => releaseCandidate.getRomsWithFiles())
.map((romWithFiles) => romWithFiles.getOutputFile());
return [parent, writtenRoms];
}));
const datsToWrittenRoms = new Map([[dat, parentsToWrittenRoms]]);
const writtenRoms = [...parentsToCandidates.values()]
.flatMap((releaseCandidates) => releaseCandidates)
.flatMap((releaseCanddiate) => releaseCanddiate.getRomsWithFiles())
.map((romWithFiles) => romWithFiles.getOutputFile());
const datsToWrittenRoms = new Map([[dat, writtenRoms]]);

const deletedFilePaths = (
await new MovedROMDeleter(new ProgressBarFake())
Expand Down
28 changes: 28 additions & 0 deletions test/polyfill/filePoly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,31 @@ describe('fileOfSize', () => {
}
});
});

describe('readAt', () => {
it('should read a small file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'file'));
await expect(fsPoly.exists(tempFile)).resolves.toEqual(false);

try {
const file = await filePoly.fileOfSize(tempFile, 'r', Constants.MAX_MEMORY_FILE_SIZE - 1);
await expect(file.readAt(0, 16)).resolves.toEqual(Buffer.alloc(16));
await file.close();
} finally {
await fsPoly.rm(tempFile);
}
});

it('should read a large file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'file'));
await expect(fsPoly.exists(tempFile)).resolves.toEqual(false);

try {
const file = await filePoly.fileOfSize(tempFile, 'r', Constants.MAX_MEMORY_FILE_SIZE + 1);
await expect(file.readAt(0, 16)).resolves.toEqual(Buffer.alloc(16));
await file.close();
} finally {
await fsPoly.rm(tempFile);
}
});
});
75 changes: 75 additions & 0 deletions test/polyfill/fsPoly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,79 @@ import path from 'path';
import Constants from '../../src/constants.js';
import fsPoly from '../../src/polyfill/fsPoly.js';

describe('canSymlink', () => {
it('should not throw', async () => {
await expect(fsPoly.canSymlink(Constants.GLOBAL_TEMP_DIR)).resolves.not.toThrow();
});
});

describe('isDirectory', () => {
it('should return true for a directory', async () => {
const tempDir = await fsPoly.mkdtemp(Constants.GLOBAL_TEMP_DIR);
await expect(fsPoly.isDirectory(tempDir)).resolves.toEqual(true);
});

it('should return false for a file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
await fsPoly.touch(tempFile);
await expect(fsPoly.isDirectory(tempFile)).resolves.toEqual(false);
await fsPoly.rm(tempFile);
});

it('should return false for non-existent file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
await expect(fsPoly.isDirectory(tempFile)).resolves.toEqual(false);
});
});

describe('isDirectorySync', () => {
it('should return true for a directory', async () => {
const tempDir = await fsPoly.mkdtemp(Constants.GLOBAL_TEMP_DIR);
expect(fsPoly.isDirectorySync(tempDir)).toEqual(true);
});

it('should return false for a file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
await fsPoly.touch(tempFile);
expect(fsPoly.isDirectorySync(tempFile)).toEqual(false);
await fsPoly.rm(tempFile);
});

it('should return false for non-existent file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
expect(fsPoly.isDirectorySync(tempFile)).toEqual(false);
});
});

describe('isSymlink', () => {
it('should return true for a symlink', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
await fsPoly.touch(tempFile);
const tempLink = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'link'));
await fsPoly.symlink(tempFile, tempLink);
await expect(fsPoly.isSymlink(tempLink)).resolves.toEqual(true);
await fsPoly.rm(tempLink);
await fsPoly.rm(tempFile);
});

it('should return false for a plain directory', async () => {
const tempDir = await fsPoly.mkdtemp(Constants.GLOBAL_TEMP_DIR);
await expect(fsPoly.isSymlink(tempDir)).resolves.toEqual(false);
});

it('should return false for a plain file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
await fsPoly.touch(tempFile);
await expect(fsPoly.isSymlink(tempFile)).resolves.toEqual(false);
await fsPoly.rm(tempFile);
});

it('should return false for non-existent file', async () => {
const tempFile = await fsPoly.mktemp(path.join(Constants.GLOBAL_TEMP_DIR, 'temp'));
await expect(fsPoly.isSymlink(tempFile)).resolves.toEqual(false);
});
});

describe('makeLegal', () => {
describe('unix', () => {
test.each([
Expand Down Expand Up @@ -70,6 +143,7 @@ describe('rm', () => {
await fsPoly.rm(tempLink);
await expect(fsPoly.exists(tempLink)).resolves.toEqual(false);
await expect(fsPoly.exists(tempFile)).resolves.toEqual(true);
await fsPoly.rm(tempFile);
});
});

Expand Down Expand Up @@ -110,5 +184,6 @@ describe('rmSync', () => {
fsPoly.rmSync(tempLink);
await expect(fsPoly.exists(tempLink)).resolves.toEqual(false);
await expect(fsPoly.exists(tempFile)).resolves.toEqual(true);
fsPoly.rmSync(tempFile);
});
});
18 changes: 18 additions & 0 deletions test/types/files/archives/archiveEntry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import fsPoly from '../../../../src/polyfill/fsPoly.js';
import ArchiveEntry from '../../../../src/types/files/archives/archiveEntry.js';
import SevenZip from '../../../../src/types/files/archives/sevenZip.js';
import Zip from '../../../../src/types/files/archives/zip.js';
import File from '../../../../src/types/files/file.js';
import FileFactory from '../../../../src/types/files/fileFactory.js';
import ROMHeader from '../../../../src/types/files/romHeader.js';
import Options from '../../../../src/types/options.js';
import IPSPatch from '../../../../src/types/patches/ipsPatch.js';
import ProgressBarFake from '../../../console/progressBarFake.js';

describe('getEntryPath', () => {
Expand Down Expand Up @@ -157,6 +159,22 @@ describe('createReadStream', () => {
});
});

describe('withPatch', () => {
it('should attach a matching patch', async () => {
const entry = await ArchiveEntry.entryOf(new Zip('file.zip'), 'entry.rom', 0, '00000000');
const patch = IPSPatch.patchFrom(await File.fileOf('patch 00000000.ips'));
const patchedEntry = entry.withPatch(patch);
expect(patchedEntry.getPatch()).toEqual(patch);
});

it('should not attach a non-matching patch', async () => {
const entry = await ArchiveEntry.entryOf(new Zip('file.zip'), 'entry.rom', 0, 'FFFFFFFF');
const patch = IPSPatch.patchFrom(await File.fileOf('patch 00000000.ips'));
const patchedEntry = entry.withPatch(patch);
expect(patchedEntry.getPatch()).toBeUndefined();
});
});

describe('equals', () => {
it('should equal itself', async () => {
const entry = await ArchiveEntry.entryOf(new Zip('file.zip'), 'entry.rom', 0, '00000000');
Expand Down
Loading

0 comments on commit 23d788a

Please sign in to comment.