mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-08-05 14:42:07 -07:00
globset: add opt-in Arbitrary
trait implementations
This feature is mandatory when using `Glob` in fuzz testing. Closes #2720
This commit is contained in:
committed by
Andrew Gallant
parent
5548e538b1
commit
95979048c9
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@@ -230,3 +230,28 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
RUSTDOCFLAGS: -D warnings
|
RUSTDOCFLAGS: -D warnings
|
||||||
run: cargo doc --no-deps --document-private-items --workspace
|
run: cargo doc --no-deps --document-private-items --workspace
|
||||||
|
|
||||||
|
fuzz_testing:
|
||||||
|
name: Compile Fuzz Test Targets
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install required packages (Ubuntu)
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install g++ --yes
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: Install Fuzzer
|
||||||
|
run: cargo install cargo-fuzz
|
||||||
|
working-directory: fuzz
|
||||||
|
|
||||||
|
- name: Verify fuzz targets build
|
||||||
|
run: cargo check
|
||||||
|
working-directory: fuzz
|
||||||
|
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -17,6 +17,15 @@ version = "1.0.98"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.1"
|
version = "2.9.1"
|
||||||
@@ -85,6 +94,17 @@ version = "0.8.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -126,6 +146,7 @@ name = "globset"
|
|||||||
version = "0.4.16"
|
version = "0.4.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
|
"arbitrary",
|
||||||
"bstr",
|
"bstr",
|
||||||
"glob",
|
"glob",
|
||||||
"log",
|
"log",
|
||||||
|
@@ -20,6 +20,7 @@ exclude = [
|
|||||||
"/pkg/brew",
|
"/pkg/brew",
|
||||||
"/benchsuite/",
|
"/benchsuite/",
|
||||||
"/scripts/",
|
"/scripts/",
|
||||||
|
"/crates/fuzz",
|
||||||
]
|
]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
autotests = false
|
autotests = false
|
||||||
|
@@ -11,4 +11,4 @@ if ! command -V sudo; then
|
|||||||
fi
|
fi
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y --no-install-recommends \
|
sudo apt-get install -y --no-install-recommends \
|
||||||
zsh xz-utils liblz4-tool musl-tools brotli zstd
|
zsh xz-utils liblz4-tool musl-tools brotli zstd g++
|
||||||
|
@@ -21,6 +21,7 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = "1.1.1"
|
aho-corasick = "1.1.1"
|
||||||
|
arbitrary = { version = "1.3.2", optional = true, features = ["derive"] }
|
||||||
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
||||||
log = { version = "0.4.20", optional = true }
|
log = { version = "0.4.20", optional = true }
|
||||||
serde = { version = "1.0.188", optional = true }
|
serde = { version = "1.0.188", optional = true }
|
||||||
@@ -41,6 +42,7 @@ serde_json = "1.0.107"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["log"]
|
default = ["log"]
|
||||||
|
arbitrary = ["dep:arbitrary"]
|
||||||
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
||||||
# dispatch.
|
# dispatch.
|
||||||
simd-accel = []
|
simd-accel = []
|
||||||
|
@@ -72,6 +72,7 @@ impl MatchStrategy {
|
|||||||
/// It cannot be used directly to match file paths, but it can be converted
|
/// It cannot be used directly to match file paths, but it can be converted
|
||||||
/// to a regular expression string or a matcher.
|
/// to a regular expression string or a matcher.
|
||||||
#[derive(Clone, Debug, Eq)]
|
#[derive(Clone, Debug, Eq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
pub struct Glob {
|
pub struct Glob {
|
||||||
glob: String,
|
glob: String,
|
||||||
re: String,
|
re: String,
|
||||||
@@ -194,6 +195,7 @@ pub struct GlobBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
struct GlobOptions {
|
struct GlobOptions {
|
||||||
/// Whether to match case insensitively.
|
/// Whether to match case insensitively.
|
||||||
case_insensitive: bool,
|
case_insensitive: bool,
|
||||||
@@ -220,6 +222,7 @@ impl GlobOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
struct Tokens(Vec<Token>);
|
struct Tokens(Vec<Token>);
|
||||||
|
|
||||||
impl std::ops::Deref for Tokens {
|
impl std::ops::Deref for Tokens {
|
||||||
@@ -236,6 +239,7 @@ impl std::ops::DerefMut for Tokens {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
enum Token {
|
enum Token {
|
||||||
Literal(char),
|
Literal(char),
|
||||||
Any,
|
Any,
|
||||||
|
@@ -94,6 +94,19 @@ Standard Unix-style glob syntax is supported:
|
|||||||
|
|
||||||
A `GlobBuilder` can be used to prevent wildcards from matching path separators,
|
A `GlobBuilder` can be used to prevent wildcards from matching path separators,
|
||||||
or to enable case insensitive matching.
|
or to enable case insensitive matching.
|
||||||
|
|
||||||
|
# Crate Features
|
||||||
|
|
||||||
|
This crate includes optional features that can be enabled if necessary.
|
||||||
|
These features are not required but may be useful depending on the use case.
|
||||||
|
|
||||||
|
The following features are available:
|
||||||
|
|
||||||
|
* **arbitrary** -
|
||||||
|
Enabling this feature introduces a public dependency on the
|
||||||
|
[`arbitrary`](https://crates.io/crates/arbitrary)
|
||||||
|
crate. Namely, it implements the `Arbitrary` trait from that crate for the
|
||||||
|
[`Glob`] type. This feature is disabled by default.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
4
fuzz/.gitignore
vendored
Normal file
4
fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
target
|
||||||
|
corpus
|
||||||
|
artifacts
|
||||||
|
coverage
|
188
fuzz/Cargo.lock
generated
Normal file
188
fuzz/Cargo.lock
generated
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuzz"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"globset",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.16"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"arbitrary",
|
||||||
|
"bstr",
|
||||||
|
"log",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.152"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.195"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.195"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
25
fuzz/Cargo.toml
Normal file
25
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "fuzz"
|
||||||
|
version = "0.0.1"
|
||||||
|
publish = false
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
cargo-fuzz = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libfuzzer-sys = "0.4"
|
||||||
|
globset = { path = "../crates/globset", features = ["arbitrary"] }
|
||||||
|
|
||||||
|
# Prevent this from interfering with workspaces
|
||||||
|
[workspace]
|
||||||
|
members = ["."]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = 1
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_glob"
|
||||||
|
path = "fuzz_targets/fuzz_glob.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
52
fuzz/README.md
Normal file
52
fuzz/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Fuzz Testing
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Fuzz testing produces pseudo-random / arbitrary data that is used to find
|
||||||
|
stability issues within a code base. While Rust provides a strong type system,
|
||||||
|
this does not guarantee that an object will convert properly from one struct
|
||||||
|
to another. It is the responsibility of the developer to ensure that a struct
|
||||||
|
is converted properly. Fuzz testing will generate input within the domain of
|
||||||
|
each property. This arbitrary data can then be used to convert from ObjectA
|
||||||
|
to ObjectB and then back. This type of testing will help catch bugs that the
|
||||||
|
type system is not able to see.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This crate relies on the `cargo-fuzz` component. To install this component,
|
||||||
|
run the following from the `fuzz` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install cargo-fuzz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Listing Targets
|
||||||
|
|
||||||
|
Once installed, fuzz targets can be listed by running the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fuzz list
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will print out a list of all targets that can be tested.
|
||||||
|
|
||||||
|
## Running Fuzz Tests
|
||||||
|
|
||||||
|
To run a fuzz test, the target must be specified:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fuzz run <target>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the above will run the fuzz test indefinitely. Use the
|
||||||
|
`-max_total_time=<num seconds>` flag to specify how many seconds the test
|
||||||
|
should run for:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fuzz run <target> -- -max_total_time=5
|
||||||
|
```
|
||||||
|
|
||||||
|
The above command will run the fuzz test for five seconds. If the test
|
||||||
|
completes without error it will show how many tests were run successfully.
|
||||||
|
The test will abort and return a non-zero error code if it is able to produce
|
||||||
|
an error. The arbitrary input will be displayed in the event of a failure.
|
22
fuzz/fuzz_targets/fuzz_glob.rs
Normal file
22
fuzz/fuzz_targets/fuzz_glob.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use globset::Glob;
|
||||||
|
|
||||||
|
libfuzzer_sys::fuzz_target!(|glob_str: &str| {
|
||||||
|
let Ok(glob) = Glob::new(glob_str) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(glob2) = Glob::from_str(glob_str) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify that a `Glob` constructed with `new` is the same as a `Glob`` constructed
|
||||||
|
// with `from_str`.
|
||||||
|
assert_eq!(glob, glob2);
|
||||||
|
|
||||||
|
// Verify that `Glob::glob` produces the same string as the original.
|
||||||
|
assert_eq!(glob.glob(), glob_str);
|
||||||
|
});
|
Reference in New Issue
Block a user