diff --git a/test/fixtures.ts b/test/fixtures.ts index 0472762b33..3d0b1c7657 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -242,6 +242,8 @@ abstract class LanguageFixture extends Fixture { filename: string, additionalRendererOptions: RendererOptions, additionalFiles: string[], + testComparisonArgs?: Partial, + expectedFilename?: string, ): Promise; additionalFiles(_sample: Sample): string[] { @@ -251,6 +253,7 @@ abstract class LanguageFixture extends Fixture { async runWithSample(sample: Sample, index: number, total: number) { const cwd = this.getRunDirectory(); const sampleFile = path.resolve(sample.path); + const sampleOutFile = sample.outPath ? path.resolve(sample.outPath) : undefined; const shouldSkip = this.shouldSkipTest(sample); const additionalFiles = this.additionalFiles(sample).map((p) => path.resolve(p), @@ -291,6 +294,8 @@ abstract class LanguageFixture extends Fixture { sampleFile, sample.additionalRendererOptions, additionalFiles, + sample.comparisonArgs, + sampleOutFile, ), MAX_TEST_RUNTIME_MS, ); @@ -355,6 +360,8 @@ class JSONFixture extends LanguageFixture { filename: string, additionalRendererOptions: RendererOptions, _additionalFiles: string[], + testComparisonArgs?: Partial, + expectedFilename?: string, ): Promise { if (this.language.compileCommand) { await execAsync(this.language.compileCommand); @@ -363,15 +370,23 @@ class JSONFixture extends LanguageFixture { return 0; } - compareJsonFileToJson( - comparisonArgs( - this.language, - filename, - filename, - additionalRendererOptions, - ), + let sampleComparisonArgs = comparisonArgs( + this.language, + filename, + expectedFilename ? expectedFilename : filename, + additionalRendererOptions, ); + // Add any additional comparison args specified in optionMap for this test + if (testComparisonArgs) { + sampleComparisonArgs = { + ...sampleComparisonArgs, + ...testComparisonArgs, + }; + } + + compareJsonFileToJson(sampleComparisonArgs); + if ( this.language.diffViaSchema && !_.includes( @@ -412,6 +427,9 @@ class JSONFixture extends LanguageFixture { if (fs.statSync(sample.path).size > 32 * 1024 * 1024) { return true; } + if (sample.language && this.language.name !== sample.language.name) { + return true; + } if (this.language.includeJSON !== undefined) { return !_.includes( this.language.includeJSON, diff --git a/test/inputs/json/misc/227e2.in.json b/test/inputs/json/misc/227e2.in.json new file mode 100644 index 0000000000..56425f9b78 --- /dev/null +++ b/test/inputs/json/misc/227e2.in.json @@ -0,0 +1,11 @@ +{ + "results": [ + { + "age": 52, + "name": null + }, + { + "age": 27 + } + ] +} diff --git a/test/inputs/json/misc/227e2.out.json b/test/inputs/json/misc/227e2.out.json new file mode 100644 index 0000000000..809e3cfa02 --- /dev/null +++ b/test/inputs/json/misc/227e2.out.json @@ -0,0 +1,10 @@ +{ + "results": [ + { + "age": 52 + }, + { + "age": 27 + } + ] +} diff --git a/test/lib/optionMap.ts b/test/lib/optionMap.ts new file mode 100644 index 0000000000..5891c35ca9 --- /dev/null +++ b/test/lib/optionMap.ts @@ -0,0 +1,23 @@ +import { RendererOptions } from "quicktype-core"; +import { ComparisonArgs } from "../utils"; +import { GoLanguage, Language } from "../languages"; + +type TestOptions = { + cliOptions: RendererOptions; + language: Language; + comparisonArgs?: Partial; +}; + +const optionMap: Record = { + "test/inputs/json/misc/227e2.in.json": { + cliOptions: { + "omit-empty": true + }, + language: GoLanguage, + comparisonArgs: { + strict: true + } + } +}; + +export default optionMap; diff --git a/test/utils.ts b/test/utils.ts index b532a2e8f5..5559f91bf9 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; -import * as _ from "lodash"; +import _ from "lodash"; import * as shell from "shelljs"; import { main as quicktype_, type CLIOptions } from "../src"; @@ -9,7 +9,10 @@ import type { RendererOptions } from "quicktype-core"; import type * as languages from "./languages"; import deepEquals from "./lib/deepEquals"; +import optionMap from "./lib/optionMap"; + import chalk from "chalk"; +import { Language } from "./languages"; const strictDeepEquals: ( x: unknown, y: unknown, @@ -18,6 +21,9 @@ const strictDeepEquals: ( const DEBUG = process.env.DEBUG !== undefined; const ASSUME_STRINGS_EQUAL = process.env.ASSUME_STRINGS_EQUAL !== undefined; +const inputFilePattern = /^(.*)\.in\.(.*)$/; +const outputFilePattern = /^.*\.out\..*$/; + export function debug(x: T): T { if (DEBUG) { console.log(x); @@ -193,14 +199,49 @@ export interface Sample { path: string; additionalRendererOptions: RendererOptions; saveOutput: boolean; + outPath?: string; + comparisonArgs?: Partial; + language?: Language; } -export function samplesFromPaths(paths: string[]): Sample[] { - return paths.map((p) => ({ - path: p, +function sampleFromPath(path: string): Sample { + const currentSample: Sample = { + path: path, additionalRendererOptions: {}, - saveOutput: true, - })); + saveOutput: true + }; + + // Check optionMap for any CLI options and comparison options the test in this path should run with + if (optionMap[path]) { + const { cliOptions, language, comparisonArgs } = optionMap[path]; + currentSample.additionalRendererOptions = cliOptions; + currentSample.language = language; + currentSample.comparisonArgs = comparisonArgs; + } + + // If this is an input file, we should expect a corresponding output file to compare against + const inputFileMatch = path.match(inputFilePattern); + if (inputFileMatch) { + // Search for expected output file. Add to sample if found, throw error if one does not exist. + const outFilePath = inputFileMatch[1] + ".out." + inputFileMatch[2]; + if (!fs.existsSync(outFilePath)) { + throw new Error(`Input file with name ${path} does not have a matching output file named ${outFilePath}`); + } + currentSample.outPath = outFilePath; + } + return currentSample; +} + +export function samplesFromPaths(paths: string[]): Sample[] { + const samples: Sample[] = []; + for (const path of paths) { + // Output files will be processed with their corresponding input file and added to the same sample. + const outputFileMatch = path.match(outputFilePattern); + if (outputFileMatch) continue; + + samples.push(sampleFromPath(path)); + } + return samples; } export function samplesFromSources(