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

chore(tasks/transform-conformance): support override to replace takeover mode #7771

Merged
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
2 changes: 1 addition & 1 deletion .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extend-exclude = [
"tasks/prettier_conformance/prettier",
"tasks/prettier_conformance/snapshots",
"tasks/transform_conformance/tests/**/output.js",
"tasks/transform_conformance/snapshots",
"tasks/transform_conformance/overrides",
"npm/oxc-wasm/oxc_wasm.js",
]

Expand Down

This file was deleted.

638 changes: 637 additions & 1 deletion tasks/transform_conformance/snapshots/babel.snap.md

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions tasks/transform_conformance/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,3 @@ pub const SKIP_TESTS: &[&str] = &[
"babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/remove-unused-excluded-keys-loose",
"babel-plugin-transform-object-rest-spread/test/fixtures/object-rest/regression/gh-8323"
];

pub const SNAPSHOT_TESTS: &[&str] =
&["babel-plugin-transform-object-rest-spread", "babel-plugin-transform-optional-chaining"];
4 changes: 4 additions & 0 deletions tasks/transform_conformance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ fn snap_root() -> PathBuf {
conformance_root().join("snapshots")
}

fn override_root() -> PathBuf {
conformance_root().join("overrides")
}

fn oxc_test_root() -> PathBuf {
conformance_root().join("tests")
}
Expand Down
76 changes: 35 additions & 41 deletions tasks/transform_conformance/src/test_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use oxc::{
use oxc_tasks_common::{normalize_path, print_diff_in_terminal, project_root};

use crate::{
constants::{PLUGINS_NOT_SUPPORTED_YET, SKIP_TESTS, SNAPSHOT_TESTS},
constants::{PLUGINS_NOT_SUPPORTED_YET, SKIP_TESTS},
driver::Driver,
fixture_root, oxc_test_root, packages_root, snap_root, TestRunnerOptions,
fixture_root, override_root, oxc_test_root, packages_root, TestRunnerOptions,
};

#[derive(Debug)]
Expand All @@ -35,12 +35,19 @@ pub struct TestCase {
pub enum TestCaseKind {
Conformance,
Exec,
Snapshot,
}

impl TestCase {
pub fn new(cwd: &Path, path: &Path) -> Option<Self> {
let mut options = BabelOptions::from_test_path(path.parent().unwrap());
let mut options_directory_path = path.parent().unwrap().to_path_buf();
// Try to find the override options.json
if let Some(path) = Self::convert_to_override_path(options_directory_path.as_path()) {
if path.join("options.json").exists() {
options_directory_path = path;
}
}

let mut options = BabelOptions::from_test_path(options_directory_path.as_path());
options.cwd.replace(cwd.to_path_buf());
let transform_options = TransformOptions::try_from(&options);
let path = path.to_path_buf();
Expand All @@ -59,14 +66,7 @@ impl TestCase {
else if path.file_stem().is_some_and(|name| name == "input" || name == "input.d")
&& path.extension().is_some_and(|ext| VALID_EXTENSIONS.contains(&ext.to_str().unwrap()))
{
if path
.strip_prefix(packages_root())
.is_ok_and(|p| SNAPSHOT_TESTS.iter().any(|t| p.to_string_lossy().starts_with(t)))
{
TestCaseKind::Snapshot
} else {
TestCaseKind::Conformance
}
TestCaseKind::Conformance
} else {
return None;
};
Expand Down Expand Up @@ -96,6 +96,28 @@ impl TestCase {
source_type
}

fn convert_to_override_path(path: &Path) -> Option<PathBuf> {
path.strip_prefix(packages_root()).ok().map(|p| override_root().join(p))
}

fn get_output_path(&self) -> Option<PathBuf> {
let babel_output_path =
self.path.parent().unwrap().read_dir().unwrap().find_map(|entry| {
let path = entry.ok()?.path();
let file_stem = path.file_stem()?;
(file_stem == "output").then_some(path)
})?;

// Try to find the override output path
if let Some(output_path) = Self::convert_to_override_path(&babel_output_path) {
if output_path.exists() {
return Some(output_path);
}
}

Some(babel_output_path)
}

pub fn skip_test_case(&self) -> bool {
let options = &self.options;

Expand Down Expand Up @@ -203,17 +225,12 @@ impl TestCase {
self.test_exec(filtered);
}
}
TestCaseKind::Snapshot => self.test_snapshot(filtered),
}
}

/// Test conformance by comparing the parsed babel code and transformed code.
fn test_conformance(&mut self, filtered: bool) {
let output_path = self.path.parent().unwrap().read_dir().unwrap().find_map(|entry| {
let path = entry.ok()?.path();
let file_stem = path.file_stem()?;
(file_stem == "output").then_some(path)
});
let output_path = self.get_output_path();

let allocator = Allocator::default();
let input = fs::read_to_string(&self.path).unwrap();
Expand Down Expand Up @@ -388,29 +405,6 @@ test("exec", () => {{
}})"#
)
}

fn test_snapshot(&self, filtered: bool) {
let result = match self.transform(HelperLoaderMode::External) {
Ok(code) => code,
Err(error) => error,
};
let mut path = snap_root().join(self.path.strip_prefix(packages_root()).unwrap());
path.set_file_name("output");
let input_extension = self.path.extension().unwrap().to_str().unwrap();
let extension =
input_extension.chars().map(|c| if c == 't' { 'j' } else { c }).collect::<String>();
path.set_extension(extension);
if filtered {
println!("Input path: {:?}", &self.path);
println!("Output path: {path:?}");
println!("Input:\n{}\n", fs::read_to_string(&self.path).unwrap());
println!("Output:\n{result}\n");
}
if fs::write(&path, &result).is_err() {
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path, &result).unwrap();
}
}
}

fn get_babel_error(error: &str) -> String {
Expand Down
77 changes: 30 additions & 47 deletions tasks/transform_conformance/update_fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
// (or maybe don't - what does this option mean?)

import { transformFileAsync } from '@babel/core';
import assert from 'assert';
import { copyFile, readdir, readFile, rename, writeFile } from 'fs/promises';
import { readdir, readFile, rename, writeFile } from 'fs/promises';
import { extname, join as pathJoin } from 'path';

const PACKAGES = [
Expand All @@ -28,7 +27,10 @@ const FILTER_OUT_PLUGINS = [
'transform-destructuring',
];

const PACKAGES_PATH = pathJoin(import.meta.dirname, '../coverage/babel/packages');
const PACKAGES_PATH = pathJoin(
import.meta.dirname,
'../coverage/babel/packages',
);
const OVERRIDES_PATH = pathJoin(import.meta.dirname, 'overrides');

// Copied from `@babel/helper-transform-fixture-test-runner`
Expand All @@ -52,7 +54,12 @@ async function updateDir(dirPath, options, hasChangedOptions) {
const dirFiles = [];

const filenames = { options: null, input: null, output: null, exec: null };
const overrides = { options: false, input: false, output: false, exec: false };
const overrides = {
options: false,
input: false,
output: false,
exec: false,
};

// Find files in dir
for (const file of files) {
Expand All @@ -66,44 +73,6 @@ async function updateDir(dirPath, options, hasChangedOptions) {
}
}

// Find override files
const overridesDirPath = pathJoin(`${OVERRIDES_PATH}${dirPath.slice(PACKAGES_PATH.length)}`);
let overrideFiles;
try {
overrideFiles = await readdir(overridesDirPath, { withFileTypes: true });
} catch (err) {
if (err?.code !== 'ENOENT') throw err;
}

if (overrideFiles) {
for (const file of overrideFiles) {
if (file.isDirectory()) continue;

const filename = file.name;
// `reason.txt` files are to document why override is used
if (filename === 'reason.txt') continue;

const ext = extname(filename),
type = filename.slice(0, -ext.length),
path = pathJoin(overridesDirPath, filename);

assert(Object.hasOwn(overrides, type), `Unexpected override file: ${path}`);

const originalPath = pathJoin(dirPath, filename);
if (filenames[type]) {
const originalFilename = filenames[type];
assert(originalFilename === filename, `Unmatched override file: ${path} (original: ${originalFilename})`);
await backupFile(originalPath);
}

filenames[type] = filename;
overrides[type] = true;
if (type === 'options') hasChangedOptions = true;

await copyFile(path, originalPath);
}
}

// Update options, save to file, and merge options with parent
if (filenames.options) {
const path = pathJoin(dirPath, filenames.options);
Expand All @@ -117,7 +86,11 @@ async function updateDir(dirPath, options, hasChangedOptions) {
}

// Run Babel with updated options/input
if (filenames.output && (hasChangedOptions || overrides.input) && !overrides.output) {
if (
filenames.output &&
(hasChangedOptions || overrides.input) &&
!overrides.output
) {
const inputPath = pathJoin(dirPath, filenames.input),
outputPath = pathJoin(dirPath, filenames.output);
await backupFile(outputPath);
Expand Down Expand Up @@ -165,7 +138,12 @@ function updateOptions(options) {
* @returns {undefined}
*/
async function transform(inputPath, outputPath, options) {
options = { ...options, configFile: false, babelrc: false, cwd: import.meta.dirname };
options = {
...options,
configFile: false,
babelrc: false,
cwd: import.meta.dirname,
};
delete options.SKIP_babel7plugins_babel8core;
delete options.minNodeVersion;

Expand All @@ -179,9 +157,11 @@ async function transform(inputPath, outputPath, options) {
return plugin;
}

if (options.presets) options.presets = options.presets.map(preset => prefixName(preset, 'preset'));
if (options.presets) {
options.presets = options.presets.map((preset) => prefixName(preset, 'preset'));
}

options.plugins = (options.plugins || []).map(plugin => prefixName(plugin, 'plugin'));
options.plugins = (options.plugins || []).map((plugin) => prefixName(plugin, 'plugin'));

let addExternalHelpersPlugin = true;
if (Object.hasOwn(options, 'externalHelpers')) {
Expand All @@ -190,7 +170,10 @@ async function transform(inputPath, outputPath, options) {
}

if (addExternalHelpersPlugin) {
options.plugins.push(['@babel/plugin-external-helpers', { helperVersion: EXTERNAL_HELPERS_VERSION }]);
options.plugins.push([
'@babel/plugin-external-helpers',
{ helperVersion: EXTERNAL_HELPERS_VERSION },
]);
}

const { code } = await transformFileAsync(inputPath, options);
Expand Down
Loading