From 24535a796de56ff6da4b10b5792fd72a52e9e9ce Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Wed, 1 May 2024 20:15:59 +0530 Subject: [PATCH 01/17] feature: watch mode for patterns test --- Cargo.lock | 118 ++++++++++++++++++++--- crates/cli/Cargo.toml | 2 + crates/cli/src/commands/patterns.rs | 5 +- crates/cli/src/commands/patterns_test.rs | 57 ++++++++++- crates/cli/src/commands/plumbing.rs | 3 +- crates/cli/src/flags.rs | 2 +- 6 files changed, 169 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b7ba2784..fff875236 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,6 +1060,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + [[package]] name = "flate2" version = "1.0.28" @@ -1126,6 +1138,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures" version = "0.3.30" @@ -1329,19 +1350,9 @@ dependencies = [ "serde", ] -[[package]] -name = "grit_cache" -version = "0.1.0" -dependencies = [ - "anyhow", - "marzano-gritmodule", - "marzano-util", - "tokio", -] - [[package]] name = "grit-wasm-bindings" -version = "0.0.0" +version = "0.1.0" dependencies = [ "ai_builtins", "anyhow", @@ -1359,6 +1370,16 @@ dependencies = [ "web-tree-sitter-sg", ] +[[package]] +name = "grit_cache" +version = "0.1.0" +dependencies = [ + "anyhow", + "marzano-gritmodule", + "marzano-util", + "tokio", +] + [[package]] name = "h2" version = "0.3.24" @@ -1636,6 +1657,26 @@ dependencies = [ "log", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "insta" version = "1.36.1" @@ -1756,6 +1797,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1966,6 +2027,8 @@ dependencies = [ "marzano-test-utils", "marzano-util", "marzano_messenger", + "notify", + "notify-debouncer-mini", "openssl", "opentelemetry", "opentelemetry-otlp", @@ -2028,7 +2091,7 @@ dependencies = [ [[package]] name = "marzano-gritmodule" -version = "0.0.0" +version = "0.1.0" dependencies = [ "anyhow", "git2", @@ -2239,6 +2302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] @@ -2280,6 +2344,36 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-mini" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43" +dependencies = [ + "crossbeam-channel", + "log", + "notify", +] + [[package]] name = "ntest" version = "0.9.1" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f1ea04365..7f8bcd97a 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -69,6 +69,8 @@ opentelemetry_sdk = { version = "0.21.1", optional = true, features = [ tracing-opentelemetry = { version = "0.22.0", optional = true, default-features = false } tracing = { version = "0.1.40", default-features = false, features = [] } +notify = "6.1.1" +notify-debouncer-mini = "0.4.1" [target.'cfg(not(windows))'.dependencies] openssl = { version = "0.10", features = ["vendored"] } diff --git a/crates/cli/src/commands/patterns.rs b/crates/cli/src/commands/patterns.rs index ca7b46dab..afcde5dea 100644 --- a/crates/cli/src/commands/patterns.rs +++ b/crates/cli/src/commands/patterns.rs @@ -36,7 +36,7 @@ pub enum PatternCommands { Describe(PatternsDescribeArgs), } -#[derive(Args, Debug, Serialize)] +#[derive(Args, Debug, Serialize, Clone)] pub struct PatternsTestArgs { /// Regex of a specific pattern to test #[clap(long = "filter")] @@ -53,6 +53,9 @@ pub struct PatternsTestArgs { /// Update expected test outputs #[clap(long = "update")] pub update: bool, + /// Enable watch mode on .grit dir + #[clap(long = "watch")] + pub watch: bool, } #[derive(Args, Debug, Serialize)] diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 478a9ade6..64f385fc6 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -27,11 +27,15 @@ use marzano_messenger::emit::{get_visibility, VisibilityLevels}; use super::patterns::PatternsTestArgs; use anyhow::{anyhow, bail, Context as _, Result}; +use std::{path::Path, time::Duration}; + +use notify::{self, RecursiveMode}; +use notify_debouncer_mini::{new_debouncer_opt, Config}; pub async fn get_marzano_pattern_test_results( patterns: Vec, libs: &PatternsDirectory, - args: PatternsTestArgs, + args: &PatternsTestArgs, output: OutputFormat, ) -> Result<()> { let cwd = std::env::current_dir()?; @@ -241,7 +245,6 @@ pub async fn get_marzano_pattern_test_results( bail!("Output format not supported for this command"); } } - Ok(()) } @@ -276,7 +279,55 @@ pub(crate) async fn run_patterns_test( bail!("No testable patterns found. To test a pattern, make sure it is defined in .grit/grit.yaml or a .md file in your .grit/patterns directory."); } info!("Found {} testable patterns.", testable_patterns.len(),); - get_marzano_pattern_test_results(testable_patterns, &libs, arg, flags.into()).await + get_marzano_pattern_test_results(testable_patterns, &libs, &arg, flags.clone().into()).await?; + + if arg.watch { + enable_watch_mode(arg, flags); + } + Ok(()) +} + +fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) -> () { + let path = Path::new(".grit"); + // setup debouncer + let (tx, rx) = std::sync::mpsc::channel(); + // notify backend configuration + let backend_config = notify::Config::default().with_poll_interval(Duration::from_millis(10)); + // debouncer configuration + let debouncer_config = Config::default() + .with_timeout(Duration::from_millis(10)) + .with_notify_config(backend_config); + // select backend via fish operator, here PollWatcher backend + let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx).unwrap(); + + debouncer + .watcher() + .watch(path, RecursiveMode::Recursive) + .unwrap(); + log::info!("\nWatch mode enabled on path: {}", path.display()); + + // event pocessing + for result in rx { + match result { + Ok(event) => { + log::info!( + "\nFile modified: {:?}\nRe-running patterns test...\n", + event + ); + + let mut arg = cmd_arg.clone(); + let flags = cmd_flags.clone(); + arg.watch = false; //avoid creating infinite watchers + + tokio::spawn(async move { + let _ = run_patterns_test(arg, flags).await; + }); + } + Err(error) => { + log::error!("Error {error:?}") + } + } + } } #[derive(Debug, Serialize)] diff --git a/crates/cli/src/commands/plumbing.rs b/crates/cli/src/commands/plumbing.rs index fbca62173..f893987df 100644 --- a/crates/cli/src/commands/plumbing.rs +++ b/crates/cli/src/commands/plumbing.rs @@ -263,9 +263,10 @@ pub(crate) async fn run_plumbing( get_marzano_pattern_test_results( patterns, &libs, - PatternsTestArgs { + &PatternsTestArgs { update: false, verbose: false, + watch: false, filter: None, exclude: vec![], }, diff --git a/crates/cli/src/flags.rs b/crates/cli/src/flags.rs index 5e993246d..7f8033d95 100644 --- a/crates/cli/src/flags.rs +++ b/crates/cli/src/flags.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Default, clap::Args)] +#[derive(Debug, Default, clap::Args, Clone)] pub struct GlobalFormatFlags { /// Enable JSON output, only supported on some commands #[arg(long, global = true, conflicts_with = "jsonl")] From 3d6bd318f0770f9b893a6342845290b1ef698675 Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Tue, 21 May 2024 00:48:06 +0530 Subject: [PATCH 02/17] feat: re-run pattern test for changed/affected patterns only --- crates/cli/src/commands/mod.rs | 2 +- crates/cli/src/commands/patterns_test.rs | 211 ++++++++++++++++++- crates/core/src/pattern_compiler/compiler.rs | 12 +- 3 files changed, 210 insertions(+), 15 deletions(-) diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index 112dbabb5..269959bc3 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -346,7 +346,7 @@ pub async fn run_command() -> Result<()> { Commands::Parse(arg) => run_parse(arg, app.format_flags, None).await, Commands::Patterns(arg) => match arg.patterns_commands { PatternCommands::List(arg) => run_patterns_list(arg, app.format_flags).await, - PatternCommands::Test(arg) => run_patterns_test(arg, app.format_flags).await, + PatternCommands::Test(arg) => run_patterns_test(arg, app.format_flags, None).await, PatternCommands::Edit(arg) => run_patterns_edit(arg).await, PatternCommands::Describe(arg) => run_patterns_describe(arg).await, }, diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 64f385fc6..16551fc9c 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -3,6 +3,7 @@ use dashmap::{DashMap, ReadOnlyView}; use log::{debug, info}; use marzano_core::api::MatchResult; +use marzano_core::constants::DEFAULT_FILE_NAME; use marzano_gritmodule::config::{GritPatternSample, GritPatternTestInfo}; use marzano_gritmodule::formatting::format_rich_files; use marzano_gritmodule::markdown::replace_sample_in_md_file; @@ -12,7 +13,9 @@ use marzano_gritmodule::testing::{ GritTestResultState, MismatchInfo, SampleTestResult, }; -use marzano_language::target_language::PatternLanguage; +use marzano_language::{ + grit_parser::MarzanoGritParser, language::Tree, target_language::PatternLanguage, +}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use serde::Serialize; @@ -27,8 +30,13 @@ use marzano_messenger::emit::{get_visibility, VisibilityLevels}; use super::patterns::PatternsTestArgs; use anyhow::{anyhow, bail, Context as _, Result}; +use std::collections::BTreeMap; +use std::path::PathBuf; use std::{path::Path, time::Duration}; +use grit_util::{traverse, Ast, AstNode, Order}; +use marzano_core::analysis::is_multifile; +use marzano_core::pattern_compiler::compiler::{defs_to_filenames, DefsToFilenames}; use notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer_opt, Config}; @@ -251,10 +259,20 @@ pub async fn get_marzano_pattern_test_results( pub(crate) async fn run_patterns_test( arg: PatternsTestArgs, flags: GlobalFormatFlags, + watch_mode_args: Option>, ) -> Result<()> { let (mut patterns, _) = resolve_from_cwd(&Source::Local).await?; let libs = get_grit_files_from_cwd().await?; + if !watch_mode_args.is_none() { + let watch_mode_args = watch_mode_args.unwrap(); + //filter out patterns that are not affected under watchmode + patterns = patterns + .into_iter() + .filter(|p| watch_mode_args.contains(&p.local_name)) + .collect::>() + } + if arg.filter.is_some() { let filter = arg.filter.as_ref().unwrap(); let regex = regex::Regex::new(filter)?; @@ -304,23 +322,28 @@ fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) -> .watcher() .watch(path, RecursiveMode::Recursive) .unwrap(); - log::info!("\nWatch mode enabled on path: {}", path.display()); + log::info!("\n[Watch Mode] enabled on path: {}", path.display()); // event pocessing for result in rx { match result { Ok(event) => { - log::info!( - "\nFile modified: {:?}\nRe-running patterns test...\n", - event - ); + let modified_file_path = event.get(0).unwrap().path.clone(); + log::info!("\n[Watch Mode] File modified: {:?}", modified_file_path); let mut arg = cmd_arg.clone(); let flags = cmd_flags.clone(); arg.watch = false; //avoid creating infinite watchers + tokio::task::spawn(async move { + let affected_patterns_names = + get_affected_patterns(modified_file_path).await.unwrap(); + + info!( + "[Watch Mode] Patterns changed/affected : {:?}\n[Watch Mode] Re-running test for changed/affected patterns...\n", + affected_patterns_names + ); - tokio::spawn(async move { - let _ = run_patterns_test(arg, flags).await; + let _ = run_patterns_test(arg, flags, Some(affected_patterns_names)).await; }); } Err(error) => { @@ -330,6 +353,178 @@ fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) -> } } +async fn get_affected_patterns(modified_file_path: PathBuf) -> Result> { + let modified_file_path_string = modified_file_path.into_os_string().into_string().unwrap(); + + let (patterns, _) = resolve_from_cwd(&Source::Local).await?; + let affected_patterns = collect_testable_patterns(patterns.clone()) + .into_iter() + .filter(|p| { + p.config.path.is_some() && p.config.path.as_ref().unwrap() == &modified_file_path_string + }) + .collect::>(); + + let mut affected_patterns_names = >::new(); + for affected_pattern in &affected_patterns { + affected_patterns_names.push(affected_pattern.local_name.clone().unwrap()); + } + let mut affected_patterns_dependents_names = >::new(); + + let grit_files = get_grit_files_from_cwd().await?; + let current_dir = std::env::current_dir()?; + let resolver = GritModuleResolver::new(current_dir.to_str().unwrap()); + + for p in patterns { + let body = format!("{}()", p.local_name); + let lang = PatternLanguage::get_language(&p.body); + let grit_files = grit_files.get_language_directory_or_default(lang)?; + let rich_pattern = resolver + .make_pattern(&body, Some(p.local_name.to_string())) + .unwrap(); + let src = rich_pattern.body; + let mut parser = MarzanoGritParser::new()?; + let src_tree = parser.parse_file(&src, Some(Path::new(DEFAULT_FILE_NAME)))?; + let root = src_tree.root_node(); + let is_multifile = is_multifile(&root, &grit_files, &mut parser)?; + + let dependents = get_dependents_of_affected_patterns( + &grit_files, + &src, + &mut parser, + !is_multifile, + &affected_patterns_names, + )?; + + for d in dependents { + if !affected_patterns_dependents_names.contains(&d) { + affected_patterns_dependents_names.push(d); + } + } + } + + for depen in affected_patterns_dependents_names { + if !affected_patterns_names.contains(&depen) { + affected_patterns_names.push(depen); + } + } + Ok(affected_patterns_names) +} + +pub(crate) fn get_dependents_of_affected_patterns( + libs: &BTreeMap, + src: &str, + parser: &mut MarzanoGritParser, + will_autowrap: bool, + affected_patterns: &Vec, +) -> Result> { + let mut dependents = >::new(); + let node_like = "nodeLike"; + let predicate_call = "predicateCall"; + + let tree = parser.parse_file(src, Some(Path::new(DEFAULT_FILE_NAME)))?; + + let DefsToFilenames { + patterns: pattern_file, + predicates: predicate_file, + functions: function_file, + foreign_functions: foreign_file, + } = defs_to_filenames(libs, parser, tree.root_node())?; + + let mut traversed_stack = >::new(); + + // gross but necessary due to running these patterns before and after each file + let mut stack: Vec = if will_autowrap { + let before_each_file = "before_each_file()"; + let before_tree = + parser.parse_file(before_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; + let after_each_file = "after_each_file()"; + let after_tree = parser.parse_file(after_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; + + vec![tree, before_tree, after_tree] + } else { + vec![tree] + }; + while let Some(tree) = stack.pop() { + let root = tree.root_node(); + let cursor = root.walk(); + + for n in traverse(cursor, Order::Pre).filter(|n| { + n.node.is_named() && (n.node.kind() == node_like || n.node.kind() == predicate_call) + }) { + let name = n + .child_by_field_name("name") + .ok_or_else(|| anyhow!("missing name of nodeLike"))?; + let name = name.text()?; + let name = name.trim().to_string(); + + if affected_patterns.contains(&name) { + while let Some(e) = traversed_stack.pop() { + dependents.push(e); + } + } + if n.node.kind() == node_like { + if let Some(tree) = find_child_tree_definition( + &pattern_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + if let Some(tree) = find_child_tree_definition( + &function_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + if let Some(tree) = find_child_tree_definition( + &foreign_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + } else if n.node.kind() == predicate_call { + if let Some(tree) = find_child_tree_definition( + &predicate_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + } + } + } + Ok(dependents) +} + +fn find_child_tree_definition( + files: &BTreeMap, + parser: &mut MarzanoGritParser, + libs: &BTreeMap, + traversed_stack: &mut Vec, + name: &str, +) -> Result> { + if let Some(file_name) = files.get(name) { + if !traversed_stack.contains(&name.to_string()) { + if let Some(file_body) = libs.get(file_name) { + traversed_stack.push(name.to_owned()); + let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?; + return Ok(Some(tree)); + } + } + }; + Ok(None) +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] enum TestOutcome { diff --git a/crates/core/src/pattern_compiler/compiler.rs b/crates/core/src/pattern_compiler/compiler.rs index 476a96c0a..3190772ee 100644 --- a/crates/core/src/pattern_compiler/compiler.rs +++ b/crates/core/src/pattern_compiler/compiler.rs @@ -372,14 +372,14 @@ pub(crate) fn get_definitions( foreign_function_definitions, }) } -struct DefsToFilenames { - patterns: BTreeMap, - predicates: BTreeMap, - functions: BTreeMap, - foreign_functions: BTreeMap, +pub struct DefsToFilenames { + pub patterns: BTreeMap, + pub predicates: BTreeMap, + pub functions: BTreeMap, + pub foreign_functions: BTreeMap, } -fn defs_to_filenames( +pub fn defs_to_filenames( libs: &BTreeMap, parser: &mut MarzanoGritParser, root: NodeWithSource, From 44fcd31005af7eb6b139ce144b93bf3b9d3f6351 Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Sat, 25 May 2024 10:45:40 +0530 Subject: [PATCH 03/17] refactor: ignore watch mode on external libs and sepraton of concerns filter, compiler code --- crates/cli/src/commands/patterns_test.rs | 207 +++++-------------- crates/core/src/pattern_compiler/compiler.rs | 127 +++++++++++- 2 files changed, 170 insertions(+), 164 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 16551fc9c..fdeb0f766 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -4,7 +4,7 @@ use log::{debug, info}; use marzano_core::api::MatchResult; use marzano_core::constants::DEFAULT_FILE_NAME; -use marzano_gritmodule::config::{GritPatternSample, GritPatternTestInfo}; +use marzano_gritmodule::config::{GritPatternSample, GritPatternTestInfo, ResolvedGritDefinition}; use marzano_gritmodule::formatting::format_rich_files; use marzano_gritmodule::markdown::replace_sample_in_md_file; use marzano_gritmodule::patterns_directory::PatternsDirectory; @@ -13,9 +13,7 @@ use marzano_gritmodule::testing::{ GritTestResultState, MismatchInfo, SampleTestResult, }; -use marzano_language::{ - grit_parser::MarzanoGritParser, language::Tree, target_language::PatternLanguage, -}; +use marzano_language::{grit_parser::MarzanoGritParser, target_language::PatternLanguage}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use serde::Serialize; @@ -30,13 +28,11 @@ use marzano_messenger::emit::{get_visibility, VisibilityLevels}; use super::patterns::PatternsTestArgs; use anyhow::{anyhow, bail, Context as _, Result}; -use std::collections::BTreeMap; -use std::path::PathBuf; use std::{path::Path, time::Duration}; -use grit_util::{traverse, Ast, AstNode, Order}; +use grit_util::Ast; use marzano_core::analysis::is_multifile; -use marzano_core::pattern_compiler::compiler::{defs_to_filenames, DefsToFilenames}; +use marzano_core::pattern_compiler::compiler::get_dependents_of_target_patterns; use notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer_opt, Config}; @@ -259,20 +255,11 @@ pub async fn get_marzano_pattern_test_results( pub(crate) async fn run_patterns_test( arg: PatternsTestArgs, flags: GlobalFormatFlags, - watch_mode_args: Option>, + watch_mode_arg: Option, ) -> Result<()> { let (mut patterns, _) = resolve_from_cwd(&Source::Local).await?; let libs = get_grit_files_from_cwd().await?; - if !watch_mode_args.is_none() { - let watch_mode_args = watch_mode_args.unwrap(); - //filter out patterns that are not affected under watchmode - patterns = patterns - .into_iter() - .filter(|p| watch_mode_args.contains(&p.local_name)) - .collect::>() - } - if arg.filter.is_some() { let filter = arg.filter.as_ref().unwrap(); let regex = regex::Regex::new(filter)?; @@ -291,7 +278,19 @@ pub(crate) async fn run_patterns_test( } } - let testable_patterns = collect_testable_patterns(patterns); + let mut testable_patterns = collect_testable_patterns(patterns.clone()); + if watch_mode_arg.is_some() { + let modified_file_path = watch_mode_arg.unwrap(); + //filter out patterns that are not affected under watchmode + let affected_patterns_names = + get_affected_patterns(modified_file_path, patterns, &libs, &testable_patterns).unwrap(); + + info!("[Watch Mode] Patterns changed/affected : {:?}\n[Watch Mode] Re-running test for changed/affected patterns...\n",affected_patterns_names); + testable_patterns = testable_patterns + .into_iter() + .filter(|p| affected_patterns_names.contains(p.local_name.as_ref().unwrap())) + .collect::>() + } if testable_patterns.is_empty() { bail!("No testable patterns found. To test a pattern, make sure it is defined in .grit/grit.yaml or a .md file in your .grit/patterns directory."); @@ -305,8 +304,9 @@ pub(crate) async fn run_patterns_test( Ok(()) } -fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) -> () { +fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) { let path = Path::new(".grit"); + let ignore_path = [".grit/.gritmodules", ".gitignore", ".grit/*.log"]; // setup debouncer let (tx, rx) = std::sync::mpsc::channel(); // notify backend configuration @@ -327,23 +327,28 @@ fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) -> // event pocessing for result in rx { match result { - Ok(event) => { - let modified_file_path = event.get(0).unwrap().path.clone(); + Ok(event) => 'event_block: { + let modified_file_path = event + .get(0) + .unwrap() + .path + .clone() + .into_os_string() + .into_string() + .unwrap(); + //temorary fix, until notify crate adds support for ignoring paths + for path in &ignore_path { + if modified_file_path.contains(path) { + break 'event_block; + } + } log::info!("\n[Watch Mode] File modified: {:?}", modified_file_path); let mut arg = cmd_arg.clone(); let flags = cmd_flags.clone(); arg.watch = false; //avoid creating infinite watchers tokio::task::spawn(async move { - let affected_patterns_names = - get_affected_patterns(modified_file_path).await.unwrap(); - - info!( - "[Watch Mode] Patterns changed/affected : {:?}\n[Watch Mode] Re-running test for changed/affected patterns...\n", - affected_patterns_names - ); - - let _ = run_patterns_test(arg, flags, Some(affected_patterns_names)).await; + let _ = run_patterns_test(arg, flags, Some(modified_file_path)).await; }); } Err(error) => { @@ -353,14 +358,16 @@ fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) -> } } -async fn get_affected_patterns(modified_file_path: PathBuf) -> Result> { - let modified_file_path_string = modified_file_path.into_os_string().into_string().unwrap(); - - let (patterns, _) = resolve_from_cwd(&Source::Local).await?; - let affected_patterns = collect_testable_patterns(patterns.clone()) - .into_iter() +fn get_affected_patterns( + modified_file_path: String, + patterns: Vec, + libs: &PatternsDirectory, + testable_patterns: &Vec, +) -> Result> { + let affected_patterns = testable_patterns + .iter() .filter(|p| { - p.config.path.is_some() && p.config.path.as_ref().unwrap() == &modified_file_path_string + p.config.path.is_some() && p.config.path.as_ref().unwrap() == &modified_file_path }) .collect::>(); @@ -370,14 +377,13 @@ async fn get_affected_patterns(modified_file_path: PathBuf) -> Result>::new(); - let grit_files = get_grit_files_from_cwd().await?; let current_dir = std::env::current_dir()?; let resolver = GritModuleResolver::new(current_dir.to_str().unwrap()); for p in patterns { let body = format!("{}()", p.local_name); let lang = PatternLanguage::get_language(&p.body); - let grit_files = grit_files.get_language_directory_or_default(lang)?; + let libs = libs.get_language_directory_or_default(lang)?; let rich_pattern = resolver .make_pattern(&body, Some(p.local_name.to_string())) .unwrap(); @@ -385,10 +391,10 @@ async fn get_affected_patterns(modified_file_path: PathBuf) -> Result Result, - src: &str, - parser: &mut MarzanoGritParser, - will_autowrap: bool, - affected_patterns: &Vec, -) -> Result> { - let mut dependents = >::new(); - let node_like = "nodeLike"; - let predicate_call = "predicateCall"; - - let tree = parser.parse_file(src, Some(Path::new(DEFAULT_FILE_NAME)))?; - - let DefsToFilenames { - patterns: pattern_file, - predicates: predicate_file, - functions: function_file, - foreign_functions: foreign_file, - } = defs_to_filenames(libs, parser, tree.root_node())?; - - let mut traversed_stack = >::new(); - - // gross but necessary due to running these patterns before and after each file - let mut stack: Vec = if will_autowrap { - let before_each_file = "before_each_file()"; - let before_tree = - parser.parse_file(before_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; - let after_each_file = "after_each_file()"; - let after_tree = parser.parse_file(after_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; - - vec![tree, before_tree, after_tree] - } else { - vec![tree] - }; - while let Some(tree) = stack.pop() { - let root = tree.root_node(); - let cursor = root.walk(); - - for n in traverse(cursor, Order::Pre).filter(|n| { - n.node.is_named() && (n.node.kind() == node_like || n.node.kind() == predicate_call) - }) { - let name = n - .child_by_field_name("name") - .ok_or_else(|| anyhow!("missing name of nodeLike"))?; - let name = name.text()?; - let name = name.trim().to_string(); - - if affected_patterns.contains(&name) { - while let Some(e) = traversed_stack.pop() { - dependents.push(e); - } - } - if n.node.kind() == node_like { - if let Some(tree) = find_child_tree_definition( - &pattern_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - if let Some(tree) = find_child_tree_definition( - &function_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - if let Some(tree) = find_child_tree_definition( - &foreign_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - } else if n.node.kind() == predicate_call { - if let Some(tree) = find_child_tree_definition( - &predicate_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - } - } - } - Ok(dependents) -} - -fn find_child_tree_definition( - files: &BTreeMap, - parser: &mut MarzanoGritParser, - libs: &BTreeMap, - traversed_stack: &mut Vec, - name: &str, -) -> Result> { - if let Some(file_name) = files.get(name) { - if !traversed_stack.contains(&name.to_string()) { - if let Some(file_body) = libs.get(file_name) { - traversed_stack.push(name.to_owned()); - let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?; - return Ok(Some(tree)); - } - } - }; - Ok(None) -} - #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] enum TestOutcome { diff --git a/crates/core/src/pattern_compiler/compiler.rs b/crates/core/src/pattern_compiler/compiler.rs index 98bfd1cfa..1d7b58dfb 100644 --- a/crates/core/src/pattern_compiler/compiler.rs +++ b/crates/core/src/pattern_compiler/compiler.rs @@ -372,14 +372,14 @@ pub(crate) fn get_definitions( foreign_function_definitions, }) } -pub struct DefsToFilenames { - pub patterns: BTreeMap, - pub predicates: BTreeMap, - pub functions: BTreeMap, - pub foreign_functions: BTreeMap, +struct DefsToFilenames { + patterns: BTreeMap, + predicates: BTreeMap, + functions: BTreeMap, + foreign_functions: BTreeMap, } -pub fn defs_to_filenames( +fn defs_to_filenames( libs: &BTreeMap, parser: &mut MarzanoGritParser, root: NodeWithSource, @@ -567,6 +567,121 @@ fn find_definition_if_exists( Ok(None) } +pub fn get_dependents_of_target_patterns( + libs: &BTreeMap, + src: &str, + parser: &mut MarzanoGritParser, + will_autowrap: bool, + target_patterns: &Vec, +) -> Result> { + let mut dependents = >::new(); + let node_like = "nodeLike"; + let predicate_call = "predicateCall"; + + let tree = parser.parse_file(src, Some(Path::new(DEFAULT_FILE_NAME)))?; + + let DefsToFilenames { + patterns: pattern_file, + predicates: predicate_file, + functions: function_file, + foreign_functions: foreign_file, + } = defs_to_filenames(libs, parser, tree.root_node())?; + + let mut traversed_stack = >::new(); + + // gross but necessary due to running these patterns before and after each file + let mut stack: Vec = if will_autowrap { + let before_each_file = "before_each_file()"; + let before_tree = + parser.parse_file(before_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; + let after_each_file = "after_each_file()"; + let after_tree = parser.parse_file(after_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; + + vec![tree, before_tree, after_tree] + } else { + vec![tree] + }; + while let Some(tree) = stack.pop() { + let root = tree.root_node(); + let cursor = root.walk(); + + for n in traverse(cursor, Order::Pre).filter(|n| { + n.node.is_named() && (n.node.kind() == node_like || n.node.kind() == predicate_call) + }) { + let name = n + .child_by_field_name("name") + .ok_or_else(|| anyhow!("missing name of nodeLike"))?; + let name = name.text()?; + let name = name.trim().to_string(); + + if target_patterns.contains(&name) { + while let Some(e) = traversed_stack.pop() { + dependents.push(e); + } + } + if n.node.kind() == node_like { + if let Some(tree) = find_child_tree_definition( + &pattern_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + if let Some(tree) = find_child_tree_definition( + &function_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + if let Some(tree) = find_child_tree_definition( + &foreign_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + } else if n.node.kind() == predicate_call { + if let Some(tree) = find_child_tree_definition( + &predicate_file, + parser, + libs, + &mut traversed_stack, + &name, + )? { + stack.push(tree); + } + } + } + } + Ok(dependents) +} + +fn find_child_tree_definition( + files: &BTreeMap, + parser: &mut MarzanoGritParser, + libs: &BTreeMap, + traversed_stack: &mut Vec, + name: &str, +) -> Result> { + if let Some(file_name) = files.get(name) { + if !traversed_stack.contains(&name.to_string()) { + if let Some(file_body) = libs.get(file_name) { + traversed_stack.push(name.to_owned()); + let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?; + return Ok(Some(tree)); + } + } + }; + Ok(None) +} + pub struct CompilationResult { pub compilation_warnings: AnalysisLogs, pub problem: Problem, From d5e045980a7cb865fbb0417fc7cd281d1e06121f Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Mon, 3 Jun 2024 19:14:35 +0530 Subject: [PATCH 04/17] handle case when reading patterns from single yaml file --- crates/gritmodule/src/parser.rs | 14 ++++++++++++++ crates/gritmodule/src/resolver.rs | 4 ++-- crates/gritmodule/src/yaml.rs | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/gritmodule/src/parser.rs b/crates/gritmodule/src/parser.rs index 61e98e371..b5c2d0873 100644 --- a/crates/gritmodule/src/parser.rs +++ b/crates/gritmodule/src/parser.rs @@ -9,6 +9,7 @@ use crate::{ fetcher::ModuleRepo, markdown::get_patterns_from_md, searcher::find_repo_root_from, + yaml::get_patterns_from_yaml, }; use anyhow::{Context, Result}; use marzano_util::rich_path::RichFile; @@ -22,6 +23,8 @@ pub enum PatternFileExt { Grit, #[serde(rename = "markdown")] Md, + #[serde(rename = "yaml")] + Yaml, } impl fmt::Display for PatternFileExt { @@ -29,6 +32,7 @@ impl fmt::Display for PatternFileExt { match self { PatternFileExt::Grit => write!(f, "grit"), PatternFileExt::Md => write!(f, "md"), + PatternFileExt::Yaml => write!(f, "yaml"), } } } @@ -39,6 +43,7 @@ impl PatternFileExt { match ext { "grit" => Some(PatternFileExt::Grit), "md" => Some(PatternFileExt::Md), + "yaml" => Some(PatternFileExt::Yaml), _ => None, } } @@ -66,6 +71,14 @@ impl PatternFileExt { ) }) } + PatternFileExt::Yaml => { + get_patterns_from_yaml(file, source_module, root).with_context(|| { + format!( + "Failed to parse yaml pattern {}", + extract_relative_file_path(file, root) + ) + }) + } } } @@ -73,6 +86,7 @@ impl PatternFileExt { match self { PatternFileExt::Grit => "grit", PatternFileExt::Md => "md", + PatternFileExt::Yaml => "yaml", } } } diff --git a/crates/gritmodule/src/resolver.rs b/crates/gritmodule/src/resolver.rs index f803d78a4..b224a1a8c 100644 --- a/crates/gritmodule/src/resolver.rs +++ b/crates/gritmodule/src/resolver.rs @@ -533,7 +533,7 @@ async fn get_grit_files_for_module( Some(config) => { if let Some(module) = module { let repo_root = find_repo_root_from(repo_path).await?; - get_patterns_from_yaml(&config, module, &repo_root)? + get_patterns_from_yaml(&config, &Some(module.to_owned()), &repo_root)? } else { vec![] } @@ -584,7 +584,7 @@ async fn resolve_patterns_for_module( Some(config) => { if let Some(module) = module { let repo_root = find_repo_root_from(repo_path).await?; - get_patterns_from_yaml(&config, module, &repo_root)? + get_patterns_from_yaml(&config, &Some(module.to_owned()), &repo_root)? } else { vec![] } diff --git a/crates/gritmodule/src/yaml.rs b/crates/gritmodule/src/yaml.rs index 123cecfbd..14ccde9f3 100644 --- a/crates/gritmodule/src/yaml.rs +++ b/crates/gritmodule/src/yaml.rs @@ -39,7 +39,7 @@ pub fn get_grit_config(source: &str, source_path: &str) -> Result { pub fn get_patterns_from_yaml( file: &RichFile, - source_module: &ModuleRepo, + source_module: &Option, root: &Option, ) -> Result> { let mut config = get_grit_config(&file.content, &extract_relative_file_path(file, root))?; From 0a2fb53aea4b5d30cb4a0babd717d3cb45ab8802 Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Thu, 6 Jun 2024 23:15:29 +0530 Subject: [PATCH 05/17] watch mode made efficient and handled patten delete case --- crates/cli/src/commands/mod.rs | 4 +- crates/cli/src/commands/patterns_test.rs | 208 ++++++++++++++----- crates/core/src/pattern_compiler/compiler.rs | 2 +- crates/gritmodule/src/config.rs | 8 +- crates/gritmodule/src/testing.rs | 26 ++- 5 files changed, 183 insertions(+), 65 deletions(-) diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index baf88d50d..41e9312c8 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -373,7 +373,7 @@ async fn run_command() -> Result<()> { Commands::Parse(arg) => run_parse(arg, app.format_flags, None).await, Commands::Patterns(arg) => match arg.patterns_commands { PatternCommands::List(arg) => run_patterns_list(arg, app.format_flags).await, - PatternCommands::Test(arg) => run_patterns_test(arg, app.format_flags, None).await, + PatternCommands::Test(arg) => run_patterns_test(arg, app.format_flags).await, PatternCommands::Edit(arg) => run_patterns_edit(arg).await, PatternCommands::Describe(arg) => run_patterns_describe(arg).await, }, @@ -549,7 +549,7 @@ pub async fn run_command_with_tracing() -> Result<()> { if let Err(ref e) = res { if let Some(good) = e.downcast_ref::() { if let Some(msg) = &good.message { - println!("{}", msg); + log::info!("{}", msg); } std::process::exit(1); } diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index fdeb0f766..dd08bd145 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -4,13 +4,13 @@ use log::{debug, info}; use marzano_core::api::MatchResult; use marzano_core::constants::DEFAULT_FILE_NAME; -use marzano_gritmodule::config::{GritPatternSample, GritPatternTestInfo, ResolvedGritDefinition}; +use marzano_gritmodule::config::{GritPatternSample, GritPatternTestInfo}; use marzano_gritmodule::formatting::format_rich_files; use marzano_gritmodule::markdown::replace_sample_in_md_file; use marzano_gritmodule::patterns_directory::PatternsDirectory; use marzano_gritmodule::testing::{ - collect_testable_patterns, get_sample_name, has_output_mismatch, test_pattern_sample, - GritTestResultState, MismatchInfo, SampleTestResult, + collect_testable_patterns, get_grit_pattern_test_info, get_sample_name, has_output_mismatch, + test_pattern_sample, GritTestResultState, MismatchInfo, SampleTestResult, }; use marzano_language::{grit_parser::MarzanoGritParser, target_language::PatternLanguage}; @@ -28,11 +28,13 @@ use marzano_messenger::emit::{get_visibility, VisibilityLevels}; use super::patterns::PatternsTestArgs; use anyhow::{anyhow, bail, Context as _, Result}; +use std::collections::HashMap; use std::{path::Path, time::Duration}; use grit_util::Ast; use marzano_core::analysis::is_multifile; -use marzano_core::pattern_compiler::compiler::get_dependents_of_target_patterns; +use marzano_core::pattern_compiler::compiler::get_dependents_of_target_patterns_by_traversal_from_src; +use marzano_gritmodule::searcher::collect_from_file; use notify::{self, RecursiveMode}; use notify_debouncer_mini::{new_debouncer_opt, Config}; @@ -42,6 +44,11 @@ pub async fn get_marzano_pattern_test_results( args: &PatternsTestArgs, output: OutputFormat, ) -> Result<()> { + if patterns.is_empty() { + bail!("No testable patterns found. To test a pattern, make sure it is defined in .grit/grit.yaml or a .md file in your .grit/patterns directory."); + } + info!("Found {} testable patterns.", patterns.len()); + let cwd = std::env::current_dir()?; let resolver = GritModuleResolver::new(cwd.to_str().unwrap()); @@ -255,7 +262,6 @@ pub async fn get_marzano_pattern_test_results( pub(crate) async fn run_patterns_test( arg: PatternsTestArgs, flags: GlobalFormatFlags, - watch_mode_arg: Option, ) -> Result<()> { let (mut patterns, _) = resolve_from_cwd(&Source::Local).await?; let libs = get_grit_files_from_cwd().await?; @@ -278,35 +284,25 @@ pub(crate) async fn run_patterns_test( } } - let mut testable_patterns = collect_testable_patterns(patterns.clone()); - if watch_mode_arg.is_some() { - let modified_file_path = watch_mode_arg.unwrap(); - //filter out patterns that are not affected under watchmode - let affected_patterns_names = - get_affected_patterns(modified_file_path, patterns, &libs, &testable_patterns).unwrap(); + let testable_patterns = collect_testable_patterns(patterns.clone()); - info!("[Watch Mode] Patterns changed/affected : {:?}\n[Watch Mode] Re-running test for changed/affected patterns...\n",affected_patterns_names); - testable_patterns = testable_patterns - .into_iter() - .filter(|p| affected_patterns_names.contains(p.local_name.as_ref().unwrap())) - .collect::>() - } - - if testable_patterns.is_empty() { - bail!("No testable patterns found. To test a pattern, make sure it is defined in .grit/grit.yaml or a .md file in your .grit/patterns directory."); - } - info!("Found {} testable patterns.", testable_patterns.len(),); - get_marzano_pattern_test_results(testable_patterns, &libs, &arg, flags.clone().into()).await?; + get_marzano_pattern_test_results(testable_patterns.clone(), &libs, &arg, flags.clone().into()) + .await?; if arg.watch { - enable_watch_mode(arg, flags); + let _ = enable_watch_mode(testable_patterns, &libs, &arg, flags.clone().into()).await; } Ok(()) } -fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) { +async fn enable_watch_mode( + testable_patterns: Vec, + libs: &PatternsDirectory, + args: &PatternsTestArgs, + output: OutputFormat, +) -> Result<()> { let path = Path::new(".grit"); - let ignore_path = [".grit/.gritmodules", ".gitignore", ".grit/*.log"]; + let ignore_path = [".grit/.gritmodules", ".grit/.gitignore", ".log"]; // setup debouncer let (tx, rx) = std::sync::mpsc::channel(); // notify backend configuration @@ -324,6 +320,11 @@ fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) { .unwrap(); log::info!("\n[Watch Mode] enabled on path: {}", path.display()); + let testable_patterns_map = testable_patterns + .iter() + .map(|p| (p.local_name.clone().unwrap(), p.clone())) + .collect::>(); + // event pocessing for result in rx { match result { @@ -343,49 +344,122 @@ fn enable_watch_mode(cmd_arg: PatternsTestArgs, cmd_flags: GlobalFormatFlags) { } } log::info!("\n[Watch Mode] File modified: {:?}", modified_file_path); + let (modified_patterns, deleted_patterns) = + get_modified_and_deleted_patterns(&modified_file_path, &testable_patterns) + .await?; + if modified_patterns.is_empty() && deleted_patterns.is_empty() { + log::info!("[Watch Mode] No patterns changed.\n"); + break 'event_block; + } + let modified_patterns_names = modified_patterns + .iter() + .map(|p| p.local_name.as_ref().unwrap()) + .collect::>(); + let deleted_patterns_names = deleted_patterns + .iter() + .map(|p| p.local_name.as_ref().unwrap()) + .collect::>(); + log::info!( + "[Watch Mode] Modified patterns: {:?}", + modified_patterns_names + ); + log::info!( + "[Watch Mode] Deleted patterns: {:?}", + deleted_patterns_names + ); - let mut arg = cmd_arg.clone(); - let flags = cmd_flags.clone(); - arg.watch = false; //avoid creating infinite watchers - tokio::task::spawn(async move { - let _ = run_patterns_test(arg, flags, Some(modified_file_path)).await; - }); + let mut patterns_to_test = modified_patterns.clone(); + let mut patterns_to_test_names = patterns_to_test + .iter() + .map(|p| p.local_name.clone().unwrap()) + .collect::>(); + + if modified_patterns.len() > 0 { + let modified_patterns_dependents_names = get_dependents_of_target_patterns( + &libs, + &testable_patterns, + &modified_patterns, + )?; + + log::info!( + "[Watch Mode] Modified patterns' Dependents Names: {:?}", + modified_patterns_dependents_names + ); + + for name in &modified_patterns_dependents_names { + if !patterns_to_test_names.contains(&name) { + patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); + patterns_to_test_names.push(name.to_owned()); + } + } + } + + if deleted_patterns.len() > 0 { + let deleted_patterns_dependents_names = get_dependents_of_target_patterns( + &libs, + &testable_patterns, + &deleted_patterns, + )?; + + log::info!( + "[Watch Mode] Deleted patterns' Dependents Names: {:?}", + deleted_patterns_dependents_names + ); + + for name in &deleted_patterns_dependents_names { + if !deleted_patterns_names.contains(&name) + && !patterns_to_test_names.contains(&name) + { + patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); + patterns_to_test_names.push(name.to_owned()); + } + } + } + + log::info!( + "[Watch Mode] Pattern to re-test: {:?}", + patterns_to_test_names + ); + if patterns_to_test_names.len() < 1 { + break 'event_block; + } + + let _ = get_marzano_pattern_test_results( + patterns_to_test, + &libs, + &args, + output.clone().into(), + ) + .await; } Err(error) => { log::error!("Error {error:?}") } } } + Ok(()) } -fn get_affected_patterns( - modified_file_path: String, - patterns: Vec, +fn get_dependents_of_target_patterns( libs: &PatternsDirectory, testable_patterns: &Vec, + target_patterns: &Vec, ) -> Result> { - let affected_patterns = testable_patterns - .iter() - .filter(|p| { - p.config.path.is_some() && p.config.path.as_ref().unwrap() == &modified_file_path - }) - .collect::>(); - - let mut affected_patterns_names = >::new(); - for affected_pattern in &affected_patterns { - affected_patterns_names.push(affected_pattern.local_name.clone().unwrap()); + let mut target_patterns_names = >::new(); + for p in target_patterns { + target_patterns_names.push(p.local_name.clone().unwrap()); } - let mut affected_patterns_dependents_names = >::new(); + let mut dependents_names = >::new(); let current_dir = std::env::current_dir()?; let resolver = GritModuleResolver::new(current_dir.to_str().unwrap()); - for p in patterns { - let body = format!("{}()", p.local_name); + for p in testable_patterns { + let body = format!("{}()", p.local_name.as_ref().unwrap()); let lang = PatternLanguage::get_language(&p.body); let libs = libs.get_language_directory_or_default(lang)?; let rich_pattern = resolver - .make_pattern(&body, Some(p.local_name.to_string())) + .make_pattern(&body, p.local_name.to_owned()) .unwrap(); let src = rich_pattern.body; let mut parser = MarzanoGritParser::new()?; @@ -393,27 +467,47 @@ fn get_affected_patterns( let root = src_tree.root_node(); let is_multifile = is_multifile(&root, &libs, &mut parser)?; - let dependents = get_dependents_of_target_patterns( + let dependents = get_dependents_of_target_patterns_by_traversal_from_src( &libs, &src, &mut parser, !is_multifile, - &affected_patterns_names, + &target_patterns_names, )?; for d in dependents { - if !affected_patterns_dependents_names.contains(&d) { - affected_patterns_dependents_names.push(d); + if !dependents_names.contains(&d) { + dependents_names.push(d); } } } + Ok(dependents_names) +} - for depen in affected_patterns_dependents_names { - if !affected_patterns_names.contains(&depen) { - affected_patterns_names.push(depen); +async fn get_modified_and_deleted_patterns( + modified_path: &String, + testable_patterns: &Vec, +) -> Result<(Vec, Vec)> { + let path = Path::new(modified_path); + let file_patterns = collect_from_file(path, &None).await?; + let modified_patterns = get_grit_pattern_test_info(file_patterns); + + let mut modified_pattern_names = >::new(); + for pattern in &modified_patterns { + modified_pattern_names.push(pattern.local_name.clone().unwrap()); + } + //modified_patterns = patterns which are updated/edited or newly created. + //deleted_patterns = patterns which are deleted. Only remaining dependents of deleted_patterns should gets tested. + let mut deleted_patterns = >::new(); + for pattern in testable_patterns { + if pattern.config.path.as_ref().unwrap() == modified_path + && !modified_pattern_names.contains(pattern.local_name.as_ref().unwrap()) + { + deleted_patterns.push(pattern.clone()); } } - Ok(affected_patterns_names) + + Ok((modified_patterns, deleted_patterns)) } #[derive(Debug, Serialize)] diff --git a/crates/core/src/pattern_compiler/compiler.rs b/crates/core/src/pattern_compiler/compiler.rs index 1d7b58dfb..b0fd4ddf2 100644 --- a/crates/core/src/pattern_compiler/compiler.rs +++ b/crates/core/src/pattern_compiler/compiler.rs @@ -567,7 +567,7 @@ fn find_definition_if_exists( Ok(None) } -pub fn get_dependents_of_target_patterns( +pub fn get_dependents_of_target_patterns_by_traversal_from_src( libs: &BTreeMap, src: &str, parser: &mut MarzanoGritParser, diff --git a/crates/gritmodule/src/config.rs b/crates/gritmodule/src/config.rs index b1d2d2e04..b297304c1 100644 --- a/crates/gritmodule/src/config.rs +++ b/crates/gritmodule/src/config.rs @@ -101,13 +101,13 @@ pub struct GritSerializedDefinitionConfig { pub samples: Option>, } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct GritPatternTestConfig { pub path: Option, pub samples: Option>, } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct GritPatternTestInfo { pub body: String, @@ -262,7 +262,7 @@ impl Ord for ResolvedGritDefinition { pub fn pattern_config_to_model( pattern: GritDefinitionConfig, - source: &ModuleRepo, + source: &Option, ) -> Result { let mut split_name = pattern.name.split('#'); let repo = split_name.next(); @@ -284,7 +284,7 @@ pub fn pattern_config_to_model( Some(split_repo.collect::>().join("/")) }; if defined_local_name.is_none() { - Some(source.clone()) + source.clone() } else if host.is_none() || full_name.is_none() { None } else { diff --git a/crates/gritmodule/src/testing.rs b/crates/gritmodule/src/testing.rs index 99d81238c..3f5dff788 100644 --- a/crates/gritmodule/src/testing.rs +++ b/crates/gritmodule/src/testing.rs @@ -9,7 +9,8 @@ use marzano_util::rich_path::RichFile; use serde::{Deserialize, Serialize}; use crate::config::{ - GritPatternSample, GritPatternTestConfig, GritPatternTestInfo, ResolvedGritDefinition, + GritPatternSample, GritPatternTestConfig, GritPatternTestInfo, ModuleGritPattern, + ResolvedGritDefinition, }; fn map_pattern_to_test_info(pattern: &mut ResolvedGritDefinition) -> GritPatternTestInfo { @@ -32,6 +33,29 @@ pub fn collect_testable_patterns( testable_patterns } +fn map_file_pattern_to_test_info(pattern: &mut ModuleGritPattern) -> GritPatternTestInfo { + let samples = pattern.config.samples.take(); + GritPatternTestInfo { + body: pattern.config.body.clone().unwrap(), + config: GritPatternTestConfig { + path: Some(pattern.config.path.clone()), + samples, + }, + local_name: Some(pattern.local_name.clone()), + } +} + +pub fn get_grit_pattern_test_info( + mut patterns: Vec, +) -> Vec { + let testable_patterns: Vec = patterns + .iter_mut() + .filter(|p| p.config.body.is_some()) + .map(map_file_pattern_to_test_info) + .collect(); + testable_patterns +} + const SAMPLE_NAME_LENGTH: usize = 50; pub fn get_sample_name(sample: &GritPatternSample) -> String { From d862cc826d3c439e78e18ffe269db4a6bcdff9b5 Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Fri, 7 Jun 2024 17:04:00 +0530 Subject: [PATCH 06/17] remove clone() usage where possible --- crates/cli/src/commands/patterns_test.rs | 16 ++++++------ crates/gritmodule/src/testing.rs | 32 +++++++++++------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 5fda41baa..a7f49c6f7 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -285,13 +285,13 @@ pub(crate) async fn run_patterns_test( } } - let testable_patterns = collect_testable_patterns(patterns.clone()); + let testable_patterns = collect_testable_patterns(patterns); get_marzano_pattern_test_results(testable_patterns.clone(), &libs, &arg, flags.clone().into()) .await?; if arg.watch { - let _ = enable_watch_mode(testable_patterns, &libs, &arg, flags.clone().into()).await; + let _ = enable_watch_mode(testable_patterns, &libs, &arg, flags.into()).await; } Ok(()) } @@ -348,6 +348,7 @@ async fn enable_watch_mode( let (modified_patterns, deleted_patterns) = get_modified_and_deleted_patterns(&modified_file_path, &testable_patterns) .await?; + if modified_patterns.is_empty() && deleted_patterns.is_empty() { log::info!("[Watch Mode] No patterns changed.\n"); break 'event_block; @@ -417,10 +418,7 @@ async fn enable_watch_mode( } } - log::info!( - "[Watch Mode] Pattern to re-test: {:?}", - patterns_to_test_names - ); + log::info!("[Watch Mode] Pattern to test: {:?}", patterns_to_test_names); if patterns_to_test_names.len() < 1 { break 'event_block; } @@ -492,16 +490,16 @@ async fn get_modified_and_deleted_patterns( let file_patterns = collect_from_file(path, &None).await?; let modified_patterns = get_grit_pattern_test_info(file_patterns); - let mut modified_pattern_names = >::new(); + let mut modified_pattern_names = >::new(); for pattern in &modified_patterns { - modified_pattern_names.push(pattern.local_name.clone().unwrap()); + modified_pattern_names.push(pattern.local_name.as_ref().unwrap()); } //modified_patterns = patterns which are updated/edited or newly created. //deleted_patterns = patterns which are deleted. Only remaining dependents of deleted_patterns should gets tested. let mut deleted_patterns = >::new(); for pattern in testable_patterns { if pattern.config.path.as_ref().unwrap() == modified_path - && !modified_pattern_names.contains(pattern.local_name.as_ref().unwrap()) + && !modified_pattern_names.contains(&pattern.local_name.as_ref().unwrap()) { deleted_patterns.push(pattern.clone()); } diff --git a/crates/gritmodule/src/testing.rs b/crates/gritmodule/src/testing.rs index 3f5dff788..007947ded 100644 --- a/crates/gritmodule/src/testing.rs +++ b/crates/gritmodule/src/testing.rs @@ -13,45 +13,43 @@ use crate::config::{ ResolvedGritDefinition, }; -fn map_pattern_to_test_info(pattern: &mut ResolvedGritDefinition) -> GritPatternTestInfo { - let samples = pattern.config.samples.take(); +fn map_pattern_to_test_info(pattern: ResolvedGritDefinition) -> GritPatternTestInfo { + let samples = pattern.config.samples; GritPatternTestInfo { - body: pattern.body.clone(), + body: pattern.body, config: GritPatternTestConfig { - path: Some(pattern.config.path.clone()), + path: Some(pattern.config.path), samples, }, - local_name: Some(pattern.local_name.clone()), + local_name: Some(pattern.local_name), } } pub fn collect_testable_patterns( - mut patterns: Vec, + patterns: Vec, ) -> Vec { let testable_patterns: Vec = - patterns.iter_mut().map(map_pattern_to_test_info).collect(); + patterns.into_iter().map(map_pattern_to_test_info).collect(); testable_patterns } -fn map_file_pattern_to_test_info(pattern: &mut ModuleGritPattern) -> GritPatternTestInfo { - let samples = pattern.config.samples.take(); +fn map_file_pattern_to_test_info(pattern: ModuleGritPattern) -> GritPatternTestInfo { + let samples = pattern.config.samples; GritPatternTestInfo { - body: pattern.config.body.clone().unwrap(), + body: pattern.config.body.unwrap(), config: GritPatternTestConfig { - path: Some(pattern.config.path.clone()), + path: Some(pattern.config.path), samples, }, - local_name: Some(pattern.local_name.clone()), + local_name: Some(pattern.local_name), } } -pub fn get_grit_pattern_test_info( - mut patterns: Vec, -) -> Vec { +pub fn get_grit_pattern_test_info(patterns: Vec) -> Vec { let testable_patterns: Vec = patterns - .iter_mut() + .into_iter() .filter(|p| p.config.body.is_some()) - .map(map_file_pattern_to_test_info) + .map(|pattern: ModuleGritPattern| map_file_pattern_to_test_info(pattern)) .collect(); testable_patterns } From 62ebf75271f875c027701ca034371e1263bb41ef Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Fri, 7 Jun 2024 17:09:24 +0530 Subject: [PATCH 07/17] snapshot test update for yaml__tests__gets_module_patterns --- ...rzano_gritmodule__yaml__tests__gets_module_patterns.snap | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/gritmodule/src/snapshots/marzano_gritmodule__yaml__tests__gets_module_patterns.snap b/crates/gritmodule/src/snapshots/marzano_gritmodule__yaml__tests__gets_module_patterns.snap index 635ad6701..74c35f3d4 100644 --- a/crates/gritmodule/src/snapshots/marzano_gritmodule__yaml__tests__gets_module_patterns.snap +++ b/crates/gritmodule/src/snapshots/marzano_gritmodule__yaml__tests__gets_module_patterns.snap @@ -72,9 +72,5 @@ expression: patterns line: 9 column: 11 raw: ~ - module: - host: "" - fullName: "" - remote: "" - providerName: "" + module: ~ local_name: remove_console_error From 266c58b545e8f5be001c5e6e3987967f42876845 Mon Sep 17 00:00:00 2001 From: Ashutosh Sharma Date: Fri, 7 Jun 2024 17:24:55 +0530 Subject: [PATCH 08/17] applied clippy fix --- crates/cli/src/commands/patterns_test.rs | 27 ++++++++++---------- crates/core/src/pattern_compiler/compiler.rs | 2 +- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index a7f49c6f7..59d59c48e 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -330,8 +330,7 @@ async fn enable_watch_mode( for result in rx { match result { Ok(event) => 'event_block: { - let modified_file_path = event - .get(0) + let modified_file_path = event.first() .unwrap() .path .clone() @@ -376,9 +375,9 @@ async fn enable_watch_mode( .map(|p| p.local_name.clone().unwrap()) .collect::>(); - if modified_patterns.len() > 0 { + if !modified_patterns.is_empty() { let modified_patterns_dependents_names = get_dependents_of_target_patterns( - &libs, + libs, &testable_patterns, &modified_patterns, )?; @@ -389,16 +388,16 @@ async fn enable_watch_mode( ); for name in &modified_patterns_dependents_names { - if !patterns_to_test_names.contains(&name) { + if !patterns_to_test_names.contains(name) { patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); patterns_to_test_names.push(name.to_owned()); } } } - if deleted_patterns.len() > 0 { + if !deleted_patterns.is_empty() { let deleted_patterns_dependents_names = get_dependents_of_target_patterns( - &libs, + libs, &testable_patterns, &deleted_patterns, )?; @@ -410,7 +409,7 @@ async fn enable_watch_mode( for name in &deleted_patterns_dependents_names { if !deleted_patterns_names.contains(&name) - && !patterns_to_test_names.contains(&name) + && !patterns_to_test_names.contains(name) { patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); patterns_to_test_names.push(name.to_owned()); @@ -419,15 +418,15 @@ async fn enable_watch_mode( } log::info!("[Watch Mode] Pattern to test: {:?}", patterns_to_test_names); - if patterns_to_test_names.len() < 1 { + if patterns_to_test_names.is_empty() { break 'event_block; } let _ = get_marzano_pattern_test_results( patterns_to_test, - &libs, - &args, - output.clone().into(), + libs, + args, + output.clone(), ) .await; } @@ -461,13 +460,13 @@ fn get_dependents_of_target_patterns( .unwrap(); let src = rich_pattern.body; let mut parser = MarzanoGritParser::new()?; - let src_tree = parser.parse_file(&src, Some(Path::new(DEFAULT_FILE_NAME)))?; + let src_tree = parser.parse_file(src, Some(Path::new(DEFAULT_FILE_NAME)))?; let root = src_tree.root_node(); let is_multifile = is_multifile(&root, &libs, &mut parser)?; let dependents = get_dependents_of_target_patterns_by_traversal_from_src( &libs, - &src, + src, &mut parser, !is_multifile, &target_patterns_names, diff --git a/crates/core/src/pattern_compiler/compiler.rs b/crates/core/src/pattern_compiler/compiler.rs index b0fd4ddf2..abfe1ac84 100644 --- a/crates/core/src/pattern_compiler/compiler.rs +++ b/crates/core/src/pattern_compiler/compiler.rs @@ -572,7 +572,7 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( src: &str, parser: &mut MarzanoGritParser, will_autowrap: bool, - target_patterns: &Vec, + target_patterns: &[String], ) -> Result> { let mut dependents = >::new(); let node_like = "nodeLike"; From 62ec2d348e47a9f57e079d19f5b75676992d2184 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Mon, 24 Jun 2024 22:41:34 +0530 Subject: [PATCH 09/17] improved code quality of watch mode functions --- crates/cli/src/commands/patterns_test.rs | 65 ++++-------------- crates/core/src/pattern_compiler/compiler.rs | 71 +++++--------------- 2 files changed, 32 insertions(+), 104 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 59d59c48e..54b9dbb2f 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -3,7 +3,6 @@ use dashmap::{DashMap, ReadOnlyView}; use log::{debug, info}; use marzano_core::api::MatchResult; -use marzano_core::constants::DEFAULT_FILE_NAME; use marzano_gritmodule::config::{GritPatternSample, GritPatternTestInfo}; use marzano_gritmodule::formatting::format_rich_files; use marzano_gritmodule::markdown::replace_sample_in_md_file; @@ -33,8 +32,6 @@ use anyhow::{anyhow, bail, Context as _, Result}; use std::collections::HashMap; use std::{path::Path, time::Duration}; -use grit_util::Ast; -use marzano_core::analysis::is_multifile; use marzano_core::pattern_compiler::compiler::get_dependents_of_target_patterns_by_traversal_from_src; use marzano_gritmodule::searcher::collect_from_file; use notify::{self, RecursiveMode}; @@ -315,10 +312,7 @@ async fn enable_watch_mode( // select backend via fish operator, here PollWatcher backend let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx).unwrap(); - debouncer - .watcher() - .watch(path, RecursiveMode::Recursive) - .unwrap(); + debouncer.watcher().watch(path, RecursiveMode::Recursive)?; log::info!("\n[Watch Mode] enabled on path: {}", path.display()); let testable_patterns_map = testable_patterns @@ -330,13 +324,13 @@ async fn enable_watch_mode( for result in rx { match result { Ok(event) => 'event_block: { - let modified_file_path = event.first() - .unwrap() - .path - .clone() - .into_os_string() - .into_string() - .unwrap(); + let modified_file_path = event.first().unwrap().path.clone(); + + if !modified_file_path.is_file() { + break 'event_block; + } + let modified_file_path = modified_file_path.into_os_string().into_string().unwrap(); + //temorary fix, until notify crate adds support for ignoring paths for path in &ignore_path { if modified_file_path.contains(path) { @@ -352,22 +346,11 @@ async fn enable_watch_mode( log::info!("[Watch Mode] No patterns changed.\n"); break 'event_block; } - let modified_patterns_names = modified_patterns - .iter() - .map(|p| p.local_name.as_ref().unwrap()) - .collect::>(); + let deleted_patterns_names = deleted_patterns .iter() .map(|p| p.local_name.as_ref().unwrap()) .collect::>(); - log::info!( - "[Watch Mode] Modified patterns: {:?}", - modified_patterns_names - ); - log::info!( - "[Watch Mode] Deleted patterns: {:?}", - deleted_patterns_names - ); let mut patterns_to_test = modified_patterns.clone(); let mut patterns_to_test_names = patterns_to_test @@ -381,12 +364,6 @@ async fn enable_watch_mode( &testable_patterns, &modified_patterns, )?; - - log::info!( - "[Watch Mode] Modified patterns' Dependents Names: {:?}", - modified_patterns_dependents_names - ); - for name in &modified_patterns_dependents_names { if !patterns_to_test_names.contains(name) { patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); @@ -401,12 +378,6 @@ async fn enable_watch_mode( &testable_patterns, &deleted_patterns, )?; - - log::info!( - "[Watch Mode] Deleted patterns' Dependents Names: {:?}", - deleted_patterns_dependents_names - ); - for name in &deleted_patterns_dependents_names { if !deleted_patterns_names.contains(&name) && !patterns_to_test_names.contains(name) @@ -422,16 +393,12 @@ async fn enable_watch_mode( break 'event_block; } - let _ = get_marzano_pattern_test_results( - patterns_to_test, - libs, - args, - output.clone(), - ) - .await; + let _ = + get_marzano_pattern_test_results(patterns_to_test, libs, args, output.clone()) + .await; } Err(error) => { - log::error!("Error {error:?}") + log::error!("[Watch Mode] Error: {error:?}") } } } @@ -460,15 +427,11 @@ fn get_dependents_of_target_patterns( .unwrap(); let src = rich_pattern.body; let mut parser = MarzanoGritParser::new()?; - let src_tree = parser.parse_file(src, Some(Path::new(DEFAULT_FILE_NAME)))?; - let root = src_tree.root_node(); - let is_multifile = is_multifile(&root, &libs, &mut parser)?; let dependents = get_dependents_of_target_patterns_by_traversal_from_src( &libs, src, &mut parser, - !is_multifile, &target_patterns_names, )?; @@ -486,7 +449,7 @@ async fn get_modified_and_deleted_patterns( testable_patterns: &Vec, ) -> Result<(Vec, Vec)> { let path = Path::new(modified_path); - let file_patterns = collect_from_file(path, &None).await?; + let file_patterns = collect_from_file(path, &None).await.unwrap_or(vec![]); let modified_patterns = get_grit_pattern_test_info(file_patterns); let mut modified_pattern_names = >::new(); diff --git a/crates/core/src/pattern_compiler/compiler.rs b/crates/core/src/pattern_compiler/compiler.rs index abfe1ac84..8c6345ed5 100644 --- a/crates/core/src/pattern_compiler/compiler.rs +++ b/crates/core/src/pattern_compiler/compiler.rs @@ -571,7 +571,6 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( libs: &BTreeMap, src: &str, parser: &mut MarzanoGritParser, - will_autowrap: bool, target_patterns: &[String], ) -> Result> { let mut dependents = >::new(); @@ -587,20 +586,16 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( foreign_functions: foreign_file, } = defs_to_filenames(libs, parser, tree.root_node())?; - let mut traversed_stack = >::new(); - - // gross but necessary due to running these patterns before and after each file - let mut stack: Vec = if will_autowrap { - let before_each_file = "before_each_file()"; - let before_tree = - parser.parse_file(before_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; - let after_each_file = "after_each_file()"; - let after_tree = parser.parse_file(after_each_file, Some(Path::new(DEFAULT_FILE_NAME)))?; + let name_to_filename: BTreeMap = pattern_file + .iter() + .map(|(k, v)| (k.clone(), (v.clone()))) + .chain(predicate_file.iter().map(|(k, v)| (k.clone(), (v.clone())))) + .chain(function_file.iter().map(|(k, v)| (k.clone(), (v.clone())))) + .chain(foreign_file.iter().map(|(k, v)| (k.clone(), (v.clone())))) + .collect(); - vec![tree, before_tree, after_tree] - } else { - vec![tree] - }; + let mut traversed_stack = >::new(); + let mut stack: Vec = vec![tree]; while let Some(tree) = stack.pop() { let root = tree.root_node(); let cursor = root.walk(); @@ -619,37 +614,9 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( dependents.push(e); } } - if n.node.kind() == node_like { - if let Some(tree) = find_child_tree_definition( - &pattern_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - if let Some(tree) = find_child_tree_definition( - &function_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - if let Some(tree) = find_child_tree_definition( - &foreign_file, - parser, - libs, - &mut traversed_stack, - &name, - )? { - stack.push(tree); - } - } else if n.node.kind() == predicate_call { + if let Some(file_name) = name_to_filename.get(&name) { if let Some(tree) = find_child_tree_definition( - &predicate_file, + file_name, parser, libs, &mut traversed_stack, @@ -664,21 +631,19 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( } fn find_child_tree_definition( - files: &BTreeMap, + file_name: &str, parser: &mut MarzanoGritParser, libs: &BTreeMap, traversed_stack: &mut Vec, name: &str, ) -> Result> { - if let Some(file_name) = files.get(name) { - if !traversed_stack.contains(&name.to_string()) { - if let Some(file_body) = libs.get(file_name) { - traversed_stack.push(name.to_owned()); - let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?; - return Ok(Some(tree)); - } + if !traversed_stack.contains(&name.to_string()) { + if let Some(file_body) = libs.get(file_name) { + traversed_stack.push(name.to_owned()); + let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?; + return Ok(Some(tree)); } - }; + } Ok(None) } From d91a1f571458011870f92fa347ba328e025a6262 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Fri, 28 Jun 2024 12:57:05 +0530 Subject: [PATCH 10/17] simplified code --- crates/cli/src/commands/patterns_test.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 54b9dbb2f..706c743df 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -320,21 +320,21 @@ async fn enable_watch_mode( .map(|p| (p.local_name.clone().unwrap(), p.clone())) .collect::>(); - // event pocessing + // event processing for result in rx { match result { - Ok(event) => 'event_block: { + Ok(event) => { let modified_file_path = event.first().unwrap().path.clone(); if !modified_file_path.is_file() { - break 'event_block; + continue; } let modified_file_path = modified_file_path.into_os_string().into_string().unwrap(); - //temorary fix, until notify crate adds support for ignoring paths + //temporary fix, until notify crate adds support for ignoring paths for path in &ignore_path { if modified_file_path.contains(path) { - break 'event_block; + continue; } } log::info!("\n[Watch Mode] File modified: {:?}", modified_file_path); @@ -344,7 +344,7 @@ async fn enable_watch_mode( if modified_patterns.is_empty() && deleted_patterns.is_empty() { log::info!("[Watch Mode] No patterns changed.\n"); - break 'event_block; + continue; } let deleted_patterns_names = deleted_patterns @@ -390,7 +390,7 @@ async fn enable_watch_mode( log::info!("[Watch Mode] Pattern to test: {:?}", patterns_to_test_names); if patterns_to_test_names.is_empty() { - break 'event_block; + continue; } let _ = From ed2f881b04322cc229b2c2f8146382096f08dfa0 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Fri, 28 Jun 2024 13:13:17 +0530 Subject: [PATCH 11/17] added integration test for: patterns test --watch --- .grit/test/test.yaml | 82 +++++++++++++++++++++++++++ crates/cli_bin/tests/patterns_test.rs | 69 ++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 .grit/test/test.yaml diff --git a/.grit/test/test.yaml b/.grit/test/test.yaml new file mode 100644 index 000000000..efd6d1fd6 --- /dev/null +++ b/.grit/test/test.yaml @@ -0,0 +1,82 @@ +# this file is used for integration testing of `grit patterns test --watch` +version: 0.0.1 +patterns: + - name: github.com/getgrit/stdlib#* + - name: our_cargo_use_long_dependency + level: error + body: | + language toml + + cargo_use_long_dependency() where $filename <: not includes or { + "language-submodules", + "language-metavariables" + } + - name: cargo_use_long_dependency + level: error + body: | + language toml + + `[dependencies] + $deps` where { + $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, + $deps <: some bubble `$name = $version` where { + $version <: string(), + $version => `{ version = $version }`, + } + } + - name: no_treesitter_in_grit_crates + description: | + The `grit-pattern-matcher` and `grit-util` crates should remain free of + TreeSitter dependencies. This also implies they cannot have dependencies on any + of the `marzano-*` crates, since those *can* have TreeSitter dependencies. + level: error + body: | + language toml + + `[dependencies] + $deps` where { + $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, + $absolute_filename <: or { + includes "crates/grit-pattern-matcher", + includes "crates/grit-util" + }, + $deps <: some bubble `$name = $specifier` where $name <: or { + includes "tree_sitter", + includes "tree-sitter", + includes "marzano" + } + } + - name: no_println_in_lsp + description: Don't use println!() in LSP code, it breaks the LSP stdio protocol. + level: error + body: | + engine marzano(0.1) + language rust + + `println!($_)` => . where { + $filename <: not includes "test.rs", + $absolute_filename <: includes "lsp", + } + - name: no_println_in_core + description: Don't use println or other debugging macros in core code. + level: error + body: | + engine marzano(0.1) + language rust + + `println!($args)` as $print where { + $outcome = ., + or { + $absolute_filename <: includes "crates/core", + $absolute_filename <: includes "crates/gritmodule", + $absolute_filename <: includes "crates/util", + and { $absolute_filename <: includes "crates/cli/", $outcome = `log::info!($args)` } + }, + // Allow tests and build utils + $absolute_filename <: not includes or { + "tests/", + "build.rs", + "test" + }, + $print <: not within `mod tests { $_ }` + } => $outcome \ No newline at end of file diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index 76da62522..a41907b60 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -1,4 +1,15 @@ +use std::{ + env, fs, + io::{BufRead, BufReader}, + path::Path, + process::{Command, Stdio}, + sync::mpsc, + thread, + time::Duration, +}; + use anyhow::Result; +use assert_cmd::cargo::CommandCargoExt; use insta::assert_snapshot; use crate::common::{get_fixture, get_test_cmd}; @@ -309,3 +320,61 @@ fn tests_python_pattern_with_file_name() -> Result<()> { Ok(()) } + +#[test] +fn watch_mode_of_patterns_test() -> Result<()> { + let (tx, rx) = mpsc::channel(); + let cur_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let root_dir = Path::new(&cur_dir) + .parent() + .unwrap() + .parent() + .unwrap() + .to_owned(); + let test_yaml_path = root_dir.join(".grit").join("test").join("test.yaml"); + + let _cmd_handle = thread::spawn(move || { + let mut cmd = Command::cargo_bin("marzano") + .unwrap() + .args(&["patterns", "test", "--watch"]) + .current_dir(&root_dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to start command"); + + let stdout = BufReader::new(cmd.stdout.take().unwrap()); + let stderr = BufReader::new(cmd.stderr.take().unwrap()); + for line in stdout.lines().chain(stderr.lines()) { + if let Ok(line) = line { + tx.send(line).unwrap(); + } + } + }); + thread::sleep(Duration::from_secs(1)); + + let _modifier_handle = thread::spawn(move || { + let content = fs::read_to_string(&test_yaml_path).unwrap(); + fs::write(&test_yaml_path, content).unwrap(); + }); + thread::sleep(Duration::from_secs(1)); + + let mut output = Vec::new(); + while let Ok(line) = rx.try_recv() { + output.push(line); + } + let expected_output = vec![ + "[Watch Mode] enabled on path: .grit", + "[Watch Mode] File modified: \".grit/test/test.yaml\"", + "[Watch Mode] Pattern to test: [\"our_cargo_use_long_dependency\", \"cargo_use_long_dependency\", \"no_treesitter_in_grit_crates\", \"no_println_in_lsp\", \"no_println_in_core\"]", + "Found 5 testable patterns.", + ]; + for expected_line in expected_output { + assert!( + output.iter().any(|line| line.contains(expected_line)), + "Expected output not found: {}", + expected_line + ); + } + Ok(()) +} From 5f26579d8806a272670e8091262845689c025b4b Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Sun, 30 Jun 2024 19:34:58 +0530 Subject: [PATCH 12/17] using temp directory for watch mode test --- .grit/grit.yaml | 13 +++++ .grit/test/test.yaml | 82 --------------------------- crates/cli_bin/tests/patterns_test.rs | 21 ++++--- 3 files changed, 27 insertions(+), 89 deletions(-) delete mode 100644 .grit/test/test.yaml diff --git a/.grit/grit.yaml b/.grit/grit.yaml index 7ece92ec8..758e168a6 100644 --- a/.grit/grit.yaml +++ b/.grit/grit.yaml @@ -10,6 +10,19 @@ patterns: "language-submodules", "language-metavariables" } + - name: cargo_use_long_dependency + level: error + body: | + language toml + + `[dependencies] + $deps` where { + $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, + $deps <: some bubble `$name = $version` where { + $version <: string(), + $version => `{ version = $version }`, + } + } - name: no_treesitter_in_grit_crates description: | The `grit-pattern-matcher` and `grit-util` crates should remain free of diff --git a/.grit/test/test.yaml b/.grit/test/test.yaml deleted file mode 100644 index efd6d1fd6..000000000 --- a/.grit/test/test.yaml +++ /dev/null @@ -1,82 +0,0 @@ -# this file is used for integration testing of `grit patterns test --watch` -version: 0.0.1 -patterns: - - name: github.com/getgrit/stdlib#* - - name: our_cargo_use_long_dependency - level: error - body: | - language toml - - cargo_use_long_dependency() where $filename <: not includes or { - "language-submodules", - "language-metavariables" - } - - name: cargo_use_long_dependency - level: error - body: | - language toml - - `[dependencies] - $deps` where { - $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, - $deps <: some bubble `$name = $version` where { - $version <: string(), - $version => `{ version = $version }`, - } - } - - name: no_treesitter_in_grit_crates - description: | - The `grit-pattern-matcher` and `grit-util` crates should remain free of - TreeSitter dependencies. This also implies they cannot have dependencies on any - of the `marzano-*` crates, since those *can* have TreeSitter dependencies. - level: error - body: | - language toml - - `[dependencies] - $deps` where { - $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, - $absolute_filename <: or { - includes "crates/grit-pattern-matcher", - includes "crates/grit-util" - }, - $deps <: some bubble `$name = $specifier` where $name <: or { - includes "tree_sitter", - includes "tree-sitter", - includes "marzano" - } - } - - name: no_println_in_lsp - description: Don't use println!() in LSP code, it breaks the LSP stdio protocol. - level: error - body: | - engine marzano(0.1) - language rust - - `println!($_)` => . where { - $filename <: not includes "test.rs", - $absolute_filename <: includes "lsp", - } - - name: no_println_in_core - description: Don't use println or other debugging macros in core code. - level: error - body: | - engine marzano(0.1) - language rust - - `println!($args)` as $print where { - $outcome = ., - or { - $absolute_filename <: includes "crates/core", - $absolute_filename <: includes "crates/gritmodule", - $absolute_filename <: includes "crates/util", - and { $absolute_filename <: includes "crates/cli/", $outcome = `log::info!($args)` } - }, - // Allow tests and build utils - $absolute_filename <: not includes or { - "tests/", - "build.rs", - "test" - }, - $print <: not within `mod tests { $_ }` - } => $outcome \ No newline at end of file diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index a41907b60..7d7ba9360 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -11,6 +11,7 @@ use std::{ use anyhow::Result; use assert_cmd::cargo::CommandCargoExt; use insta::assert_snapshot; +use tempfile::tempdir; use crate::common::{get_fixture, get_test_cmd}; @@ -331,13 +332,21 @@ fn watch_mode_of_patterns_test() -> Result<()> { .parent() .unwrap() .to_owned(); - let test_yaml_path = root_dir.join(".grit").join("test").join("test.yaml"); + let temp_dir = tempdir()?; + let temp_grit_dir = temp_dir.path().join(".grit"); + fs::create_dir(&temp_grit_dir)?; + + let test_yaml_path = temp_grit_dir.join("grit.yaml"); + let source_yaml_path = root_dir.join(".grit").join("grit.yaml"); + fs::copy(source_yaml_path, &test_yaml_path)?; + + let temp_dir_path = temp_dir.path().to_owned(); let _cmd_handle = thread::spawn(move || { let mut cmd = Command::cargo_bin("marzano") .unwrap() .args(&["patterns", "test", "--watch"]) - .current_dir(&root_dir) + .current_dir(&temp_dir_path) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() @@ -353,10 +362,8 @@ fn watch_mode_of_patterns_test() -> Result<()> { }); thread::sleep(Duration::from_secs(1)); - let _modifier_handle = thread::spawn(move || { - let content = fs::read_to_string(&test_yaml_path).unwrap(); - fs::write(&test_yaml_path, content).unwrap(); - }); + let content = fs::read_to_string(&test_yaml_path).expect("Unable to read the file"); + fs::write(&test_yaml_path, content).unwrap(); thread::sleep(Duration::from_secs(1)); let mut output = Vec::new(); @@ -365,7 +372,7 @@ fn watch_mode_of_patterns_test() -> Result<()> { } let expected_output = vec![ "[Watch Mode] enabled on path: .grit", - "[Watch Mode] File modified: \".grit/test/test.yaml\"", + "[Watch Mode] File modified: \".grit/grit.yaml\"", "[Watch Mode] Pattern to test: [\"our_cargo_use_long_dependency\", \"cargo_use_long_dependency\", \"no_treesitter_in_grit_crates\", \"no_println_in_lsp\", \"no_println_in_core\"]", "Found 5 testable patterns.", ]; From e73e73c8344e192e06a540d0f8168b499d54fce5 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Sun, 30 Jun 2024 20:04:27 +0530 Subject: [PATCH 13/17] using get_fixture for temp directory setup --- .grit/grit.yaml | 13 ---- crates/cli_bin/fixtures/.grit/grit.yaml | 81 +++++++++++++++++++++++++ crates/cli_bin/tests/patterns_test.rs | 18 +----- 3 files changed, 83 insertions(+), 29 deletions(-) create mode 100644 crates/cli_bin/fixtures/.grit/grit.yaml diff --git a/.grit/grit.yaml b/.grit/grit.yaml index 758e168a6..7ece92ec8 100644 --- a/.grit/grit.yaml +++ b/.grit/grit.yaml @@ -10,19 +10,6 @@ patterns: "language-submodules", "language-metavariables" } - - name: cargo_use_long_dependency - level: error - body: | - language toml - - `[dependencies] - $deps` where { - $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, - $deps <: some bubble `$name = $version` where { - $version <: string(), - $version => `{ version = $version }`, - } - } - name: no_treesitter_in_grit_crates description: | The `grit-pattern-matcher` and `grit-util` crates should remain free of diff --git a/crates/cli_bin/fixtures/.grit/grit.yaml b/crates/cli_bin/fixtures/.grit/grit.yaml new file mode 100644 index 000000000..758e168a6 --- /dev/null +++ b/crates/cli_bin/fixtures/.grit/grit.yaml @@ -0,0 +1,81 @@ +version: 0.0.1 +patterns: + - name: github.com/getgrit/stdlib#* + - name: our_cargo_use_long_dependency + level: error + body: | + language toml + + cargo_use_long_dependency() where $filename <: not includes or { + "language-submodules", + "language-metavariables" + } + - name: cargo_use_long_dependency + level: error + body: | + language toml + + `[dependencies] + $deps` where { + $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, + $deps <: some bubble `$name = $version` where { + $version <: string(), + $version => `{ version = $version }`, + } + } + - name: no_treesitter_in_grit_crates + description: | + The `grit-pattern-matcher` and `grit-util` crates should remain free of + TreeSitter dependencies. This also implies they cannot have dependencies on any + of the `marzano-*` crates, since those *can* have TreeSitter dependencies. + level: error + body: | + language toml + + `[dependencies] + $deps` where { + $filename <: or { includes "Cargo.toml", includes "cargo.toml" }, + $absolute_filename <: or { + includes "crates/grit-pattern-matcher", + includes "crates/grit-util" + }, + $deps <: some bubble `$name = $specifier` where $name <: or { + includes "tree_sitter", + includes "tree-sitter", + includes "marzano" + } + } + - name: no_println_in_lsp + description: Don't use println!() in LSP code, it breaks the LSP stdio protocol. + level: error + body: | + engine marzano(0.1) + language rust + + `println!($_)` => . where { + $filename <: not includes "test.rs", + $absolute_filename <: includes "lsp", + } + - name: no_println_in_core + description: Don't use println or other debugging macros in core code. + level: error + body: | + engine marzano(0.1) + language rust + + `println!($args)` as $print where { + $outcome = ., + or { + $absolute_filename <: includes "crates/core", + $absolute_filename <: includes "crates/gritmodule", + $absolute_filename <: includes "crates/util", + and { $absolute_filename <: includes "crates/cli/", $outcome = `log::info!($args)` } + }, + // Allow tests and build utils + $absolute_filename <: not includes or { + "tests/", + "build.rs", + "test" + }, + $print <: not within `mod tests { $_ }` + } => $outcome diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index 7d7ba9360..0e75464de 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -1,7 +1,6 @@ use std::{ env, fs, io::{BufRead, BufReader}, - path::Path, process::{Command, Stdio}, sync::mpsc, thread, @@ -11,7 +10,6 @@ use std::{ use anyhow::Result; use assert_cmd::cargo::CommandCargoExt; use insta::assert_snapshot; -use tempfile::tempdir; use crate::common::{get_fixture, get_test_cmd}; @@ -325,23 +323,11 @@ fn tests_python_pattern_with_file_name() -> Result<()> { #[test] fn watch_mode_of_patterns_test() -> Result<()> { let (tx, rx) = mpsc::channel(); - let cur_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - let root_dir = Path::new(&cur_dir) - .parent() - .unwrap() - .parent() - .unwrap() - .to_owned(); - - let temp_dir = tempdir()?; - let temp_grit_dir = temp_dir.path().join(".grit"); - fs::create_dir(&temp_grit_dir)?; + let (temp_dir, temp_grit_dir) = get_fixture(".grit", false)?; let test_yaml_path = temp_grit_dir.join("grit.yaml"); - let source_yaml_path = root_dir.join(".grit").join("grit.yaml"); - fs::copy(source_yaml_path, &test_yaml_path)?; - let temp_dir_path = temp_dir.path().to_owned(); + let _cmd_handle = thread::spawn(move || { let mut cmd = Command::cargo_bin("marzano") .unwrap() From e28388d252d0796f83a087bf59323b33af1e8df3 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Sun, 30 Jun 2024 20:14:02 +0530 Subject: [PATCH 14/17] watch mode logs minor fix --- crates/cli/src/commands/patterns_test.rs | 7 +++++-- crates/cli_bin/tests/patterns_test.rs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 706c743df..1014b01da 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -313,7 +313,7 @@ async fn enable_watch_mode( let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx).unwrap(); debouncer.watcher().watch(path, RecursiveMode::Recursive)?; - log::info!("\n[Watch Mode] enabled on path: {}", path.display()); + log::info!("\n[Watch Mode] Enabled on path: {}", path.display()); let testable_patterns_map = testable_patterns .iter() @@ -388,7 +388,10 @@ async fn enable_watch_mode( } } - log::info!("[Watch Mode] Pattern to test: {:?}", patterns_to_test_names); + log::info!( + "[Watch Mode] Pattern(s) to test: {:?}", + patterns_to_test_names + ); if patterns_to_test_names.is_empty() { continue; } diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index 0e75464de..8974cec79 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -357,9 +357,9 @@ fn watch_mode_of_patterns_test() -> Result<()> { output.push(line); } let expected_output = vec![ - "[Watch Mode] enabled on path: .grit", + "[Watch Mode] Enabled on path: .grit", "[Watch Mode] File modified: \".grit/grit.yaml\"", - "[Watch Mode] Pattern to test: [\"our_cargo_use_long_dependency\", \"cargo_use_long_dependency\", \"no_treesitter_in_grit_crates\", \"no_println_in_lsp\", \"no_println_in_core\"]", + "[Watch Mode] Pattern(s) to test: [\"our_cargo_use_long_dependency\", \"cargo_use_long_dependency\", \"no_treesitter_in_grit_crates\", \"no_println_in_lsp\", \"no_println_in_core\"]", "Found 5 testable patterns.", ]; for expected_line in expected_output { From 00d411940815ce91fd200afe52d87ea2445cb490 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Sun, 30 Jun 2024 22:56:14 +0530 Subject: [PATCH 15/17] added test for watch mode case when all patterns are removed --- crates/cli_bin/tests/common/mod.rs | 2 +- crates/cli_bin/tests/patterns_test.rs | 57 +++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/crates/cli_bin/tests/common/mod.rs b/crates/cli_bin/tests/common/mod.rs index 8e8e94022..07a408a28 100644 --- a/crates/cli_bin/tests/common/mod.rs +++ b/crates/cli_bin/tests/common/mod.rs @@ -6,7 +6,7 @@ use assert_cmd::Command; use marzano_gritmodule::config::GRIT_GLOBAL_DIR_ENV; use tempfile::tempdir; -const BIN_NAME: &str = "marzano"; +pub const BIN_NAME: &str = "marzano"; #[allow(dead_code)] pub fn get_test_cmd() -> Result { diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index 8974cec79..f3d4f9a70 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -11,7 +11,7 @@ use anyhow::Result; use assert_cmd::cargo::CommandCargoExt; use insta::assert_snapshot; -use crate::common::{get_fixture, get_test_cmd}; +use crate::common::{get_fixture, get_test_cmd, BIN_NAME}; mod common; @@ -321,7 +321,7 @@ fn tests_python_pattern_with_file_name() -> Result<()> { } #[test] -fn watch_mode_of_patterns_test() -> Result<()> { +fn patterns_test_watch_mode_case_patterns_changed() -> Result<()> { let (tx, rx) = mpsc::channel(); let (temp_dir, temp_grit_dir) = get_fixture(".grit", false)?; @@ -329,7 +329,7 @@ fn watch_mode_of_patterns_test() -> Result<()> { let temp_dir_path = temp_dir.path().to_owned(); let _cmd_handle = thread::spawn(move || { - let mut cmd = Command::cargo_bin("marzano") + let mut cmd = Command::cargo_bin(BIN_NAME) .unwrap() .args(&["patterns", "test", "--watch"]) .current_dir(&temp_dir_path) @@ -371,3 +371,54 @@ fn watch_mode_of_patterns_test() -> Result<()> { } Ok(()) } + +#[test] +fn patterns_test_watch_mode_case_no_pattern_to_test() -> Result<()> { + let (tx, rx) = mpsc::channel(); + + let (temp_dir, temp_grit_dir) = get_fixture(".grit", false)?; + let test_yaml_path = temp_grit_dir.join("grit.yaml"); + let temp_dir_path = temp_dir.path().to_owned(); + + let _cmd_handle = thread::spawn(move || { + let mut cmd = Command::cargo_bin(BIN_NAME) + .unwrap() + .args(&["patterns", "test", "--watch"]) + .current_dir(&temp_dir_path) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("Failed to start command"); + + let stdout = BufReader::new(cmd.stdout.take().unwrap()); + let stderr = BufReader::new(cmd.stderr.take().unwrap()); + for line in stdout.lines().chain(stderr.lines()) { + if let Ok(line) = line { + tx.send(line).unwrap(); + } + } + }); + thread::sleep(Duration::from_secs(1)); + + fs::write(&test_yaml_path, "").unwrap(); + thread::sleep(Duration::from_secs(1)); + + let mut output = Vec::new(); + while let Ok(line) = rx.try_recv() { + output.push(line); + } + + let expected_output = vec![ + "[Watch Mode] Enabled on path: .grit", + "[Watch Mode] File modified: \".grit/grit.yaml\"", + "[Watch Mode] Pattern(s) to test: []", + ]; + for expected_line in expected_output { + assert!( + output.iter().any(|line| line.contains(expected_line)), + "Expected output not found: {}", + expected_line + ); + } + Ok(()) +} From 1ca1a43bab2879db49b6fcf57bae38b956cc9f85 Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Mon, 1 Jul 2024 16:24:40 +0530 Subject: [PATCH 16/17] avoid clone() and unwrap() where possible --- crates/cli/src/commands/patterns_test.rs | 30 ++++++++++++-------- crates/cli_bin/tests/patterns_test.rs | 4 +-- crates/core/src/pattern_compiler/compiler.rs | 14 ++++----- crates/gritmodule/src/config.rs | 5 ++++ 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/crates/cli/src/commands/patterns_test.rs b/crates/cli/src/commands/patterns_test.rs index 1014b01da..c27120676 100644 --- a/crates/cli/src/commands/patterns_test.rs +++ b/crates/cli/src/commands/patterns_test.rs @@ -310,26 +310,30 @@ async fn enable_watch_mode( .with_timeout(Duration::from_millis(10)) .with_notify_config(backend_config); // select backend via fish operator, here PollWatcher backend - let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx).unwrap(); + let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx)?; debouncer.watcher().watch(path, RecursiveMode::Recursive)?; log::info!("\n[Watch Mode] Enabled on path: {}", path.display()); let testable_patterns_map = testable_patterns .iter() - .map(|p| (p.local_name.clone().unwrap(), p.clone())) + .map(|p| (p.local_name.as_ref().unwrap(), p.as_ref())) .collect::>(); // event processing for result in rx { match result { Ok(event) => { - let modified_file_path = event.first().unwrap().path.clone(); + let modified_file_path = &event.first().unwrap().path; if !modified_file_path.is_file() { continue; } - let modified_file_path = modified_file_path.into_os_string().into_string().unwrap(); + let modified_file_path = modified_file_path + .clone() + .into_os_string() + .into_string() + .unwrap(); //temporary fix, until notify crate adds support for ignoring paths for path in &ignore_path { @@ -365,8 +369,11 @@ async fn enable_watch_mode( &modified_patterns, )?; for name in &modified_patterns_dependents_names { - if !patterns_to_test_names.contains(name) { - patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); + if !deleted_patterns_names.contains(&name) + && !patterns_to_test_names.contains(name) + { + patterns_to_test + .push((*testable_patterns_map.get(name).unwrap()).clone()); patterns_to_test_names.push(name.to_owned()); } } @@ -382,7 +389,8 @@ async fn enable_watch_mode( if !deleted_patterns_names.contains(&name) && !patterns_to_test_names.contains(name) { - patterns_to_test.push(testable_patterns_map.get(name).unwrap().clone()); + patterns_to_test + .push((*testable_patterns_map.get(name).unwrap()).clone()); patterns_to_test_names.push(name.to_owned()); } } @@ -413,9 +421,9 @@ fn get_dependents_of_target_patterns( testable_patterns: &Vec, target_patterns: &Vec, ) -> Result> { - let mut target_patterns_names = >::new(); + let mut target_patterns_names = Vec::new(); for p in target_patterns { - target_patterns_names.push(p.local_name.clone().unwrap()); + target_patterns_names.push(p.local_name.as_ref().unwrap()); } let mut dependents_names = >::new(); @@ -425,9 +433,7 @@ fn get_dependents_of_target_patterns( let body = format!("{}()", p.local_name.as_ref().unwrap()); let lang = PatternLanguage::get_language(&p.body); let libs = libs.get_language_directory_or_default(lang)?; - let rich_pattern = resolver - .make_pattern(&body, p.local_name.to_owned()) - .unwrap(); + let rich_pattern = resolver.make_pattern(&body, p.local_name.to_owned())?; let src = rich_pattern.body; let mut parser = MarzanoGritParser::new()?; diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index f3d4f9a70..e8c50bcaf 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -349,7 +349,7 @@ fn patterns_test_watch_mode_case_patterns_changed() -> Result<()> { thread::sleep(Duration::from_secs(1)); let content = fs::read_to_string(&test_yaml_path).expect("Unable to read the file"); - fs::write(&test_yaml_path, content).unwrap(); + fs::write(&test_yaml_path, content)?; thread::sleep(Duration::from_secs(1)); let mut output = Vec::new(); @@ -400,7 +400,7 @@ fn patterns_test_watch_mode_case_no_pattern_to_test() -> Result<()> { }); thread::sleep(Duration::from_secs(1)); - fs::write(&test_yaml_path, "").unwrap(); + fs::write(&test_yaml_path, "")?; thread::sleep(Duration::from_secs(1)); let mut output = Vec::new(); diff --git a/crates/core/src/pattern_compiler/compiler.rs b/crates/core/src/pattern_compiler/compiler.rs index 8c6345ed5..e4663a16a 100644 --- a/crates/core/src/pattern_compiler/compiler.rs +++ b/crates/core/src/pattern_compiler/compiler.rs @@ -571,7 +571,7 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( libs: &BTreeMap, src: &str, parser: &mut MarzanoGritParser, - target_patterns: &[String], + target_patterns: &[&String], ) -> Result> { let mut dependents = >::new(); let node_like = "nodeLike"; @@ -586,12 +586,12 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( foreign_functions: foreign_file, } = defs_to_filenames(libs, parser, tree.root_node())?; - let name_to_filename: BTreeMap = pattern_file + let name_to_filename: BTreeMap<&String, &String> = pattern_file .iter() - .map(|(k, v)| (k.clone(), (v.clone()))) - .chain(predicate_file.iter().map(|(k, v)| (k.clone(), (v.clone())))) - .chain(function_file.iter().map(|(k, v)| (k.clone(), (v.clone())))) - .chain(foreign_file.iter().map(|(k, v)| (k.clone(), (v.clone())))) + .map(|(k, v)| (k, (v))) + .chain(predicate_file.iter().map(|(k, v)| (k, (v)))) + .chain(function_file.iter().map(|(k, v)| (k, (v)))) + .chain(foreign_file.iter().map(|(k, v)| (k, (v)))) .collect(); let mut traversed_stack = >::new(); @@ -609,7 +609,7 @@ pub fn get_dependents_of_target_patterns_by_traversal_from_src( let name = name.text()?; let name = name.trim().to_string(); - if target_patterns.contains(&name) { + if target_patterns.contains(&&name) { while let Some(e) = traversed_stack.pop() { dependents.push(e); } diff --git a/crates/gritmodule/src/config.rs b/crates/gritmodule/src/config.rs index b1b95b2ec..10b2aa128 100644 --- a/crates/gritmodule/src/config.rs +++ b/crates/gritmodule/src/config.rs @@ -129,6 +129,11 @@ pub struct GritPatternTestInfo { pub config: GritPatternTestConfig, pub local_name: Option, } +impl AsRef for GritPatternTestInfo { + fn as_ref(&self) -> &GritPatternTestInfo { + self + } +} #[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)] pub struct ModuleGritPattern { From 58957570dba4313b4980c1d6e6053f3ef861c6dd Mon Sep 17 00:00:00 2001 From: Ashu999 Date: Fri, 5 Jul 2024 11:10:46 +0530 Subject: [PATCH 17/17] use get_test_process_cmd() to get base command --- crates/cli_bin/tests/common/mod.rs | 10 +++++++++- crates/cli_bin/tests/patterns_test.rs | 9 ++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/cli_bin/tests/common/mod.rs b/crates/cli_bin/tests/common/mod.rs index 07a408a28..3c79929f3 100644 --- a/crates/cli_bin/tests/common/mod.rs +++ b/crates/cli_bin/tests/common/mod.rs @@ -1,6 +1,7 @@ -use std::{env, path}; +use std::{env, path, process}; use anyhow::Result; +use assert_cmd::cargo::CommandCargoExt; use assert_cmd::Command; use marzano_gritmodule::config::GRIT_GLOBAL_DIR_ENV; @@ -15,6 +16,13 @@ pub fn get_test_cmd() -> Result { Ok(cmd) } +#[allow(dead_code)] +pub fn get_test_process_cmd() -> Result { + let mut cmd = process::Command::cargo_bin(BIN_NAME)?; + cmd.env("GRIT_TELEMETRY_DISABLED", "true"); + Ok(cmd) +} + // This is used in tests #[allow(dead_code)] pub fn get_fixtures_root() -> Result { diff --git a/crates/cli_bin/tests/patterns_test.rs b/crates/cli_bin/tests/patterns_test.rs index e8c50bcaf..bbca569d4 100644 --- a/crates/cli_bin/tests/patterns_test.rs +++ b/crates/cli_bin/tests/patterns_test.rs @@ -1,17 +1,16 @@ use std::{ env, fs, io::{BufRead, BufReader}, - process::{Command, Stdio}, + process::Stdio, sync::mpsc, thread, time::Duration, }; use anyhow::Result; -use assert_cmd::cargo::CommandCargoExt; use insta::assert_snapshot; -use crate::common::{get_fixture, get_test_cmd, BIN_NAME}; +use crate::common::{get_fixture, get_test_cmd, get_test_process_cmd}; mod common; @@ -329,7 +328,7 @@ fn patterns_test_watch_mode_case_patterns_changed() -> Result<()> { let temp_dir_path = temp_dir.path().to_owned(); let _cmd_handle = thread::spawn(move || { - let mut cmd = Command::cargo_bin(BIN_NAME) + let mut cmd = get_test_process_cmd() .unwrap() .args(&["patterns", "test", "--watch"]) .current_dir(&temp_dir_path) @@ -381,7 +380,7 @@ fn patterns_test_watch_mode_case_no_pattern_to_test() -> Result<()> { let temp_dir_path = temp_dir.path().to_owned(); let _cmd_handle = thread::spawn(move || { - let mut cmd = Command::cargo_bin(BIN_NAME) + let mut cmd = get_test_process_cmd() .unwrap() .args(&["patterns", "test", "--watch"]) .current_dir(&temp_dir_path)