summaryrefslogtreecommitdiff
path: root/ci-web/src/get-test-job.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ci-web/src/get-test-job.rs')
-rw-r--r--ci-web/src/get-test-job.rs218
1 files changed, 218 insertions, 0 deletions
diff --git a/ci-web/src/get-test-job.rs b/ci-web/src/get-test-job.rs
new file mode 100644
index 0000000..e3db5d3
--- /dev/null
+++ b/ci-web/src/get-test-job.rs
@@ -0,0 +1,218 @@
+extern crate libc;
+use std::fs::{OpenOptions, create_dir_all};
+use std::os::unix::fs::OpenOptionsExt;
+use std::io::ErrorKind;
+use std::path::Path;
+use std::process;
+mod lib;
+use lib::{Ktestrc, read_lines, ktestrc_read, git_get_commit};
+
+extern crate multimap;
+use multimap::MultiMap;
+use die::die;
+
+fn get_subtests(test_path: &str) -> Vec<String> {
+ let test_name = Path::new(test_path).file_stem();
+
+ if let Some(test_name) = test_name {
+ let test_name = test_name.to_string_lossy();
+
+ let output = std::process::Command::new(&test_path)
+ .arg("list-tests")
+ .output()
+ .expect(&format!("failed to execute process {:?} ", &test_path))
+ .stdout;
+ let output = String::from_utf8_lossy(&output);
+
+ output
+ .split_whitespace()
+ .map(|i| format!("{}.{}", test_name, i))
+ .collect()
+ } else {
+ Vec::new()
+ }
+}
+
+fn lockfile_exists(rc: &Ktestrc, commit: &str, subtest: &str, create: bool) -> bool {
+ fn test_or_create(lockfile: &Path, create: bool) -> bool {
+ if !create {
+ lockfile.exists()
+ } else {
+ let dir = lockfile.parent();
+ let r = create_dir_all(dir.unwrap());
+
+ if let Err(e) = r {
+ if e.kind() != ErrorKind::AlreadyExists {
+ die!("error creating {:?}: {}", dir, e);
+ }
+ }
+
+ let mut options = OpenOptions::new();
+ options.write(true);
+ options.custom_flags(libc::O_CREAT);
+ options.open(lockfile).is_ok()
+ }
+ }
+
+ let lockfile = rc.ci_output_dir.join(commit).join(subtest);
+ let mut exists = test_or_create(&lockfile, create);
+
+ if exists {
+ let timeout = std::time::Duration::new(3600, 0);
+ let now = std::time::SystemTime::now();
+ let metadata = std::fs::metadata(&lockfile).unwrap();
+
+ if metadata.len() == 0&&
+ metadata.is_file() &&
+ metadata.modified().unwrap() + timeout < now &&
+ std::fs::remove_file(&lockfile).is_ok() {
+ exists = test_or_create(&lockfile, create);
+ }
+ }
+
+ exists
+}
+
+struct TestJob {
+ branch: String,
+ commit: String,
+ age: usize,
+ test: String,
+ subtests: Vec<String>,
+}
+
+fn branch_get_next_test_job(rc: &Ktestrc, repo: &git2::Repository,
+ branch: &str, test_path: &str) -> Option<TestJob> {
+ let mut ret = TestJob {
+ branch: branch.to_string(),
+ commit: String::new(),
+ age: 0,
+ test: test_path.to_string(),
+ subtests: Vec::new(),
+ };
+
+ let subtests = get_subtests(test_path);
+
+ let mut walk = repo.revwalk().unwrap();
+ let reference = git_get_commit(&repo, branch.to_string());
+ if reference.is_err() {
+ eprintln!("branch {} not found", branch);
+ return None;
+ }
+ let reference = reference.unwrap();
+
+ if let Err(e) = walk.push(reference.id()) {
+ eprintln!("Error walking {}: {}", branch, e);
+ return None;
+ }
+
+ for commit in walk
+ .filter_map(|i| i.ok())
+ .filter_map(|i| repo.find_commit(i).ok()) {
+ let commit = commit.id().to_string();
+ ret.commit = commit.clone();
+
+ for subtest in subtests.iter() {
+ if !lockfile_exists(rc, &commit, subtest, false) {
+ ret.subtests.push(subtest.to_string());
+ if ret.subtests.len() > 20 {
+ break;
+ }
+ }
+ }
+
+ if !ret.subtests.is_empty() {
+ return Some(ret);
+ }
+
+ ret.age += 1;
+ }
+
+ None
+}
+
+fn get_best_test_job(rc: &Ktestrc, repo: &git2::Repository,
+ branch_tests: &MultiMap<String, String>) -> Option<TestJob> {
+ let mut ret: Option<TestJob> = None;
+
+ for (branch, testvec) in branch_tests.iter_all() {
+ for test in testvec {
+ let job = branch_get_next_test_job(rc, repo, branch, test);
+
+ if let Some(job) = job {
+ match &ret {
+ Some(r) => if r.age > job.age { ret = Some(job) },
+ None => ret = Some(job),
+ }
+ }
+ }
+ }
+
+ ret
+}
+
+fn create_job_lockfiles(rc: &Ktestrc, mut job: TestJob) -> Option<TestJob> {
+ job.subtests = job.subtests.iter()
+ .filter(|i| lockfile_exists(rc, &job.commit, &i, true))
+ .map(|i| i.to_string())
+ .collect();
+
+ if !job.subtests.is_empty() { Some(job) } else { None }
+}
+
+fn main() {
+ let ktestrc = ktestrc_read();
+
+ let repo = git2::Repository::open(&ktestrc.ci_linux_repo);
+ if let Err(e) = repo {
+ eprintln!("Error opening {:?}: {}", ktestrc.ci_linux_repo, e);
+ eprintln!("Please specify correct JOBSERVER_LINUX_DIR");
+ process::exit(1);
+ }
+ let repo = repo.unwrap();
+
+ let lines = read_lines(&ktestrc.ci_branches_to_test);
+ if let Err(e) = lines {
+ eprintln!("Error opening {:?}: {}", ktestrc.ci_branches_to_test, e);
+ eprintln!("Please specify correct JOBSERVER_BRANCHES_TO_TEST");
+ process::exit(1);
+ }
+ let lines = lines.unwrap();
+
+ let lines = lines.filter_map(|i| i.ok());
+
+ let mut branch_tests: MultiMap<String, String> = MultiMap::new();
+
+ for l in lines {
+ let l: Vec<_> = l.split_whitespace().take(2).collect();
+
+ if l.len() == 2 {
+ let branch = l[0];
+ let test = l[1];
+ branch_tests.insert(branch.to_string(), test.to_string());
+ }
+ }
+
+ let mut job: Option<TestJob>;
+
+ loop {
+ job = get_best_test_job(&ktestrc, &repo, &branch_tests);
+
+ if job.is_none() {
+ break;
+ }
+
+ job = create_job_lockfiles(&ktestrc, job.unwrap());
+ if job.is_some() {
+ break;
+ }
+ }
+
+ if let Some(job) = job {
+ print!("{} {} {}", job.branch, job.commit, job.test);
+ for t in job.subtests {
+ print!(" {}", t);
+ }
+ println!("");
+ }
+}