From bda6ec7e892a6a46fb79dcbabc64912879ec8827 Mon Sep 17 00:00:00 2001 From: Langston Barrett Date: Tue, 14 Mar 2023 23:23:55 -0400 Subject: [PATCH] WIP: Automation for adding tests --- Cargo.lock | 11 +++++ Cargo.toml | 1 + grabber/Cargo.toml | 14 ++++++ grabber/src/github.rs | 107 ++++++++++++++++++++++++++++++++++++++++++ grabber/src/main.rs | 73 ++++++++++++++++++++++++++++ 5 files changed, 206 insertions(+) create mode 100644 grabber/Cargo.toml create mode 100644 grabber/src/github.rs create mode 100644 grabber/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 446329b6..0f7b2a11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,6 +345,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "glacier-grabber" +version = "0.1.0" +dependencies = [ + "glacier", + "once_cell", + "regex", + "reqwest", + "serde", +] + [[package]] name = "h2" version = "0.3.15" diff --git a/Cargo.toml b/Cargo.toml index 198af5ab..565c2e3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ tempfile = "3.1.0" [workspace] members = [ "autofix", + "grabber", "labeler" ] diff --git a/grabber/Cargo.toml b/grabber/Cargo.toml new file mode 100644 index 00000000..4e836322 --- /dev/null +++ b/grabber/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "glacier-grabber" +version = "0.1.0" +authors = ["Langston Barrett "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +glacier = "0.1.0" +once_cell = { workspace = true } +regex = "1" +reqwest = { workspace = true } +serde = { workspace = true } diff --git a/grabber/src/github.rs b/grabber/src/github.rs new file mode 100644 index 00000000..7b175adb --- /dev/null +++ b/grabber/src/github.rs @@ -0,0 +1,107 @@ +use once_cell::sync::{Lazy, OnceCell}; +use regex::Regex; +use reqwest::blocking::Client; +use serde::{Deserialize, Serialize}; +use std::env::{var, VarError}; + +static CLIENT: Lazy = Lazy::new(|| { + Client::builder() + .user_agent("rust-lang/glacier") + .build() + .unwrap() +}); + +pub(crate) struct Config { + token: String, +} + +impl Config { + pub(crate) fn from_env(on_glacier: bool) -> Result { + Ok(Self { + token: if on_glacier { + var("GITHUB_TOKEN")? + } else { + var("GRAB_TOKEN")? + }, + }) + } +} + +pub(crate) fn get_labeled_issues( + config: &Config, + repo: &str, + label_name: String, +) -> Result, reqwest::Error> { + let url = format!("https://api.github.com/repos/{repo}/issues?labels={label_name}&state=open"); + + let mut issues: Vec = CLIENT + .get(&url) + .bearer_auth(&config.token) + .send()? + .error_for_status()? + .json()?; + + let pages = get_result_length(config, &url).unwrap(); + + if pages > 1 { + for i in 2..=pages { + let url = format!( + "https://api.github.com/repos/rust-lang/rust/issues?labels={label_name}&state=open&page={i}" + ); + let mut paged_issues: Vec = CLIENT + .get(&url) + .bearer_auth(&config.token) + .send()? + .error_for_status()? + .json()?; + + issues.append(&mut paged_issues); + } + } + + Ok(issues) +} + +fn get_result_length(config: &Config, url: &str) -> Result> { + static RE_LAST_PAGE: OnceCell = OnceCell::new(); + let res = CLIENT.get(url).bearer_auth(&config.token).send()?; + + if res.status().is_success() { + if let Some(link) = res.headers().get("Link") { + let link_string = String::from_utf8(link.as_bytes().to_vec()).unwrap(); + let re_last_page = + RE_LAST_PAGE.get_or_init(|| Regex::new(r#"page=([0-9]+)>; rel="last""#).unwrap()); + let last_page_number = re_last_page + .captures(&link_string) + .unwrap() + .get(1) + .unwrap() + .as_str(); + let pages: usize = last_page_number.parse().unwrap(); + + Ok(pages) + } else { + Ok(0) + } + } else { + Ok(0) + } +} + +#[derive(Serialize, Debug)] +pub(crate) struct Labels { + pub(crate) labels: Vec, +} + +#[derive(Deserialize, Debug)] +pub(crate) struct Issue { + pub(crate) number: usize, + pub(crate) body: String, +} + +#[derive(Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub(crate) enum IssueState { + Open, + Closed, +} diff --git a/grabber/src/main.rs b/grabber/src/main.rs new file mode 100644 index 00000000..79eebb60 --- /dev/null +++ b/grabber/src/main.rs @@ -0,0 +1,73 @@ +use glacier::rayon::iter::ParallelIterator; + +mod github; + +fn main() { + let config = github::Config::from_env(false).unwrap(); + + let mut tested_issue_list = glacier::discover("./ices") + .unwrap() + .into_iter() + .map(|ice| ice.id()) + .collect::>(); + tested_issue_list.sort_unstable(); + tested_issue_list.dedup(); + + let mut untesteds = + crate::github::get_labeled_issues(&config, "rust-lang/rust", "I-ICE".to_string()).unwrap(); + untesteds.retain(|i| !tested_issue_list.contains(&i.number)); + for untested in untesteds { + let mut reproduction = Vec::new(); + let mut in_code_section = false; + let mut in_code = false; + let mut has_main = false; + for line in untested.body.lines() { + if in_code { + if line.starts_with("```") { + in_code = false; + continue; + } + if line.starts_with("fn main") { + has_main = true; + } + reproduction.push(line); + } + if line.starts_with("### Code") { + in_code_section = true; + } else if line.starts_with('#') && in_code_section { + in_code_section = false; + } + if (line.starts_with("```rust") || line.starts_with("```Rust")) && in_code_section { + in_code = true; + } + } + if reproduction.is_empty() { + continue; + } + if !has_main { + reproduction.push(""); + reproduction.push("fn main() {}"); + } + std::fs::write( + format!("./ices/{}.rs", untested.number), + reproduction.join("\n"), + ) + .unwrap(); + } + + let failed = glacier::test_all() + .unwrap() + .filter(|res| { + if let Ok(test) = res { + test.outcome() != glacier::Outcome::ICEd + } else { + true + } + }) + .collect::, _>>() + .unwrap(); + + for result in &failed { + std::fs::remove_file(result.path()).unwrap(); + } +}