Skip to content

Commit

Permalink
chore: expose test_utils (#325)
Browse files Browse the repository at this point in the history
  • Loading branch information
morgante authored May 12, 2024
1 parent 9c63d9a commit 6c1150b
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 63 deletions.
3 changes: 3 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ path-absolutize = { version = "3.1.1", optional = false, features = [
"use_unix_paths_on_wasm",
] }
getrandom = { version = "0.2.11", optional = true }
lazy_static = {version = "1.4.0", optional = true }
walkdir = { version = "2.3.3", optional = true }

[dev-dependencies]
similar = "2.2.1"
Expand Down Expand Up @@ -86,3 +88,4 @@ language-parsers = ["marzano-language/builtin-parser"]
grit-parser = ["marzano-language/grit-parser"]
absolute_filename = []
non_wasm = ["absolute_filename"]
test_utils = []
8 changes: 7 additions & 1 deletion crates/core/src/ast_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ impl ASTNode {
}

impl AstNodePattern<MarzanoQueryContext> for ASTNode {
const INCLUDES_TRIVIA: bool = false;

fn children(&self) -> Vec<PatternOrPredicate<MarzanoQueryContext>> {
self.args
.iter()
Expand Down Expand Up @@ -144,7 +146,11 @@ impl AstLeafNode {
}
}

impl AstLeafNodePattern<MarzanoQueryContext> for AstLeafNode {}
impl AstLeafNodePattern<MarzanoQueryContext> for AstLeafNode {
fn text(&self) -> Option<&str> {
Some(&self.text)
}
}

impl PatternName for AstLeafNode {
fn name(&self) -> &'static str {
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ use getrandom as _;
mod test;
#[cfg(test)]
mod test_files;
#[cfg(any(test, feature = "test_utils"))]
pub mod test_utils;
8 changes: 4 additions & 4 deletions crates/core/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ fn match_pattern_libs(
Ok(execution_result)
}

struct TestArg {
pattern: String,
source: String,
pub struct TestArg {
pub pattern: String,
pub source: String,
}

struct TestArgExpected {
Expand Down Expand Up @@ -392,7 +392,7 @@ fn run_test_expected_with_new_file(arg: TestArgExpectedWithNewFile) -> Result<()
Ok(())
}

fn run_test_match(arg: TestArg) -> Result<()> {
pub fn run_test_match(arg: TestArg) -> Result<()> {
let pattern = arg.pattern;
let js_lang: TargetLanguage = PatternLanguage::Tsx.try_into().unwrap();
let source = arg.source;
Expand Down
64 changes: 6 additions & 58 deletions crates/core/src/test_files.rs
Original file line number Diff line number Diff line change
@@ -1,66 +1,14 @@
use marzano_language::target_language::TargetLanguage;
use marzano_util::{
cache::NullCache,
rich_path::{FileName, RichFile, TryIntoInputFile},
runtime::ExecutionContext,
};
use serde::{Deserialize, Serialize};

use crate::api::{MatchResult, Rewrite};
use crate::{
api::{MatchResult, Rewrite},
test_utils::{run_on_test_files, SyntheticFile},
};

use self::{pattern_compiler::src_to_problem_libs, problem::Problem};
use anyhow::Result;
use self::pattern_compiler::src_to_problem_libs;

use super::*;
use std::{borrow::Cow, collections::BTreeMap, sync::mpsc};

/// SyntheticFile is used for ensuring we don't read files until their file names match
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
struct SyntheticFile {
pub path: String,
pub content: String,
pub can_read: bool,
}

impl SyntheticFile {
pub fn new(path: String, content: String, can_read: bool) -> Self {
Self {
path,
content,
can_read,
}
}
}

impl TryIntoInputFile for SyntheticFile {
fn try_into_cow(&self) -> Result<Cow<RichFile>> {
if !self.can_read {
println!("Tried to read file that should not be read: {}", self.path);
}

Ok(Cow::Owned(RichFile::new(
self.path.clone(),
self.content.clone(),
)))
}
}

impl FileName for SyntheticFile {
fn name(&self) -> String {
self.path.to_owned()
}
}

fn run_on_test_files(problem: &Problem, test_files: &[SyntheticFile]) -> Vec<MatchResult> {
let mut results = vec![];
let context = ExecutionContext::default();
let (tx, rx) = mpsc::channel::<Vec<MatchResult>>();
problem.execute_shared(test_files.to_vec(), &context, tx, &NullCache::new());
for r in rx.iter() {
results.extend(r)
}
results
}
use std::collections::BTreeMap;

#[test]
fn test_lazy_file_parsing() {
Expand Down
115 changes: 115 additions & 0 deletions crates/core/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::{borrow::Cow, collections::BTreeMap, sync::mpsc};

use anyhow::Result;
use marzano_language::target_language::TargetLanguage;
use marzano_util::{
cache::NullCache,
rich_path::{FileName, RichFile, TryIntoInputFile},
runtime::ExecutionContext,
};
use serde::{Deserialize, Serialize};

use crate::{api::MatchResult, pattern_compiler::src_to_problem_libs, problem::Problem};

/// SyntheticFile is used for ensuring we don't read files until their file names match
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub(crate) struct SyntheticFile {
pub path: String,
pub content: String,
pub can_read: bool,
}

impl SyntheticFile {
pub fn new(path: String, content: String, can_read: bool) -> Self {
Self {
path,
content,
can_read,
}
}
}

impl TryIntoInputFile for SyntheticFile {
fn try_into_cow(&self) -> Result<Cow<RichFile>> {
if !self.can_read {
println!("Tried to read file that should not be read: {}", self.path);
}

Ok(Cow::Owned(RichFile::new(
self.path.clone(),
self.content.clone(),
)))
}
}

impl FileName for SyntheticFile {
fn name(&self) -> String {
self.path.to_owned()
}
}

enum TestCaseExpectation {
Match,
}

pub struct TestCase {
files: Vec<SyntheticFile>,
pattern: String,
expectation: TestCaseExpectation,
}

impl TestCase {
pub fn new_match(file_contents: &str, pattern: &str) -> Self {
Self {
files: vec![SyntheticFile::new(
"target.js".to_string(),
file_contents.to_string(),
true,
)],
pattern: pattern.to_string(),
expectation: TestCaseExpectation::Match,
}
}
}

pub(crate) fn run_on_test_files(
problem: &Problem,
test_files: &[SyntheticFile],
) -> Vec<MatchResult> {
let mut results = vec![];
let context = ExecutionContext::default();
let (tx, rx) = mpsc::channel::<Vec<MatchResult>>();
problem.execute_shared(test_files.to_vec(), &context, tx, &NullCache::new());
for r in rx.iter() {
results.extend(r)
}
results
}

pub fn run_test(case: TestCase) -> Vec<MatchResult> {
let pattern_src = case.pattern;
let libs = BTreeMap::new();

let pattern = src_to_problem_libs(
pattern_src.to_string(),
&libs,
TargetLanguage::default(),
None,
None,
None,
None,
)
.unwrap()
.problem;

let results = run_on_test_files(&pattern, &case.files);

match case.expectation {
TestCaseExpectation::Match => {
let match_case = results.iter().any(|r| r.is_match());
assert!(match_case, "Expected a match, but got none");
}
}

results
}
24 changes: 24 additions & 0 deletions crates/grit-pattern-matcher/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,27 @@ pub fn debug<'a, Q: QueryContext>(
}
Ok(())
}

pub fn warning<'a, Q: QueryContext>(
analysis_logs: &mut AnalysisLogs,
state: &State<'a, Q>,
lang: &Q::Language<'a>,
message: &str,
) -> Result<()> {
let mut builder = AnalysisLogBuilder::default();
builder.level(301_u16);
builder.message(message);

if let Ok(file) = get_file_name(state, lang) {
builder.file(file);
}

let log = builder.build();
match log {
Ok(log) => analysis_logs.push(log),
Err(err) => {
bail!(err);
}
}
Ok(())
}
9 changes: 9 additions & 0 deletions crates/grit-pattern-matcher/src/pattern/ast_node_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use crate::context::QueryContext;
pub trait AstNodePattern<Q: QueryContext>:
Clone + std::fmt::Debug + Matcher<Q> + PatternName + Sized
{
/// Does this AST include trivia?
/// Trivia is useful for being able to re-print an AST, but not all parsers support collecting it.
const INCLUDES_TRIVIA: bool;

fn children(&self) -> Vec<PatternOrPredicate<Q>>;

fn matches_kind_of(&self, node: &Q::Node<'_>) -> bool;
Expand All @@ -17,4 +21,9 @@ pub trait AstNodePattern<Q: QueryContext>:
pub trait AstLeafNodePattern<Q: QueryContext>:
Clone + std::fmt::Debug + Matcher<Q> + PatternName + Sized
{
/// Provides a *possible* text value for the leaf node.
/// This is not mandatory, but enables some advanced functionality.
fn text(&self) -> Option<&str> {
None
}
}
1 change: 1 addition & 0 deletions crates/grit-pattern-matcher/src/pattern/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ impl<Q: QueryContext> Matcher<Q> for Pattern<Q> {
}

pub trait CodeSnippet<Q: QueryContext + 'static>: Clone + Debug + Matcher<Q> + PatternName {
/// Return the different patterns which could *all* possibly match the code snippet.
fn patterns(&self) -> impl Iterator<Item = &Pattern<Q>>;

fn dynamic_snippet(&self) -> Option<&DynamicPattern<Q>>;
Expand Down

0 comments on commit 6c1150b

Please sign in to comment.