diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2022-09-21 23:08:33 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2022-09-22 15:43:43 -0400 |
commit | 06b890f10a0d602814086304953d5bc70eb22b62 (patch) | |
tree | 5ef2d4a980150fe5fb3de7f94b8a8964828f7294 | |
parent | a4c7c97110bc51346ee0409e620c9e0f81fbda24 (diff) |
get-test-job is now written in rustci-rust
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | ci-web/Cargo.toml | 12 | ||||
-rw-r--r-- | ci-web/commit-filter (renamed from ci/commit-filter) | 0 | ||||
-rw-r--r-- | ci-web/src/get-test-job.rs | 218 | ||||
-rw-r--r-- | ci-web/src/lib.rs | 90 | ||||
-rw-r--r-- | ci-web/src/main.rs | 124 | ||||
-rw-r--r-- | ci/_test-git-branch.sh | 2 | ||||
-rwxr-xr-x | ci/get-test-job.sh | 10 | ||||
-rw-r--r-- | lib/Makefile | 1 | ||||
-rw-r--r-- | lib/get-test-job.c | 385 |
9 files changed, 336 insertions, 506 deletions
diff --git a/ci-web/Cargo.toml b/ci-web/Cargo.toml index 1b8b1d6..c247c34 100644 --- a/ci-web/Cargo.toml +++ b/ci-web/Cargo.toml @@ -1,12 +1,20 @@ [package] -name = "ci-web" +name = "ci-cgi" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "get-test-job" +path = "src/get-test-job.rs" + +#[workspace] +#members = ["get-test-job", "ci-cgi"] [dependencies] cgi = "0.6" git2 = "0.14" querystring = "1.1.0" dirs = "4.0.0" +multimap = "0.8.3" +die = "0.2.0" +libc = "0.2" diff --git a/ci/commit-filter b/ci-web/commit-filter index c2abea9..c2abea9 100644 --- a/ci/commit-filter +++ b/ci-web/commit-filter 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!(""); + } +} diff --git a/ci-web/src/lib.rs b/ci-web/src/lib.rs new file mode 100644 index 0000000..9a8b40d --- /dev/null +++ b/ci-web/src/lib.rs @@ -0,0 +1,90 @@ +use std::fs::File; +use std::io::{self, BufRead}; +use std::path::{Path, PathBuf}; +extern crate dirs; + +pub fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> +where P: AsRef<Path>, { + let file = File::open(filename)?; + Ok(io::BufReader::new(file).lines()) +} + +pub fn git_get_commit(repo: &git2::Repository, reference: String) -> Result<git2::Commit, git2::Error> { + let r = repo.revparse_single(&reference); + if let Err(e) = r { + eprintln!("Error from resolve_reference_from_short_name {} in {}: {}", reference, repo.path().display(), e); + return Err(e); + } + + let r = r.unwrap().peel_to_commit(); + if let Err(e) = r { + eprintln!("Error from peel_to_commit {} in {}: {}", reference, repo.path().display(), e); + return Err(e); + } + r +} + +pub struct Ktestrc { + pub ci_linux_repo: PathBuf, + pub ci_output_dir: PathBuf, + pub ci_branches_to_test: PathBuf, +} + +pub fn ktestrc_read() -> Ktestrc { + let mut ktestrc = Ktestrc { + ci_linux_repo: PathBuf::new(), + ci_output_dir: PathBuf::new(), + ci_branches_to_test: PathBuf::new(), + }; + + if let Some(home) = dirs::home_dir() { + ktestrc.ci_branches_to_test = home.join("BRANCHES-TO-TEST"); + } + + fn ktestrc_get(rc: &'static str, var: &'static str) -> Option<PathBuf> { + let cmd = format!(". {}; echo -n ${}", rc, var); + + let output = std::process::Command::new("/usr/bin/env") + .arg("bash") + .arg("-c") + .arg(&cmd) + .output() + .expect("failed to execute process /bin/sh") + .stdout; + + let output = String::from_utf8_lossy(&output); + let output = output.trim(); + + if !output.is_empty() { + Some(PathBuf::from(output)) + } else { + None + } + } + + if let Some(v) = ktestrc_get("/etc/ktestrc", "JOBSERVER_LINUX_DIR") { + ktestrc.ci_linux_repo = v; + } + + if let Some(v) = ktestrc_get("/etc/ktestrc", "JOBSERVER_OUTPUT_DIR") { + ktestrc.ci_output_dir = v; + } + + if let Some(v) = ktestrc_get("/etc/ktestrc", "JOBSERVER_BRANCHES_TO_TEST") { + ktestrc.ci_branches_to_test = v; + } + + if let Some(v) = ktestrc_get("$HOME/.ktestrc", "JOBSERVER_LINUX_DIR") { + ktestrc.ci_linux_repo = v; + } + + if let Some(v) = ktestrc_get("$HOME/.ktestrc", "JOBSERVER_OUTPUT_DIR") { + ktestrc.ci_output_dir = v; + } + + if let Some(v) = ktestrc_get("$HOME/.ktestrc", "JOBSERVER_BRANCHES_TO_TEST") { + ktestrc.ci_branches_to_test = v; + } + + ktestrc +} diff --git a/ci-web/src/main.rs b/ci-web/src/main.rs index 230689a..a18678e 100644 --- a/ci-web/src/main.rs +++ b/ci-web/src/main.rs @@ -1,14 +1,14 @@ -use git2::Repository; use std::fmt::Write; use std::fs::File; -use std::io::{self, Read, BufRead}; -use std::path::{Path, PathBuf}; -use std::process::Command; +use std::io::Read; +use std::path::Path; extern crate cgi; -extern crate dirs; extern crate querystring; -const COMMIT_FILTER: &str = include_str!("../../ci/commit-filter"); +mod lib; +use lib::*; + +const COMMIT_FILTER: &str = include_str!("../commit-filter"); const STYLESHEET: &str = "/bootstrap.min.css"; fn read_file(f: &Path) -> Option<String> { @@ -18,93 +18,6 @@ fn read_file(f: &Path) -> Option<String> { Some(ret) } -fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> -where P: AsRef<Path>, { - let file = File::open(filename)?; - Ok(io::BufReader::new(file).lines()) -} - -fn git_get_commit(repo: &git2::Repository, reference: String) -> Result<git2::Commit, git2::Error> { - let r = repo.revparse_single(&reference); - if let Err(e) = r { - eprintln!("Error from resolve_reference_from_short_name {} in {}: {}", reference, repo.path().display(), e); - return Err(e); - } - - let r = r.unwrap().peel_to_commit(); - if let Err(e) = r { - eprintln!("Error from peel_to_commit {} in {}: {}", reference, repo.path().display(), e); - return Err(e); - } - r -} - -struct Ktestrc { - ci_linux_repo: PathBuf, - ci_output_dir: PathBuf, - ci_branches_to_test: PathBuf, -} - -fn ktestrc_read() -> Ktestrc { - let mut ktestrc = Ktestrc { - ci_linux_repo: PathBuf::new(), - ci_output_dir: PathBuf::new(), - ci_branches_to_test: PathBuf::new(), - }; - - if let Some(home) = dirs::home_dir() { - ktestrc.ci_branches_to_test = home.join("BRANCHES-TO-TEST"); - } - - fn ktestrc_get(rc: &'static str, var: &'static str) -> Option<PathBuf> { - let cmd = format!(". {}; echo -n ${}", rc, var); - - let output = Command::new("sh") - .arg("-c") - .arg(&cmd) - .output() - .expect("failed to execute process /bin/sh") - .stdout; - - let output = String::from_utf8_lossy(&output); - let output = output.trim(); - - if !output.is_empty() { - Some(PathBuf::from(output)) - } else { - None - } - } - - if let Some(v) = ktestrc_get("/etc/ktestrc", "JOBSERVER_LINUX_DIR") { - ktestrc.ci_linux_repo = v; - } - - if let Some(v) = ktestrc_get("/etc/ktestrc", "JOBSERVER_OUTPUT_DIR") { - ktestrc.ci_output_dir = v; - } - - if let Some(v) = ktestrc_get("/etc/ktestrc", "JOBSERVER_BRANCHES_TO_TEST") { - ktestrc.ci_branches_to_test = v; - } - - /* - if let Some(v) = ktestrc_get("$HOME/.ktestrc", "JOBSERVER_LINUX_DIR") { - ktestrc.ci_linux_repo = v; - } - - if let Some(v) = ktestrc_get("$HOME/.ktestrc", "JOBSERVER_OUTPUT_DIR") { - ktestrc.ci_output_dir = v; - } - - if let Some(v) = ktestrc_get("$HOME/.ktestrc", "JOBSERVER_BRANCHES_TO_TEST") { - ktestrc.ci_branches_to_test = v; - } - */ - - ktestrc -} - #[derive(PartialEq)] enum TestStatus { InProgress, @@ -325,21 +238,18 @@ fn ci_list_branches(ci: &Ci) -> cgi::Response { writeln!(&mut out, "<body>").unwrap(); writeln!(&mut out, "<table class=\"table\">").unwrap(); - if let Ok(lines) = read_lines(&ci.ktestrc.ci_branches_to_test) { - let branches: std::collections::HashSet<_> = lines - .filter_map(|i| i.ok()) - .map(|i| if let Some(w) = i.split_whitespace().nth(0) { Some(String::from(w)) } else { None }) - .filter_map(|i| i) - .collect(); + let lines = read_lines(&ci.ktestrc.ci_branches_to_test).unwrap(); + let branches: std::collections::HashSet<_> = lines + .filter_map(|i| i.ok()) + .map(|i| if let Some(w) = i.split_whitespace().nth(0) { Some(String::from(w)) } else { None }) + .filter_map(|i| i) + .collect(); - let mut branches: Vec<_> = branches.iter().collect(); - branches.sort(); + let mut branches: Vec<_> = branches.iter().collect(); + branches.sort(); - for b in branches { - writeln!(&mut out, "<tr> <th> <a href={}?log={}>{}</a> </th> </tr>", ci.script_name, b, b).unwrap(); - } - } else { - writeln!(&mut out, "(BRANCHES-TO-TEST not found)").unwrap(); + for b in branches { + writeln!(&mut out, "<tr> <th> <a href={}?log={}>{}</a> </th> </tr>", ci.script_name, b, b).unwrap(); } writeln!(&mut out, "</table>").unwrap(); @@ -383,7 +293,7 @@ cgi::cgi_main! {|request: cgi::Request| -> cgi::Response { ktestrc.ci_branches_to_test.as_os_str())); } - let repo = Repository::open(&ktestrc.ci_linux_repo).unwrap(); + let repo = git2::Repository::open(&ktestrc.ci_linux_repo).unwrap(); let ci = Ci { ktestrc: ktestrc, diff --git a/ci/_test-git-branch.sh b/ci/_test-git-branch.sh index 05886a7..c28aa20 100644 --- a/ci/_test-git-branch.sh +++ b/ci/_test-git-branch.sh @@ -30,7 +30,7 @@ sync_git_repos() echo "Getting test job" -TEST_JOB=( $(ssh $JOBSERVER get-test-job.sh) ) +TEST_JOB=( $(ssh $JOBSERVER get-test-job) ) BRANCH=${TEST_JOB[0]} COMMIT=${TEST_JOB[1]} diff --git a/ci/get-test-job.sh b/ci/get-test-job.sh deleted file mode 100755 index bdcb888..0000000 --- a/ci/get-test-job.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -[[ -f ~/.ktestrc ]] && . ~/.ktestrc - -cd $JOBSERVER_HOME/linux -flock --nonblock .git_fetch.lock git fetch --all > /dev/null - -make -C ~/ktest/lib get-test-job 1>&2 - -~/ktest/lib/get-test-job -b ~/BRANCHES-TO-TEST -o $JOBSERVER_OUTPUT_DIR diff --git a/lib/Makefile b/lib/Makefile index 62b5682..f776db5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,5 +1,4 @@ CFLAGS=-Wall -g -O2 supervisor: -get-test-job: lwip-connect: LDLIBS=-llwipv6 diff --git a/lib/get-test-job.c b/lib/get-test-job.c deleted file mode 100644 index a5245b7..0000000 --- a/lib/get-test-job.c +++ /dev/null @@ -1,385 +0,0 @@ -#define _GNU_SOURCE - -#include <ctype.h> -#include <getopt.h> -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -#define HAVE_STATEMENT_EXPR 1 -#include "darray/darray.h" - -static char *outdir = NULL; -static char *branches_to_test = NULL; -static bool verbose = false; - -#define die(msg, ...) \ -do { \ - fprintf(stderr, msg "\n", ##__VA_ARGS__); \ - exit(EXIT_FAILURE); \ -} while (0) - -static char *mprintf(const char *fmt, ...) -{ - va_list args; - char *str; - int ret; - - va_start(args, fmt); - ret = vasprintf(&str, fmt, args); - va_end(args); - - if (ret < 0) - die("insufficient memory"); - - return str; -} - -static void strim(char *line) -{ - char *p = line; - - while (isalnum(*p)) - p++; - *p = 0; -} - -static char *test_basename(const char *str) -{ - char *p = strrchr(str, '/'); - char *ret = strdup(p ? p + 1 : str); - - p = strstr(ret, ".ktest"); - if (p) - *p = 0; - return ret; -} - -typedef darray(char *) strings; - -static void strings_free(strings *strs) -{ - char **s; - - darray_foreach(s, *strs) - free(*s); - darray_free(*strs); - - memset(strs, 0, sizeof(*strs)); -} - -typedef struct { - char *branch; - char *commit; - unsigned age; - char *test; - strings subtests; -} test_job; - -static void test_job_free(test_job *job) -{ - free(job->branch); - free(job->commit); - free(job->test); - strings_free(&job->subtests); - memset(job, 0, sizeof(*job)); -} - -static void test_job_print(test_job job) -{ - fprintf(stderr, "%s %s %s age %u subtests", - job.branch, job.commit, job.test, job.age); - - char **subtest; - darray_foreach(subtest, job.subtests) - fprintf(stderr, " %s", *subtest); - fprintf(stderr, "\n"); -} - -static strings get_subtests(char *test_path) -{ - darray_char output; - strings ret; - size_t bytes_read; - - darray_init(output); - darray_init(ret); - - if (verbose) - fprintf(stderr, "Getting subtests for %s\n", test_path); - - char *cmd = mprintf("%s list-tests", test_path); - FILE *f = popen(cmd, "r"); - free(cmd); - - if (!f) - die("error executing %s", test_path); - - do { - darray_make_room(output, 4096); - - bytes_read = fread(output.item + output.size, - 1, 4095, f); - output.size += bytes_read; - } while (bytes_read); - - pclose(f); - - output.item[output.size] = '\0'; - - char *subtest, *p = output.item; - while ((subtest = strtok(p, " \t\n"))) { - darray_push(ret, strdup(subtest)); - p = NULL; - } - - darray_free(output); - - if (darray_empty(ret)) - die("error getting subtests from %s", test_path); - - return ret; -} - -static char *slashes_to_dots(const char *str) -{ - char *p, *ret = strdup(str); - - while ((p = strchr(ret, '/'))) - *p = '.'; - - return ret; -} - -static bool __lockfile_exists(const char *commitdir, - const char *testdir, - const char *lockfile, - bool create) -{ - if (!create) { - return access(lockfile, F_OK) == 0; - } else { - bool exists; - - if (mkdir(commitdir, 0755) < 0 && errno != EEXIST) - die("error creating %s", commitdir); - - if (mkdir(testdir, 0755) < 0 && errno != EEXIST) - die("error creating %s", testdir); - - int fd = open(lockfile, O_RDWR|O_CREAT|O_EXCL, 0644); - exists = fd < 0; - if (!exists) - close(fd); - - return exists; - } -} - -static bool lockfile_exists(const char *commit, - const char *test_path, - const char *subtest, - bool create) -{ - char *test_name = test_basename(test_path); - char *subtest_mangled = slashes_to_dots(subtest); - char *commitdir = mprintf("%s/%s", outdir, commit); - char *testdir = mprintf("%s/%s.%s", commitdir, test_name, subtest_mangled); - char *lockfile = mprintf("%s/status", testdir); - struct timeval now; - struct stat statbuf; - bool exists; - - gettimeofday(&now, NULL); - - exists = __lockfile_exists(commitdir, testdir, lockfile, create); - - if (exists && - !stat(lockfile, &statbuf) && - !statbuf.st_size && - S_ISREG(statbuf.st_mode) && - statbuf.st_ctime + 60 * 60 < now.tv_sec && - !unlink(lockfile)) { - fprintf(stderr, "Deleting stale test job %s %s %s (%lu minutes old)\n", - commit, test_name, subtest, - (now.tv_sec - statbuf.st_ctime) / 60); - exists = false; - } - - free(lockfile); - free(testdir); - free(commitdir); - free(subtest_mangled); - free(test_name); - - return exists; -} - -static test_job branch_get_next_test_job(char *branch, - char *test_path, - strings subtests) -{ - char *cmd = mprintf("git log --pretty=format:%H %s", branch); - FILE *commits = popen(cmd, "r"); - char *commit = NULL; - size_t n = 0; - ssize_t len; - test_job ret; - - memset(&ret, 0, sizeof(ret)); - - while ((len = getline(&commit, &n, commits)) >= 0) { - strim(commit); - - char **subtest; - darray_foreach(subtest, subtests) - if (!lockfile_exists(commit, test_path, *subtest, false)) { - darray_push(ret.subtests, strdup(*subtest)); - if (darray_size(ret.subtests) > 20) - break; - } - - if (!darray_empty(ret.subtests)) { - ret.branch = strdup(branch); - ret.commit = strdup(commit); - ret.test = strdup(test_path); - goto success; - } - - ret.age++; - } - fprintf(stderr, "error looking up commits on branch %s\n", branch); -success: - pclose(commits); - free(commit); - free(cmd); - return ret; -} - -static test_job get_best_test_job() -{ - FILE *branches = fopen(branches_to_test, "r"); - char *line = NULL; - size_t n = 0; - ssize_t len; - test_job best; - - memset(&best, 0, sizeof(best)); - - if (!branches) - die("error opening %s: %m", branches_to_test); - - while ((len = getline(&line, &n, branches)) >= 0) { - char *branch = strtok(line, " \t\n"); - char *test_path = strtok(NULL, " \t\n"); - - if (!branch || !test_path) - continue; - - if (verbose) - fprintf(stderr, "get_best_test_job: checking branch %s test %s\n", - branch, test_path); - - strings subtests = get_subtests(test_path); - - test_job job = branch_get_next_test_job(branch, test_path, subtests); - - strings_free(&subtests); - - if (!best.branch || job.age < best.age) { - test_job_free(&best); - best = job; - } else { - test_job_free(&job); - } - } - - if (!best.branch) - die("Nothing found"); - - if (verbose) { - fprintf(stderr, "get_best_test_job: best "); - test_job_print(best); - } - - fclose(branches); - free(line); - return best; -} - -void usage(void) -{ - puts("get-test-job: get a test job and create lockfile\n" - "Usage: get-test-job [OPTIONS]\n" - "\n" - "Options\n" - " -b file List of branches to test and tests to run\n" - " -o dir Directory for tests results\n" - " -v Verbose\n" - " -h Display this help and exit"); - exit(EXIT_SUCCESS); -} - -int main(int argc, char *argv[]) -{ - int opt; - test_job job; - strings subtests; - char **subtest; - - darray_init(subtests); - memset(&job, 0, sizeof(job)); - - while ((opt = getopt(argc, argv, "b:o:vh")) != -1) { - switch (opt) { - case 'b': - branches_to_test = strdup(optarg); - break; - case 'o': - outdir = strdup(optarg); - break; - case 'v': - verbose = true; - break; - case 'h': - usage(); - exit(EXIT_SUCCESS); - case '?': - usage(); - exit(EXIT_FAILURE); - } - } - - if (!branches_to_test || !outdir) - die("required argument missing"); - - do { - test_job_free(&job); - job = get_best_test_job(); - - darray_free(subtests); - darray_init(subtests); - - darray_foreach(subtest, job.subtests) - if (!lockfile_exists(job.commit, job.test, *subtest, true)) - darray_push(subtests, *subtest); - } while (darray_empty(subtests)); - - printf("%s %s %s", job.branch, job.commit, job.test); - darray_foreach(subtest, subtests) - printf(" %s", *subtest); - printf("\n"); - - test_job_free(&job); - darray_free(subtests); - free(outdir); - free(branches_to_test); -} |