Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: watch mode for patterns test #298

Merged
merged 26 commits into from
Jul 8, 2024
Merged

Conversation

Ashu999
Copy link
Contributor

@Ashu999 Ashu999 commented May 1, 2024

Fixes: #51
/claim #51

Summary by CodeRabbit

  • New Features

    • Introduced watch mode for testing patterns with grit patterns test --watch.
    • Added YAML support for pattern files alongside Grit and Markdown formats.
  • Enhancements

    • Improved dependency management and added new dependencies.
    • Added error handling and logging for unsupported output formats.
    • Enhanced pattern testing functionality and file modification tracking.
  • Bug Fixes

    • Improved error handling for empty patterns in test results.
  • Testing

    • Added new test cases for watch mode functionality.
    • Introduced integration test patterns for dependency management and code restrictions.
  • Documentation

    • Updated .gitignore to ignore specific log files and grit modules.

Greptile Summary

This is an auto-generated summary

  • Introduced watch mode for pattern testing with real-time file monitoring
  • Enhanced error handling and logging in pattern testing
  • Modified function parameters for better efficiency in compiler.rs
  • Optimized dependency management
  • Improved handling of modified and deleted patterns during testing

@Ashu999 Ashu999 changed the title feature: watch mode for patterns test feat: watch mode for patterns test May 2, 2024
@Ashu999 Ashu999 marked this pull request as ready for review May 20, 2024 19:21
@Ashu999 Ashu999 requested a review from a team as a code owner May 20, 2024 19:21
Copy link
Contributor

coderabbitai bot commented May 20, 2024

Walkthrough

Walkthrough

This update introduces a watch mode for the grit patterns test command. The watch mode monitors changes in pattern files in the .grit directory and automatically reruns the relevant tests. The system efficiently manages dependencies, ensuring only affected patterns are retested. Key changes involve new dependencies, updated function signatures, and additional test cases to verify the watch functionality.

Changes

Files/Paths Change Summary
.../Cargo.toml Added dependencies (notify, notify-debouncer-mini) and features for axoupdater and grit_beta.
.../commands/mod.rs Added an argument to run_patterns_test in the run_command function.
.../commands/patterns.rs Added Clone trait to PatternsTestArgs struct and added watch field with #[clap(long = "watch")] attribute.
.../commands/patterns_test.rs Introduced functions like enable_watch_mode, and enhanced logic for testing patterns, handling file changes.
.../commands/plumbing.rs Passed reference to PatternsTestArgs with watch: false in run_plumbing function.
.../tests/patterns_test.rs Added new test functions to verify watch mode functionality.
.grit/test/test.yaml Introduced new integration testing patterns related to dependency management in Cargo.toml.
.grit/grit.yaml Added new pattern cargo_use_long_dependency with error level for detecting long dependencies in Cargo.toml.
.../pattern_compiler/compiler.rs Added get_dependents_of_target_patterns_by_traversal_from_src function for dependency traversal in patterns.
.../gritmodule/src/config.rs Added Clone and Debug traits to GritPatternTestConfig and GritPatternTestInfo structs.
.../gritmodule/src/parser.rs Added support for YAML pattern files and updated PatternFileExt enum to handle them.
.../gritmodule/src/resolver.rs Updated arguments for get_patterns_from_yaml function in resolver module.
.../gritmodule/src/yaml.rs Function get_patterns_from_yaml now accepts Option<ModuleRepo> type; switched to tokio::task::spawn_local.
.../cli_bin/fixtures/.grit/.gitignore Added patterns to ignore .gritmodules* files and .log files.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant FileSystem
    participant TestRunner
    
    User->>+CLI: run `grit patterns test --watch`
    CLI->>+FileSystem: Watch for changes in .grit directory
    FileSystem-->>CLI: Notify on file changes
    CLI->>+TestRunner: Re-run affected pattern tests
    TestRunner->>CLI: Return test results
    CLI-->>User: Display test results
Loading

Assessment against linked issues

Objective (Issue #51) Addressed Explanation
Implement grit patterns test --watch mode
Watch mode should monitor .grit directory for file changes
Re-run only affected pattern tests on file changes
Handle pattern dependencies for re-running tests
Include at least two tests, including integration tests for the final binary

Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 69c48e2 and 43dc81e.

Files selected for processing (7)
  • crates/cli/src/commands/patterns_test.rs (4 hunks)
  • crates/cli_bin/fixtures/.grit/.gitignore (1 hunks)
  • crates/core/src/pattern_compiler/compiler.rs (1 hunks)
  • crates/gritmodule/src/config.rs (3 hunks)
  • crates/gritmodule/src/parser.rs (5 hunks)
  • crates/gritmodule/src/resolver.rs (2 hunks)
  • crates/gritmodule/src/yaml.rs (3 hunks)
Files skipped from review due to trivial changes (1)
  • crates/cli_bin/fixtures/.grit/.gitignore
Files skipped from review as they are similar to previous changes (6)
  • crates/cli/src/commands/patterns_test.rs
  • crates/core/src/pattern_compiler/compiler.rs
  • crates/gritmodule/src/config.rs
  • crates/gritmodule/src/parser.rs
  • crates/gritmodule/src/resolver.rs
  • crates/gritmodule/src/yaml.rs

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Comment on lines 308 to 354
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!("\n[Watch Mode] enabled on path: {}", path.display());

// event pocessing
for result in rx {
match result {
Ok(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
);

let _ = run_patterns_test(arg, flags, Some(affected_patterns_names)).await;
});
}
Err(error) => {
log::error!("Error {error:?}")
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of watch mode in enable_watch_mode function is well-structured. However, consider adding error handling for the unwrap calls to prevent potential panics in production.

- let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx).unwrap();
+ let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx)
+     .expect("Failed to create debouncer");

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
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!("\n[Watch Mode] enabled on path: {}", path.display());
// event pocessing
for result in rx {
match result {
Ok(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
);
let _ = run_patterns_test(arg, flags, Some(affected_patterns_names)).await;
});
}
Err(error) => {
log::error!("Error {error:?}")
}
}
}
}
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)
.expect("Failed to create debouncer");
debouncer
.watcher()
.watch(path, RecursiveMode::Recursive)
.unwrap();
log::info!("\n[Watch Mode] enabled on path: {}", path.display());
// event pocessing
for result in rx {
match result {
Ok(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
);
let _ = run_patterns_test(arg, flags, Some(affected_patterns_names)).await;
});
}
Err(error) => {
log::error!("Error {error:?}")
}
}
}
}

Comment on lines 356 to 411
async fn get_affected_patterns(modified_file_path: PathBuf) -> Result<Vec<String>> {
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::<Vec<_>>();

let mut affected_patterns_names = <Vec<String>>::new();
for affected_pattern in &affected_patterns {
affected_patterns_names.push(affected_pattern.local_name.clone().unwrap());
}
let mut affected_patterns_dependents_names = <Vec<String>>::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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

The search results indicate that there are numerous test functions across the codebase, but none specifically target the get_affected_patterns function. This suggests that there might not be direct unit tests for this function.

To ensure comprehensive testing of the get_affected_patterns function, it is crucial to add or verify the existence of tests that cover various scenarios, especially complex dependency scenarios.

  • Action Required: Add unit tests for the get_affected_patterns function to ensure it correctly handles complex dependency scenarios.
Analysis chain

The get_affected_patterns function effectively identifies patterns affected by file modifications. Ensure comprehensive testing to validate that it correctly handles complex dependency scenarios.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `get_affected_patterns` correctly identifies affected patterns and their dependents.

# Test: Run unit tests for `get_affected_patterns`.
cat tests/patterns_test.rs | grep "test_get_affected_patterns"

Length of output: 117



Script:

#!/bin/bash
# Locate the definition of the `get_affected_patterns` function
ast-grep --lang rust --pattern 'async fn get_affected_patterns($_) -> $_ { $$$ }'

# Locate test functions related to `get_affected_patterns`
rg 'get_affected_patterns' --type rust

Length of output: 5187



Script:

#!/bin/bash
# Search for test functions in the `crates/cli/src/commands/patterns_test.rs` file
rg '#\[test\]' crates/cli/src/commands/patterns_test.rs -A 2

Length of output: 62



Script:

#!/bin/bash
# Search for test functions across the entire codebase
rg '#\[test\]' -A 2 --type rust

Length of output: 138229

Copy link
Contributor

@morgante morgante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the start, but this should be doable in a cleaner way with less code.

Ok(())
}

pub(crate) async fn run_patterns_test(
arg: PatternsTestArgs,
flags: GlobalFormatFlags,
watch_mode_args: Option<Vec<String>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't feel like the right interface.

I think you should probably extract the core / repeated logic into a test_patterns function. Then the filtering of which patterns to run is kind of the same whether it's based on watch or exclude or something else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of using arg.filter here, but it only takes single string as input and uses regex.
If it's ok with you, I can modify the behavior of filter to support multiple arguments, not use regex and filter based on exact pattern name instead. This is how I have seen most of the testing tools work, when targeting a specific test(s).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to change the interface. No the point is you should have an inner function with a signature like test_patterns(..., targeted_patterns: Vec<String>). The outer / top functions (like arg.filter) chooses which patterns to run, then invokes the inner function to do the actual execution. The same function can also be used for the watch inner, since the core of what it does is just identifying the correct subset of patterns to run.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understood the point (from 1st comment) about keeping the filtration logic together, since they share similar purpose and variables. I can take some common filtration logic from get_marzano_pattern_test_results as well.

regarding the test execution, it happens inside get_marzano_pattern_test_results, are you suggesting to modify it into a new function test_patterns and make it an inner function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it doesn't need to be an inner function. You might not need a new function actually, but instead a structure like this:

  • run_patterns_test(), root that handles computing available patterns, invokes the initial test, and (if configured) calls enable_watch_mode with the patterns found earlier.
  • enable_watch_mode listens to notifications, and if files are updated, updates the patterns associated with those files. Importantly, it should not invoke run_patterns_test() or re-discover patterns - just (a) mutate the patterns based on the file change and (b) invoke get_marzano_pattern_test_results itself.
  • If necessary, you could have a do_patterns_test function that abstracts some of the shared logic from enable_watch_mode and run_patterns_test but it actually looks unnecessary now that I look deeper.

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
Comment on lines 359 to 360
let (patterns, _) = resolve_from_cwd(&Source::Local).await?;
let affected_patterns = collect_testable_patterns(patterns.clone())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebuilding this from scratch every time is inefficient. We should instead just be looking at the existing retrieve patterns that were first found in the original top level command, then filtering those down to the ones related to the modified file.

Copy link
Contributor Author

@Ashu999 Ashu999 May 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in: #298 (comment), also code is now refactored (moved filter logic inside run_patterns_test()) to read patterns, testable only once per modification trigger.

}
Ok(affected_patterns_names)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reparsing shouldn't be necessary. If we need additional metadata as the patterns are built in the first place, collect that in the compiler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain this a little more, how to collect the required data in in the compiler? does it happen inside filter_libs() in compiler.rs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point is you shouldn't need to reparse any/all patterns to get the dependency tree. Every pattern is already parsed at least one when running the initial test, so you should build the mapping when that happens instead of running again.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Outside diff range and nitpick comments (1)
crates/cli/src/commands/patterns_test.rs (1)

Line range hint 258-304: The implementation of the run_patterns_test function with the new watch_mode_arg parameter is well-structured. However, consider handling the case where the watch_mode_arg is None more explicitly to avoid potential confusion about the function's behavior in non-watch mode.

if let Some(watch_mode_arg) = watch_mode_arg {
    // Existing watch mode logic here
} else {
    // Logic for non-watch mode
}

Comment on lines 307 to 359
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
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!("\n[Watch Mode] enabled on path: {}", path.display());

// event pocessing
for result in rx {
match result {
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 _ = run_patterns_test(arg, flags, Some(modified_file_path)).await;
});
}
Err(error) => {
log::error!("Error {error:?}")
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enable_watch_mode function effectively sets up file watching and event handling. However, the use of .unwrap() could lead to panics in production if errors occur. Replace .unwrap() with more robust error handling.

let mut debouncer = new_debouncer_opt::<_, notify::PollWatcher>(debouncer_config, tx)
    .expect("Failed to create debouncer");

Comment on lines 361 to 416
fn get_affected_patterns(
modified_file_path: String,
patterns: Vec<ResolvedGritDefinition>,
libs: &PatternsDirectory,
testable_patterns: &Vec<GritPatternTestInfo>,
) -> Result<Vec<String>> {
let affected_patterns = testable_patterns
.iter()
.filter(|p| {
p.config.path.is_some() && p.config.path.as_ref().unwrap() == &modified_file_path
})
.collect::<Vec<_>>();

let mut affected_patterns_names = <Vec<String>>::new();
for affected_pattern in &affected_patterns {
affected_patterns_names.push(affected_pattern.local_name.clone().unwrap());
}
let mut affected_patterns_dependents_names = <Vec<String>>::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);
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()))
.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(
&libs,
&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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

The search results indicate that there are numerous test functions across the codebase, but none of them seem to be directly related to the get_affected_patterns function. Additionally, the only occurrence of get_affected_patterns is in the crates/cli/src/commands/patterns_test.rs file, which does not contain any test functions.

Given this information, it appears that the get_affected_patterns function is not currently covered by unit tests. This is a significant oversight, especially considering the function's importance in determining affected patterns based on file changes.

To address this, I recommend adding unit tests for the get_affected_patterns function to ensure it handles complex dependency scenarios correctly.

  • Add unit tests for the get_affected_patterns function in crates/cli/src/commands/patterns_test.rs.
Analysis chain

The get_affected_patterns function is crucial for determining which patterns are affected by file changes. Ensure that this function is covered by unit tests, especially to handle complex dependency scenarios.


The previous attempts to locate unit tests for the get_affected_patterns function did not yield any results. To ensure thorough verification, we should search for all test functions in the crates/cli/src/commands/patterns_test.rs file and manually inspect if any of them test the get_affected_patterns function.


The previous attempts to locate test functions in crates/cli/src/commands/patterns_test.rs did not yield any results. This suggests that there might not be any test functions in that file, or they might be named differently.

To ensure thorough verification, we should search for all test functions across the entire codebase and check if any of them test the get_affected_patterns function.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `get_affected_patterns` correctly identifies affected patterns and their dependents.

# Test: Run unit tests for `get_affected_patterns`.
cat tests/patterns_test.rs | grep "test_get_affected_patterns"

Length of output: 117



Script:

#!/bin/bash
# Locate the definition of the `get_affected_patterns` function
ast-grep --lang rust --pattern 'fn get_affected_patterns($_, $_, $_, $_) -> $_ { $$$ }'

# Search for test functions that might be testing `get_affected_patterns`
rg 'get_affected_patterns' --type rust

Length of output: 5042



Script:

#!/bin/bash
# Search for test functions in `crates/cli/src/commands/patterns_test.rs` and check if they call `get_affected_patterns`
rg -A 5 'fn test_' crates/cli/src/commands/patterns_test.rs | grep -B 5 'get_affected_patterns'

Length of output: 95



Script:

#!/bin/bash
# List all test functions in `crates/cli/src/commands/patterns_test.rs`
rg 'fn test_' crates/cli/src/commands/patterns_test.rs

Length of output: 56



Script:

#!/bin/bash
# List all test functions across the entire codebase
rg 'fn test_' --type rust

# Search for calls to `get_affected_patterns` across the entire codebase
rg 'get_affected_patterns' --type rust

Length of output: 11294

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Outside diff range and nitpick comments (2)
crates/cli/src/commands/patterns_test.rs (2)

6-6: Consider adding a comment explaining the purpose of DEFAULT_FILE_NAME for better code clarity.


444-485: The function get_dependents_of_target_patterns is well-structured but consider adding comments to explain the logic, especially how dependencies are resolved.

Comment on lines 570 to 664
pub fn get_dependents_of_target_patterns_by_traversal_from_src(
libs: &BTreeMap<String, String>,
src: &str,
parser: &mut MarzanoGritParser,
will_autowrap: bool,
target_patterns: &Vec<String>,
) -> Result<Vec<String>> {
let mut dependents = <Vec<String>>::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 = <Vec<String>>::new();

// gross but necessary due to running these patterns before and after each file
let mut stack: Vec<Tree> = 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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider optimizing the traversal logic to avoid redundant checks.

The current implementation of get_dependents_of_target_patterns_by_traversal_from_src seems to perform redundant checks and might benefit from optimization. Specifically, the traversal logic could be streamlined to avoid unnecessary stack operations and checks. Consider using a more efficient data structure or algorithm to handle the traversal and dependency resolution.

crates/gritmodule/src/resolver.rs Outdated Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Outdated Show resolved Hide resolved
Comment on lines 487 to 510
async fn get_modified_and_deleted_patterns(
modified_path: &String,
testable_patterns: &Vec<GritPatternTestInfo>,
) -> Result<(Vec<GritPatternTestInfo>, Vec<GritPatternTestInfo>)> {
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 = <Vec<String>>::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 = <Vec<GritPatternTestInfo>>::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((modified_patterns, deleted_patterns))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the function get_modified_and_deleted_patterns handles cases where pattern.config.path might be None, which could lead to unwrapping a None value.

if let Some(pattern_path) = pattern.config.path.as_ref() {
    if pattern_path == modified_path && !modified_pattern_names.contains(pattern.local_name.as_ref().unwrap()) {
        deleted_patterns.push(pattern.clone());
    }
} else {
    log::warn!("Pattern config path is missing for pattern: {:?}", pattern.local_name);
}

crates/cli/src/commands/patterns_test.rs Outdated Show resolved Hide resolved
@@ -39,7 +39,7 @@ pub fn get_grit_config(source: &str, source_path: &str) -> Result<GritConfig> {

pub fn get_patterns_from_yaml(
file: &RichFile,
source_module: &ModuleRepo,
source_module: &Option<ModuleRepo>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

The functions fetch_grit_module and get_grit_module_dir in fetcher.rs do not handle the None case for Option<ModuleRepo>. They need to be updated to manage the optional nature of ModuleRepo.

  • fetch_grit_module at lines 327 and 374 in crates/gritmodule/src/fetcher.rs
  • get_grit_module_dir at lines 317 and 364 in crates/gritmodule/src/fetcher.rs
Analysis chain

The modification to accept an Option<ModuleRepo> in get_patterns_from_yaml enhances flexibility in handling module repositories, which is crucial for robust pattern resolution.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that all functions handling `ModuleRepo` correctly manage the `None` case.

# Test: Search for functions using `ModuleRepo`. Expect: Proper handling of `None`.
rg --type rust $'fn .*\\(.*ModuleRepo.*\\)'

Length of output: 923



Script:

#!/bin/bash
# Extract the implementations of functions using `ModuleRepo` to verify handling of `Option<ModuleRepo>`

# Extract functions from config.rs
ast-grep --lang rust --pattern $'fn url(&self, local_repo: &ModuleRepo, local_path: &Path) -> String { $$$ }' crates/gritmodule/src/config.rs

# Extract functions from fetcher.rs
ast-grep --lang rust --pattern $'fn fetch_grit_module(&self, repo: &ModuleRepo) -> Result<String> { $$$ }' crates/gritmodule/src/fetcher.rs
ast-grep --lang rust --pattern $'fn clone_repo(&self, repo: &ModuleRepo, target_dir: &PathBuf) -> Result<&PathBuf> { $$$ }' crates/gritmodule/src/fetcher.rs
ast-grep --lang rust --pattern $'fn get_grit_module_dir(&self, repo: &ModuleRepo) -> PathBuf { $$$ }' crates/gritmodule/src/fetcher.rs

Length of output: 1875

Comment on lines +266 to +269
&PatternsTestArgs {
update: false,
verbose: false,
watch: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

The watch field in PatternsTestArgs is defined but not utilized in any meaningful way across the codebase. It is only set to false in the initialization within plumbing.rs and not referenced elsewhere.

  • patterns_test.rs: The PatternsTestArgs struct is used in function parameters, but the watch field is not referenced.
  • patterns.rs: The PatternsTestArgs struct is defined, but the watch field is not utilized.
  • plumbing.rs: The watch field is set to false but not used in any logic.

This indicates that the watch field is not integrated into the functionality as intended.

  • crates/cli/src/commands/patterns_test.rs: Lines 46, 264, 302
  • crates/cli/src/commands/patterns.rs: Lines 32, 40
  • crates/cli/src/commands/plumbing.rs: Lines 27, 266
Analysis chain

The addition of the watch field in PatternsTestArgs is well-implemented and aligns with the PR objectives. Ensure that this new field is correctly utilized across all relevant function calls.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that the `watch` field is utilized correctly in function calls.

# Test: Search for the function usage. Expect: Correct usage of the new field.
rg --type rust $'PatternsTestArgs {.*watch.*}'

Length of output: 176



Script:

#!/bin/bash
# Description: Verify that the `watch` field is utilized correctly in function calls.

# Test: Search for the function usage. Expect: Correct usage of the new field.
rg --type rust 'PatternsTestArgs \{[^}]*watch[^}]*\}'

Length of output: 55



Script:

#!/bin/bash
# Description: Search for all instances of `PatternsTestArgs` in the codebase.

# Test: Search for all instances of `PatternsTestArgs`. Expect: Identify if the `watch` field is being utilized or omitted.
rg --type rust 'PatternsTestArgs'

Length of output: 598



Script:

#!/bin/bash
# Description: Extract and display lines where `PatternsTestArgs` is used to verify if the `watch` field is being utilized.

# Extract lines from `patterns_test.rs`
rg --type rust --line-number 'PatternsTestArgs' crates/cli/src/commands/patterns_test.rs

# Extract lines from `patterns.rs`
rg --type rust --line-number 'PatternsTestArgs' crates/cli/src/commands/patterns.rs

# Extract lines from `plumbing.rs`
rg --type rust --line-number 'PatternsTestArgs' crates/cli/src/commands/plumbing.rs

Length of output: 540



Script:

#!/bin/bash
# Description: Extract and display surrounding lines where `PatternsTestArgs` is used to verify if the `watch` field is being utilized.

# Extract surrounding lines from `patterns_test.rs`
rg --type rust --context 5 --line-number 'PatternsTestArgs' crates/cli/src/commands/patterns_test.rs

# Extract surrounding lines from `patterns.rs`
rg --type rust --context 5 --line-number 'PatternsTestArgs' crates/cli/src/commands/patterns.rs

# Extract surrounding lines from `plumbing.rs`
rg --type rust --context 5 --line-number 'PatternsTestArgs' crates/cli/src/commands/plumbing.rs

Length of output: 3336

mut patterns: Vec<ModuleGritPattern>,
) -> Vec<GritPatternTestInfo> {
let testable_patterns: Vec<GritPatternTestInfo> = patterns
.iter_mut()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use into_iter and avoid needing to clone.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
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);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use &String instead of &str for consistency with modified_pattern_names.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Outside diff range and nitpick comments (1)
crates/core/src/pattern_compiler/compiler.rs (1)

Line range hint 686-703: Enhance error handling in src_to_problem_libs.

The function src_to_problem_libs could improve its error handling by explicitly checking the results of TargetLanguage::from_tree and PatternBuilder::start before proceeding. This would prevent runtime panics in cases where these functions fail.

Comment on lines 570 to 664
pub fn get_dependents_of_target_patterns_by_traversal_from_src(
libs: &BTreeMap<String, String>,
src: &str,
parser: &mut MarzanoGritParser,
will_autowrap: bool,
target_patterns: &[String],
) -> Result<Vec<String>> {
let mut dependents = <Vec<String>>::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 = <Vec<String>>::new();

// gross but necessary due to running these patterns before and after each file
let mut stack: Vec<Tree> = 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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize the traversal logic to avoid redundant checks.

The current implementation of get_dependents_of_target_patterns_by_traversal_from_src seems to perform redundant checks and might benefit from optimization. Specifically, the traversal logic could be streamlined to avoid unnecessary stack operations and checks. Consider using a more efficient data structure or algorithm to handle the traversal and dependency resolution.

Comment on lines 666 to 683
fn find_child_tree_definition(
files: &BTreeMap<String, String>,
parser: &mut MarzanoGritParser,
libs: &BTreeMap<String, String>,
traversed_stack: &mut Vec<String>,
name: &str,
) -> Result<Option<Tree>> {
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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refine error handling and simplify logic in find_child_tree_definition.

Consider handling potential errors from parser.parse_file more gracefully. Additionally, the logic to check for cycles could be optimized by using a set instead of a vector for traversed_stack, which would offer O(1) complexity for the containment check.

debouncer
.watcher()
.watch(path, RecursiveMode::Recursive)
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.unwrap();
?

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
Comment on lines 623 to 659
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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to repeat like this is a code smell. Instead of having a separate lookup for each kind we should be able to collapse into a main map before hand then just traverse that.

let mut traversed_stack = <Vec<String>>::new();

// gross but necessary due to running these patterns before and after each file
let mut stack: Vec<Tree> = if will_autowrap {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just leave this logic out - we don't need to worry about autowrap / before patterns for --watch.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Comment on lines 327 to 333
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();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential errors gracefully when unwrapping.

Avoid using .unwrap() directly on potentially None values to prevent panics.

let modified_file_path = event.first().map(|e| e.path.clone()).unwrap_or_else(|| {
    log::warn!("Event does not contain a valid path.");
    return;
});

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Outside diff range and nitpick comments (1)
crates/cli/src/commands/plumbing.rs (1)

Line range hint 49-49: Ensure proper error handling and context in async functions.

The function run_plumbing performs various operations that could fail, such as reading inputs and parsing JSON. It's crucial to ensure that all potential errors are handled gracefully and that sufficient context is provided for each error to aid debugging.

- let input: PlumbingApplyInput = serde_json::from_str::<PlumbingApplyInput>(&buffer).map_err(|e| {
-     anyhow!(
-         "Failed to parse input JSON: {}. Ensure that input matches schema \
-         {{ pattern_body: string; pattern_libs: {{ [string]: string }}; paths: string[]; }}",
-         e
-     )
- })?;
+ let input: PlumbingApplyInput = serde_json::from_str::<PlumbingApplyInput>(&buffer).with_context(|| format!("Failed to parse input JSON: {}", &buffer))?;

@@ -535,7 +535,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)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure proper error handling for get_patterns_from_yaml to prevent potential panics.

As previously commented, there's an unwrapping of the result from get_patterns_from_yaml which can lead to panics if an error occurs. It's safer to handle this potential error explicitly to avoid runtime issues.

let yaml_patterns = match get_patterns_from_yaml(&config, &Some(module.to_owned()), &repo_root) {
    Ok(patterns) => patterns,
    Err(e) => return Err(e.into()),
};

Tip

Codebase Verification

Review the error handling for get_patterns_from_yaml across multiple locations.

  • Inconsistent error handling found:

    • crates/gritmodule/src/yaml.rs:
      let patterns = get_patterns_from_yaml(&grit_yaml, &repo, &None).unwrap();

    The use of .unwrap() can cause the application to panic if an error occurs. Consider using proper error handling mechanisms like match, unwrap_or_else, or the ? operator to ensure the application handles errors gracefully.

Please review and update the error handling as necessary.

Analysis chain

Review the handling of get_patterns_from_yaml results across multiple locations.

It's crucial to ensure consistent and robust error handling throughout the codebase. The repeated emphasis on handling errors from get_patterns_from_yaml is to ensure that the application does not crash unexpectedly.

Also applies to: 589-589

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the error handling consistency for `get_patterns_from_yaml`.

# Test: Search for all usages of `get_patterns_from_yaml` and ensure they use proper error handling.
rg --type rust $'get_patterns_from_yaml'

Length of output: 722

Comment on lines 324 to 380
#[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(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure robustness in the watch mode test implementation.

The implementation of the watch mode test is comprehensive, but it relies heavily on unwrapping, which could lead to panics. Consider adding error handling to improve the robustness of the test.

- let cur_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+ let cur_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[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(())
}
#[test]
fn watch_mode_of_patterns_test() -> Result<()> {
let (tx, rx) = mpsc::channel();
let cur_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
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(())
}

@@ -277,7 +277,7 @@ impl Ord for ResolvedGritDefinition {

pub fn pattern_config_to_model(
pattern: GritDefinitionConfig,
source: &ModuleRepo,
source: &Option<ModuleRepo>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor the function signature for clarity and simplicity.

The pattern_config_to_model function's signature is complex. Consider simplifying it or adding detailed documentation to clarify its purpose and usage.

- pub fn pattern_config_to_model(pattern: GritDefinitionConfig, source: &Option<ModuleRepo>) -> Result<ModuleGritPattern> {
+ pub fn pattern_config_to_model(pattern: GritDefinitionConfig, source: Option<&ModuleRepo>) -> Result<ModuleGritPattern> {
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
source: &Option<ModuleRepo>,
source: Option<&ModuleRepo>,

@@ -0,0 +1,82 @@
# this file is used for integration testing of `grit patterns test --watch`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't invent a new way to do integration tests. We already have fixtures and existing conventions. Look at them and follow that.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

.grit/grit.yaml Outdated
@@ -10,6 +10,19 @@ patterns:
"language-submodules",
"language-metavariables"
}
- name: cargo_use_long_dependency
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider renaming the pattern name our_cargo_use_long_dependency.

The current pattern name might be confusing. Consider renaming it to something more descriptive like long_dependency_in_cargo_toml.

-  - name: our_cargo_use_long_dependency
+  - name: long_dependency_in_cargo_toml
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: cargo_use_long_dependency
- name: long_dependency_in_cargo_toml

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Outdated Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Comment on lines 323 to 373
#[test]
fn patterns_test_watch_mode_case_patterns_changed() -> 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));

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();
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: [\"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(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure robustness in the watch mode test implementation.

The implementation of the watch mode test is comprehensive, but it relies heavily on unwrapping, which could lead to panics. Consider adding error handling to improve the robustness of the test.

- let cur_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+ let cur_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");

Committable suggestion was skipped due to low confidence.

Comment on lines 375 to 424
#[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(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure robustness in the watch mode test implementation.

The implementation of the watch mode test is comprehensive, but it relies heavily on unwrapping, which could lead to panics. Consider adding error handling to improve the robustness of the test.

- let cur_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+ let cur_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@morgante morgante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to be more disciplined with clones.

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
.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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use ? where possible.

Comment on lines 327 to 332
let modified_file_path = event.first().unwrap().path.clone();

if !modified_file_path.is_file() {
continue;
}
let modified_file_path = modified_file_path.into_os_string().into_string().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely cloning twice unnecessarily.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to be the best available way to do it: https://stackoverflow.com/a/42579588

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but the clone() on line 327 shouldn't be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it can be changed to:

                let modified_file_path = &event.first().unwrap().path;

                if !modified_file_path.is_file() {
                    continue;
                }
                let modified_file_path = modified_file_path
                    .clone()
                    .into_os_string()
                    .into_string()
                    .unwrap();

which is slightly better because of if condition in the middle. but clone() can't be avoided as into_os_string() requires owned PathBuf.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

crates/cli_bin/tests/patterns_test.rs Outdated Show resolved Hide resolved
crates/cli_bin/tests/patterns_test.rs Outdated Show resolved Hide resolved
Comment on lines +17 to +25
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),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Ensure pattern.config.samples is always initialized.

The samples field in GritDefinitionConfig is of type Option<Vec<GritPatternSample>>, which means it can be None. To avoid potential runtime errors, ensure that pattern.config.samples is always initialized within the map_pattern_to_test_info function.

  • crates/gritmodule/src/testing.rs, lines 17-25
Analysis chain

Verify initialization of pattern.config.samples.

Ensure that pattern.config.samples is always initialized to avoid potential runtime errors.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that `pattern.config.samples` is always initialized.

# Test: Search for the initialization of `pattern.config.samples`. Expect: Initialization in all cases.
rg --type rust 'config.samples'

Length of output: 1272



Script:

#!/bin/bash
# Description: Verify the initialization of `config.samples` within `ResolvedGritDefinition` or related structures.

# Search for the definition and initialization of `ResolvedGritDefinition` and check how `config.samples` is handled.
rg --type rust 'struct ResolvedGritDefinition' -A 10
rg --type rust 'struct GritPatternConfig' -A 10

Length of output: 789



Script:

#!/bin/bash
# Description: Verify the initialization of `config.samples` within `GritDefinitionConfig`.

# Search for the definition and initialization of `GritDefinitionConfig` and check how `samples` is handled.
rg --type rust 'struct GritDefinitionConfig' -A 10

Length of output: 758



Script:

#!/bin/bash
# Description: Verify the initialization of `samples` within `GritDefinitionConfig` instances.

# Search for instances where `GritDefinitionConfig` is instantiated and check how the `samples` field is handled.
rg --type rust 'GritDefinitionConfig' -A 5 -B 5

Length of output: 8916

crates/core/src/pattern_compiler/compiler.rs Show resolved Hide resolved
@@ -567,6 +567,86 @@ fn find_definition_if_exists(
Ok(None)
}

pub fn get_dependents_of_target_patterns_by_traversal_from_src(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Vec::new() instead of <Vec<String>>::new() for consistency.

For better readability and consistency, use Vec::new() instead of <Vec<String>>::new().

- let mut dependents = <Vec<String>>::new();
+ let mut dependents = Vec::new();

Committable suggestion was skipped due to low confidence.

traversed_stack: &mut Vec<String>,
name: &str,
) -> Result<Option<Tree>> {
if !traversed_stack.contains(&name.to_string()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimize the traversal logic to avoid redundant checks.

The current implementation seems to perform redundant checks and might benefit from optimization. Specifically, the traversal logic could be streamlined to avoid unnecessary stack operations and checks.

- 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.insert(name.to_string()) {
+     if let Some(file_body) = libs.get(file_name) {
+         let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?;
+         return Ok(Some(tree));
+     }
+ }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if !traversed_stack.contains(&name.to_string()) {
if traversed_stack.insert(name.to_string()) {
if let Some(file_body) = libs.get(file_name) {
let tree = parser.parse_file(file_body, Some(Path::new(file_name)))?;
return Ok(Some(tree));
}
}

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
Comment on lines +472 to +475
if pattern.config.path.as_ref().unwrap() == modified_path
&& !modified_pattern_names.contains(&pattern.local_name.as_ref().unwrap())
{
deleted_patterns.push(pattern.clone());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential None values in pattern configuration paths.

Ensure that the function get_modified_and_deleted_patterns gracefully handles cases where pattern.config.path might be None.

- if pattern.config.path.as_ref().unwrap() == modified_path
+ if let Some(pattern_path) = pattern.config.path.as_ref() {
+     if pattern_path == modified_path && !modified_pattern_names.contains(&pattern.local_name.as_ref().unwrap()) {
+         deleted_patterns.push(pattern.clone());
+     }
+ } else {
+     log::warn!("Pattern config path is missing for pattern: {:?}", pattern.local_name);
+ }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if pattern.config.path.as_ref().unwrap() == modified_path
&& !modified_pattern_names.contains(&pattern.local_name.as_ref().unwrap())
{
deleted_patterns.push(pattern.clone());
if let Some(pattern_path) = pattern.config.path.as_ref() {
if pattern_path == modified_path && !modified_pattern_names.contains(&pattern.local_name.as_ref().unwrap()) {
deleted_patterns.push(pattern.clone());
}
} else {
log::warn!("Pattern config path is missing for pattern: {:?}", pattern.local_name);
}

crates/cli/src/commands/patterns_test.rs Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

crates/cli_bin/tests/patterns_test.rs Outdated Show resolved Hide resolved
crates/cli_bin/tests/patterns_test.rs Outdated Show resolved Hide resolved
let temp_dir_path = temp_dir.path().to_owned();

let _cmd_handle = thread::spawn(move || {
let mut cmd = Command::cargo_bin(BIN_NAME)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use get_test_cmd() instead and avoid hard-coding the BIN_name.

Copy link
Contributor Author

@Ashu999 Ashu999 Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try to use get_test_cmd(), but was not able to use .stdout() with it.

was getting below error:
no method named stdout found for mutable reference &mut assert_cmd::Command in the current scope
method not found in `&mut Command``

Copy link
Contributor Author

@Ashu999 Ashu999 Jul 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created get_test_process_cmd() to use process::Command, which supports .stdout()

5895757

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have many other tests that inspect stdout:

assert_snapshot!(String::from_utf8(output.stdout)?);

I don't understand why you need a new interface.

Copy link
Contributor

@morgante morgante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you resolve merge conflicts and tests pass, this is good to go.

@morgante
Copy link
Contributor

morgante commented Jul 5, 2024

@Ashu999 Tests and linters are failing. Please make sure everything passes.

@morgante morgante merged commit 8f3f077 into getgrit:main Jul 8, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Watch mode for grit patterns test
2 participants