• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pomsky-lang / pomsky / 12305481124

12 Dec 2024 09:57PM UTC coverage: 77.983% (-2.3%) from 80.275%
12305481124

push

github

Aloso
feat: support Rust regex engine, change help styling, many small improvements

191 of 509 new or added lines in 14 files covered. (37.52%)

4 existing lines in 3 files now uncovered.

4778 of 6127 relevant lines covered (77.98%)

350716.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pomsky-bin/src/testing.rs
1
use std::{path::Path, process::exit, time::Instant};
2

3
use helptext::text;
4
use pomsky::options::RegexFlavor;
5

6
use crate::{
7
    args::{CompileOptions, GlobalOptions, Input, RegexEngine, TestOptions},
8
    format::Logger,
9
    CompilationResult,
10
};
11

12
pub(crate) struct TestDirectoryResult {
13
    pub(crate) total: usize,
14
    pub(crate) failed: usize,
15
    pub(crate) results: Vec<CompilationResult>,
16
}
17

NEW
18
pub(crate) fn test(logger: &Logger, args: GlobalOptions, test_args: TestOptions) {
×
NEW
19
    let (test_engine, flavor) = match (args.flavor, test_args.engine) {
×
20
        (None, None) => {
NEW
21
            logger.error().println("No regex engine specified");
×
NEW
22
            exit(2);
×
23
        }
NEW
24
        (None, Some(engine)) => match engine {
×
NEW
25
            RegexEngine::Pcre2 => (engine, RegexFlavor::Pcre),
×
NEW
26
            RegexEngine::Rust => (engine, RegexFlavor::Rust),
×
27
        },
NEW
28
        (Some(flavor), None) => match flavor {
×
NEW
29
            RegexFlavor::Pcre => (RegexEngine::Pcre2, flavor),
×
NEW
30
            RegexFlavor::Rust => (RegexEngine::Rust, flavor),
×
31
            _ => {
NEW
32
                logger
×
NEW
33
                    .error()
×
NEW
34
                    .println(format_args!("No supported regex engine for the {flavor:?} flavor"));
×
NEW
35
                exit(2);
×
36
            }
37
        },
NEW
38
        (Some(flavor), Some(engine)) => (engine, flavor),
×
39
    };
40

NEW
41
    let args = GlobalOptions { flavor: Some(flavor), ..args };
×
NEW
42
    let compile_args = CompileOptions {
×
NEW
43
        input: Input::File(test_args.path),
×
NEW
44
        no_new_line: false,
×
NEW
45
        test: Some(test_engine),
×
NEW
46
        in_test_suite: true,
×
NEW
47
    };
×
48

NEW
49
    let Input::File(path) = &compile_args.input else { unreachable!() };
×
50

NEW
51
    let Ok(current_dir) = std::env::current_dir() else {
×
NEW
52
        logger.error().println("Could not get current directory");
×
NEW
53
        exit(3);
×
54
    };
NEW
55
    let path = if path.is_relative() { current_dir.join(path) } else { path.to_owned() };
×
NEW
56
    let Ok(metadata) = std::fs::metadata(&path) else {
×
NEW
57
        logger.error().println("Could not get path metadata");
×
NEW
58
        exit(3);
×
59
    };
60

NEW
61
    if metadata.is_dir() {
×
NEW
62
        let start = Instant::now();
×
NEW
63

×
NEW
64
        let TestDirectoryResult { total, failed, results } =
×
NEW
65
            test_directory(logger, &path, &current_dir, &args, &compile_args);
×
NEW
66

×
NEW
67
        if total == 0 && !args.json {
×
NEW
68
            if test_args.pass_with_no_tests {
×
NEW
69
                exit(0);
×
70
            } else {
NEW
71
                logger.error().println("no `*.pomsky` files found to test");
×
NEW
72
                exit(5);
×
73
            }
NEW
74
        }
×
NEW
75

×
NEW
76
        logger.emptyln();
×
NEW
77

×
NEW
78
        let time = start.elapsed();
×
NEW
79
        let time_fmt = format!("{time:.2?}");
×
NEW
80
        if failed > 0 {
×
NEW
81
            logger.basic().fmtln(text![
×
NEW
82
                "test result: " R!{&failed.to_string()} R!" pomsky file(s) failed" ", "
×
NEW
83
                {&total.to_string()} " files tested in " {&time_fmt}
×
NEW
84
            ]);
×
NEW
85
        } else {
×
NEW
86
            logger.basic().fmtln(text![
×
NEW
87
                "test result: " G!"ok" ", "
×
NEW
88
                {&total.to_string()} " files tested in " {&time_fmt}
×
NEW
89
            ]);
×
NEW
90
        }
×
NEW
91
        print_json_test_result(args.json, &results);
×
NEW
92
    } else if metadata.is_file() {
×
NEW
93
        let mut results = Vec::new();
×
NEW
94
        let mut failed = 0;
×
NEW
95
        test_single(logger, &path, &current_dir, &args, &compile_args, &mut results, &mut failed);
×
NEW
96
        print_json_test_result(args.json, &results);
×
NEW
97
    } else {
×
NEW
98
        logger.error().println(format_args!(
×
NEW
99
            "expected file or directory, but `{}` is neither",
×
NEW
100
            path.display()
×
NEW
101
        ));
×
NEW
102
        exit(3);
×
103
    }
NEW
104
}
×
105

NEW
106
fn test_directory(
×
NEW
107
    logger: &Logger,
×
NEW
108
    path: &Path,
×
NEW
109
    current_dir: &Path,
×
NEW
110
    args: &GlobalOptions,
×
NEW
111
    compile_args: &CompileOptions,
×
NEW
112
) -> TestDirectoryResult {
×
NEW
113
    let mut total = 0;
×
NEW
114
    let mut failed = 0;
×
NEW
115
    let mut results = Vec::new();
×
116

NEW
117
    for entry in ignore::WalkBuilder::new(path)
×
NEW
118
        .follow_links(true)
×
NEW
119
        .filter_entry(is_dir_or_pomsky_file)
×
NEW
120
        .build()
×
121
    {
NEW
122
        if let Ok(entry) = entry.map_err(|d| handle_walk_error(d, logger, current_dir)) {
×
NEW
123
            if entry.file_type().is_some_and(|ty| ty.is_file()) {
×
NEW
124
                total += 1;
×
NEW
125

×
NEW
126
                let path = entry.path();
×
NEW
127
                test_single(
×
NEW
128
                    logger,
×
NEW
129
                    path,
×
NEW
130
                    current_dir,
×
NEW
131
                    args,
×
NEW
132
                    compile_args,
×
NEW
133
                    &mut results,
×
NEW
134
                    &mut failed,
×
NEW
135
                );
×
NEW
136
            }
×
NEW
137
        };
×
138
    }
139

NEW
140
    TestDirectoryResult { total, failed, results }
×
NEW
141
}
×
142

NEW
143
fn test_single(
×
NEW
144
    logger: &Logger,
×
NEW
145
    path: &Path,
×
NEW
146
    current_dir: &Path,
×
NEW
147
    args: &GlobalOptions,
×
NEW
148
    compile_args: &CompileOptions,
×
NEW
149
    results: &mut Vec<CompilationResult>,
×
NEW
150
    failed: &mut usize,
×
NEW
151
) {
×
NEW
152
    match std::fs::read_to_string(path) {
×
NEW
153
        Ok(input) => {
×
NEW
154
            logger.basic().fmt(text![C!"testing " {&show_relative(path, current_dir)} " ... "]);
×
NEW
155
            let result = super::compile(Some(path), &input, compile_args, args);
×
NEW
156
            if !result.success {
×
NEW
157
                *failed += 1;
×
NEW
158
            }
×
NEW
159
            if args.json {
×
NEW
160
                results.push(result);
×
NEW
161
            } else {
×
NEW
162
                result.output(
×
NEW
163
                    logger,
×
NEW
164
                    args.json,
×
NEW
165
                    !compile_args.no_new_line,
×
NEW
166
                    compile_args.in_test_suite,
×
NEW
167
                    &input,
×
NEW
168
                );
×
NEW
169
            }
×
170
        }
NEW
171
        Err(error) => {
×
NEW
172
            logger.error().println(error);
×
NEW
173
            exit(3);
×
174
        }
175
    }
NEW
176
}
×
177

NEW
178
fn is_dir_or_pomsky_file(entry: &ignore::DirEntry) -> bool {
×
NEW
179
    let Some(ty) = entry.file_type() else { return false };
×
NEW
180
    if ty.is_dir() {
×
NEW
181
        return true;
×
NEW
182
    }
×
NEW
183
    let Some(ext) = entry.path().extension() else { return false };
×
NEW
184
    ext == "pomsky"
×
NEW
185
}
×
186

NEW
187
fn handle_walk_error(error: ignore::Error, logger: &Logger, current_dir: &Path) {
×
NEW
188
    match error {
×
NEW
189
        ignore::Error::Partial(errors) => {
×
NEW
190
            for error in errors {
×
NEW
191
                handle_walk_error(error, logger, current_dir);
×
NEW
192
            }
×
193
        }
NEW
194
        ignore::Error::WithLineNumber { line, err } => {
×
NEW
195
            handle_walk_error(*err, logger, current_dir);
×
NEW
196
            logger.basic().fmtln(text!["    at line " C!{&line.to_string()}]);
×
NEW
197
        }
×
NEW
198
        ignore::Error::WithPath { path, err } => {
×
NEW
199
            handle_walk_error(*err, logger, current_dir);
×
NEW
200
            logger.basic().fmtln(text!["    at path " C!{&path.display().to_string()}]);
×
NEW
201
        }
×
NEW
202
        ignore::Error::WithDepth { depth, err } => {
×
NEW
203
            handle_walk_error(*err, logger, current_dir);
×
NEW
204
            logger.basic().fmtln(text!["    at depth " C!{&depth.to_string()}]);
×
NEW
205
        }
×
NEW
206
        ignore::Error::Loop { ancestor, child } => {
×
NEW
207
            let ancestor = ancestor.strip_prefix(current_dir).unwrap_or(&ancestor);
×
NEW
208
            let child = child.canonicalize().unwrap_or(child);
×
NEW
209

×
NEW
210
            logger.error().println("file system loop detected!");
×
NEW
211
            logger.basic().fmtln(text!["    ancestor: " C!{&ancestor.display().to_string()}]);
×
NEW
212
            logger.basic().fmtln(text!["    child: " C!{&child.display().to_string()}]);
×
NEW
213
        }
×
NEW
214
        ignore::Error::Io(error) => {
×
NEW
215
            logger.error().println(error);
×
NEW
216
        }
×
NEW
217
        ignore::Error::Glob { glob, err } => {
×
NEW
218
            logger.error().println(err);
×
NEW
219
            if let Some(glob) = glob {
×
NEW
220
                logger.basic().fmtln(text!["    glob: " C!{&glob}]);
×
NEW
221
            }
×
222
        }
NEW
223
        ignore::Error::UnrecognizedFileType(file_type) => {
×
NEW
224
            logger.error().println(format_args!("file type `{file_type}` not recognized"));
×
NEW
225
        }
×
NEW
226
        ignore::Error::InvalidDefinition => {
×
NEW
227
            logger.error().println("file type definition could not be parsed");
×
NEW
228
        }
×
229
    }
NEW
230
}
×
231

NEW
232
pub(crate) fn print_json_test_result(json: bool, results: &[CompilationResult]) {
×
NEW
233
    if json {
×
NEW
234
        match serde_json::to_string(&results) {
×
NEW
235
            Ok(string) => println!("{string}"),
×
NEW
236
            Err(e) => eprintln!("{e}"),
×
237
        }
NEW
238
    }
×
NEW
239
}
×
240

NEW
241
fn show_relative(path: &Path, relative_to: &Path) -> String {
×
NEW
242
    path.strip_prefix(relative_to).unwrap_or(path).display().to_string()
×
NEW
243
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc