oxc/tasks/common/src/test_file.rs
overlookmotel f0cbbbe28c
ci: build each benchmark only with deps it needs (#3221)
This PR builds on #3201 to further speed up the benchmarks and reduce CI
time.

* Build and run each benchmark as separate job (like before).
* But now each bench is only built with the dependencies it needs.
* For linter benchmarks, build benchmark in 1 job (like #3201 does).
* Run each linter fixture in a separate job as they're slow.

This reduces total time to complete benchmarks from between 6m-7m to
~4m40s.

All the individual jobs complete in under 1m30s, except for building
linter benchmark which takes 2m30s. So there won't be the problem of
blocking the CI queue that there was before.

NB: I did try this before, and didn't see a benefit. But I realized
today what I was doing wrong - it only works once the caches are
populated by a previous run on main branch.

So the CI times in this PR won't look good, but once it's merged to
main, it will take effect. Here it is running on main branch of my fork:

https://github.com/overlookmotel/oxc/actions/runs/9030511348

I also added a step to delete the temp artefacts which aren't needed
once the run has completed.
2024-05-10 22:01:24 +08:00

121 lines
3.7 KiB
Rust

use std::{fmt, fs::read_to_string, str::FromStr};
use crate::project_root;
use crate::request::agent;
pub struct TestFiles {
files: Vec<TestFile>,
}
impl Default for TestFiles {
fn default() -> Self {
Self::new()
}
}
impl TestFiles {
pub fn new() -> Self {
let files = Self::get_libs().into_iter().map(|file| TestFile::new(&file)).collect();
Self { files }
}
pub fn files(&self) -> &Vec<TestFile> {
&self.files
}
fn get_libs() -> Vec<String> {
let root = project_root();
read_to_string(root.join("./tasks/libs.txt"))
.unwrap()
.lines()
.map(ToString::to_string)
.collect::<Vec<_>>()
}
pub fn minimal() -> Self {
let files = Self::get_libs()
.into_iter()
.filter(|name| ["react", "antd", "typescript"].iter().any(|f| name.contains(f)))
.map(|file| TestFile::new(&file))
.collect();
Self { files }
}
pub fn complicated() -> Self {
let files = Self::complicated_urls().into_iter().map(TestFile::new).collect();
Self { files }
}
pub fn complicated_one(index: usize) -> Self {
let url = Self::complicated_urls()[index];
let file = TestFile::new(url);
Self { files: vec![file] }
}
fn complicated_urls() -> [&'static str; 5] {
[
// TypeScript syntax (2.81MB)
"https://raw.githubusercontent.com/microsoft/TypeScript/v5.3.3/src/compiler/checker.ts",
// Real world app tsx (1.0M)
"https://raw.githubusercontent.com/oxc-project/benchmark-files/main/cal.com.tsx",
// Real world content-heavy app jsx (3K)
"https://raw.githubusercontent.com/oxc-project/benchmark-files/main/RadixUIAdoptionSection.jsx",
// Heavy with classes (554K)
"https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.269/build/pdf.mjs",
// ES5 (3.9M)
"https://cdn.jsdelivr.net/npm/antd@5.12.5/dist/antd.js",
]
}
}
pub struct TestFile {
pub url: String,
pub file_name: String,
pub source_text: String,
}
impl TestFile {
/// # Errors
/// # Panics
pub fn new(url: &str) -> Self {
let (file_name, source_text) = Self::get_source_text(url).unwrap();
Self { url: url.to_string(), file_name, source_text }
}
/// # Errors
/// # Panics
pub fn get_source_text(lib: &str) -> Result<(String, String), String> {
let url = url::Url::from_str(lib).map_err(err_to_string)?;
let segments = url.path_segments().ok_or_else(|| "lib url has no segments".to_string())?;
let filename = segments.last().ok_or_else(|| "lib url has no segments".to_string())?;
let file = project_root().join("target").join(filename);
if let Ok(code) = std::fs::read_to_string(&file) {
println!("[{filename}] - using [{}]", file.display());
Ok((filename.to_string(), code))
} else {
println!("[{filename}] - Downloading [{lib}] to [{}]", file.display());
match agent().get(lib).call() {
Ok(response) => {
let mut reader = response.into_reader();
let _drop = std::fs::remove_file(&file);
let mut writer = std::fs::File::create(&file).map_err(err_to_string)?;
let _drop = std::io::copy(&mut reader, &mut writer);
std::fs::read_to_string(&file)
.map_err(err_to_string)
.map(|code| (filename.to_string(), code))
}
Err(e) => Err(format!("{e:?}")),
}
}
}
}
fn err_to_string<E: fmt::Debug>(e: E) -> String {
format!("{e:?}")
}