Skip to content

Commit 780caa2

Browse files
committed
feat: generate codspeed_runner.go to call benchmarks
1 parent e6d681c commit 780caa2

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed

go-runner/src/builder/template.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package main
2+
3+
import (
4+
"io"
5+
"time"
6+
"reflect"
7+
codspeed "github.com/CodSpeedHQ/codspeed-go/compat/testing"
8+
codspeed_testing "github.com/CodSpeedHQ/codspeed-go/testing/testing"
9+
10+
// Import parent package containing the benchmarks
11+
{{#each benchmarks}}
12+
{{import_alias}} "{{module_path}}"
13+
{{/each}}
14+
)
15+
16+
type BenchmarkEntry struct {
17+
Name string
18+
Func func(b *codspeed.B)
19+
}
20+
21+
type corpusEntry = struct {
22+
Parent string
23+
Path string
24+
Data []byte
25+
Values []any
26+
Generation int
27+
IsSeed bool
28+
}
29+
30+
type simpleDeps struct{}
31+
32+
func (d simpleDeps) ImportPath() string { return "" }
33+
func (d simpleDeps) MatchString(pat, str string) (bool, error) { return true, nil }
34+
func (d simpleDeps) SetPanicOnExit0(bool) {}
35+
func (d simpleDeps) StartCPUProfile(io.Writer) error { return nil }
36+
func (d simpleDeps) StopCPUProfile() {}
37+
func (d simpleDeps) StartTestLog(io.Writer) {}
38+
func (d simpleDeps) StopTestLog() error { return nil }
39+
func (d simpleDeps) WriteProfileTo(string, io.Writer, int) error { return nil }
40+
41+
func (d simpleDeps) CoordinateFuzzing(
42+
fuzzTime time.Duration,
43+
fuzzN int64,
44+
minimizeTime time.Duration,
45+
minimizeN int64,
46+
parallel int,
47+
corpus []corpusEntry,
48+
types []reflect.Type,
49+
corpusDir,
50+
cacheDir string,
51+
) error {
52+
return nil
53+
}
54+
func (d simpleDeps) RunFuzzWorker(fn func(corpusEntry) error) error {
55+
return nil
56+
}
57+
func (d simpleDeps) ReadCorpus(dir string, types []reflect.Type) ([]corpusEntry, error) {
58+
return nil, nil
59+
}
60+
func (d simpleDeps) CheckCorpus(vals []any, types []reflect.Type) error {
61+
return nil
62+
}
63+
func (d simpleDeps) ResetCoverage() {}
64+
func (d simpleDeps) SnapshotCoverage() {}
65+
func (d simpleDeps) InitRuntimeCoverage() (mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) {
66+
return "", nil, nil
67+
}
68+
69+
func main() {
70+
var tests = []codspeed_testing.InternalTest{}
71+
var fuzzTargets = []codspeed_testing.InternalFuzzTarget{}
72+
var examples = []codspeed_testing.InternalExample{}
73+
var benchmarks = []codspeed_testing.InternalBenchmark{
74+
{{#each benchmarks}}
75+
{"{{name}}", {{qualified_name}}},
76+
{{/each}}
77+
}
78+
79+
m := codspeed_testing.MainStart(simpleDeps{}, tests, benchmarks, fuzzTargets, examples)
80+
m.Run()
81+
}

go-runner/src/builder/templater.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::fs;
2+
use std::path::{Path, PathBuf};
3+
4+
use handlebars::Handlebars;
5+
use serde::{Deserialize, Serialize};
6+
use tempfile::TempDir;
7+
8+
use crate::builder::{BenchmarkPackage, GoBenchmark};
9+
use crate::utils;
10+
use crate::{builder::patcher, prelude::*};
11+
12+
#[derive(Debug, Serialize, Deserialize)]
13+
struct TemplateData {
14+
benchmarks: Vec<GoBenchmark>,
15+
module_name: String,
16+
}
17+
18+
pub fn run(package: &BenchmarkPackage) -> anyhow::Result<(TempDir, PathBuf)> {
19+
// 1. Copy the whole module to a build directory
20+
let target_dir = TempDir::new()?;
21+
std::fs::create_dir_all(&target_dir).context("Failed to create target directory")?;
22+
utils::copy_dir_recursively(&package.module.dir, &target_dir)?;
23+
24+
// Get files that need to be renamed first
25+
let files = package
26+
.test_go_files
27+
.as_ref()
28+
.ok_or_else(|| anyhow::anyhow!("No test files found for package: {}", package.name))?;
29+
30+
// Calculate the relative path from module root to package directory
31+
let package_dir = Path::new(&package.dir);
32+
let module_dir = Path::new(&package.module.dir);
33+
let relative_package_path = package_dir.strip_prefix(module_dir).context(format!(
34+
"Package dir {:?} is not within module dir {:?}",
35+
package.dir, package.module.dir
36+
))?;
37+
debug!("Relative package path: {relative_package_path:?}");
38+
39+
// 2. Find benchmark files and patch their imports
40+
let files_to_patch = package
41+
.benchmarks
42+
.iter()
43+
.map(|bench| target_dir.path().join(&bench.file_path))
44+
.collect::<Vec<_>>();
45+
patcher::patch_imports(&target_dir, files_to_patch)?;
46+
47+
// 3. Rename the _test.go files to _codspeed.go
48+
for file in files {
49+
let old_path = target_dir.path().join(relative_package_path).join(file);
50+
let new_path = old_path.with_file_name(
51+
old_path
52+
.file_name()
53+
.unwrap()
54+
.to_string_lossy()
55+
.replace("_test", "_codspeed"),
56+
);
57+
58+
fs::rename(&old_path, &new_path)
59+
.context(format!("Failed to rename {old_path:?} to {new_path:?}"))?;
60+
}
61+
62+
// 4. Generate the codspeed/runner.go file using the template
63+
let mut handlebars = Handlebars::new();
64+
let template_content = include_str!("template.go");
65+
handlebars.register_template_string("main", template_content)?;
66+
67+
// import <alias> <mod_path>
68+
// { "<name>", <qualified_path> },
69+
let data = TemplateData {
70+
benchmarks: package.benchmarks.clone(),
71+
module_name: "codspeed_runner".into(),
72+
};
73+
let rendered = handlebars.render("main", &data)?;
74+
75+
let runner_path = target_dir
76+
.path()
77+
.join(relative_package_path)
78+
.join("codspeed/runner.go");
79+
fs::create_dir_all(
80+
target_dir
81+
.path()
82+
.join(relative_package_path)
83+
.join("codspeed"),
84+
)
85+
.context("Failed to create codspeed directory")?;
86+
fs::write(&runner_path, rendered).context("Failed to write runner.go file")?;
87+
88+
Ok((target_dir, runner_path))
89+
}

go-runner/src/utils.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use std::path::Path;
2+
use std::{fs, io};
3+
4+
pub fn copy_dir_recursively(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
5+
fs::create_dir_all(&dst)?;
6+
for entry in fs::read_dir(src)? {
7+
let entry = entry?;
8+
let ty = entry.file_type()?;
9+
if ty.is_dir() {
10+
if entry.file_name() == ".git" {
11+
continue;
12+
}
13+
14+
copy_dir_recursively(entry.path(), dst.as_ref().join(entry.file_name()))?;
15+
} else {
16+
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
17+
}
18+
}
19+
Ok(())
20+
}

0 commit comments

Comments
 (0)