test-strategy-0.3.1/.cargo_vcs_info.json0000644000000001360000000000100136540ustar { "git": { "sha1": "19797fdf2bede6ec0b066b2e577db3a8bb48c583" }, "path_in_vcs": "" }test-strategy-0.3.1/.github/workflows/ci.yml000064400000000000000000000025720072674642500172150ustar 00000000000000name: CI on: push: pull_request: schedule: [cron: "20 5 * * *"] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Rustup update stable run: rustup update stable - name: Show cargo version run: cargo --version - name: Build run: cargo build --verbose - name: Build tests run: cargo test --verbose --no-run - name: Run tests run: cargo test --verbose - name: Run compile fail tests run: cargo test --test compile_fail --verbose -- --ignored - name: Clippy run: cargo clippy --all-features --tests --lib -- -W clippy::all env: RUSTFLAGS: -D warnings - name: Rustup toolchain install nightly run: rustup toolchain install nightly # - name: Rustup toolchain install nightly # run: rustup toolchain install nightly -c clippy - name: Set minimal versions run: cargo +nightly update -Z minimal-versions - name: Build tests (minimal versions) run: cargo +stable test --verbose --no-run - name: Run tests (minimal versions) run: cargo +stable test --verbose # - name: Clean # run: cargo clean # - name: Clippy nightly # run: cargo +nightly clippy --all-features --tests --lib -- -W clippy::all # env: # RUSTFLAGS: -D warnings test-strategy-0.3.1/.gitignore000064400000000000000000000000520072674642500144610ustar 00000000000000/target Cargo.lock *.proptest-regressions test-strategy-0.3.1/.vscode/settings.json000064400000000000000000000001410072674642500165640ustar 00000000000000{ "rust-analyzer.server.extraEnv": { "CARGO_TARGET_DIR": "./target/debug-ra", } }test-strategy-0.3.1/.vscode/tasks.json000064400000000000000000000132160072674642500160600ustar 00000000000000{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "type": "shell", "label": "cargo build", "command": "cargo", "args": [ "build" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } // "options": { // "env": { // "RUST_BACKTRACE": "1", // } // } }, { "type": "shell", "label": "cargo build release", "command": "cargo", "args": [ "build", "--release" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "cargo watch", "command": "wt", "args": [ "new-tab", "cargo-watch", "-c", "-x", "test" ], "problemMatcher": [], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "cargo test", "command": "cargo", "args": [ "test" ], "problemMatcher": [ "$rustc" ], "group": { "kind": "build", "isDefault": true }, "presentation": { "panel": "dedicated", "clear": true }, "dependsOn": [ "include doc" ], }, { "type": "shell", "label": "cargo test nightly", "command": "cargo", "args": [ "+nightly", "test", "--target-dir", "target/debug-nightly", ], "problemMatcher": [ "$rustc" ], "group": { "kind": "test", "isDefault": true }, "presentation": { "panel": "dedicated", "clear": true }, "options": { "env": { "RUSTFLAGS": "-Z proc-macro-backtrace" } }, }, { "type": "shell", "label": "cargo run exmaple", "command": "cargo", "args": [ "run", "--example", "${fileBasenameNoExtension}" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "cargo doc open", "command": "cargo", "args": [ "doc", "--open", "--no-deps", "--all-features" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true }, "dependsOn": [ "include doc" ], }, { "type": "shell", "label": "cargo clippy", "command": "cargo", "args": [ "clippy", "--all-features", "--tests", "--lib", "--", "-W", "clippy::all" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "cargo clippy nightly", "command": "cargo", "args": [ "+nightly", "clippy", "--target-dir", "target/debug-nightly", "--all-features", "--tests", "--lib", "--", "-W", "clippy::all" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "cargo fix & fmt", "command": "cargo fix && cargo fmt", "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true, } }, { "type": "shell", "label": "cargo bench", "command": "cargo", "args": [ "bench" ], "options": { "cwd": "${workspaceFolder}/benchmarks" }, "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "cargo update minimal-versions", "command": "cargo", "args": [ "+nightly", "update", "-Z", "minimal-versions" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true } }, { "type": "shell", "label": "update compile error", "command": "cargo", "args": [ "test", "--test", "compile_fail", "--", "--ignored" ], "problemMatcher": [ "$rustc" ], "presentation": { "panel": "dedicated", "clear": true }, "options": { "env": { "TRYBUILD": "overwrite" } } }, { "type": "shell", "label": "include doc", "command": "include-doc", "args": [ "${workspaceFolder}" ], "problemMatcher": [ { "owner": "include-doc", "fileLocation": [ "relative", "${workspaceFolder}" ], "pattern": [ { "regexp": "^(error): (.*)$", "severity": 1, "message": 2, }, { "regexp": "^--> (.*):(\\d+)\\s*$", "file": 1, "line": 2, "loop": true, }, ] }, ], "presentation": { "panel": "dedicated", "clear": true } }, ] }test-strategy-0.3.1/Cargo.lock0000644000000426030000000000100116340ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "basic-toml" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" dependencies = [ "serde", ] [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "errno" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", "windows-sys 0.48.0", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "io-lifetimes" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", "windows-sys 0.48.0", ] [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libm" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "linux-raw-sys" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", "libm", ] [[package]] name = "num_cpus" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", "bitflags", "byteorder", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", "unarray", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustix" version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" [[package]] name = "serde_derive" version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "structmeta" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ad9e09554f0456d67a69c1584c9798ba733a5b50349a6c0d0948710523922d" dependencies = [ "proc-macro2", "quote", "structmeta-derive", "syn", ] [[package]] name = "structmeta-derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys 0.45.0", ] [[package]] name = "termcolor" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "test-strategy" version = "0.3.1" dependencies = [ "proc-macro2", "proptest", "quote", "structmeta", "syn", "tokio", "trybuild", ] [[package]] name = "tokio" version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "num_cpus", "pin-project-lite", "windows-sys 0.48.0", ] [[package]] name = "trybuild" version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" dependencies = [ "basic-toml", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", ] [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ "windows-targets 0.42.2", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.0", ] [[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", "windows_i686_gnu 0.48.0", "windows_i686_msvc 0.48.0", "windows_x86_64_gnu 0.48.0", "windows_x86_64_gnullvm 0.48.0", "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" test-strategy-0.3.1/Cargo.toml0000644000000024660000000000100116620ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "test-strategy" version = "0.3.1" authors = ["frozenlib"] description = "Procedural macro to easily write higher-order strategies in proptest." documentation = "https://docs.rs/test-strategy/" readme = "README.md" keywords = [ "macro", "testing", "proptest", ] categories = ["development-tools::testing"] license = "MIT OR Apache-2.0" repository = "https://github.com/frozenlib/test-strategy" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.55" [dependencies.quote] version = "1.0.26" [dependencies.structmeta] version = "0.2.0" [dependencies.syn] version = "2.0.13" features = [ "visit", "full", "extra-traits", ] [dev-dependencies.proptest] version = "1.1.0" [dev-dependencies.tokio] version = "1.28.1" features = ["rt-multi-thread"] [dev-dependencies.trybuild] version = "1.0.80" test-strategy-0.3.1/Cargo.toml.orig000064400000000000000000000014650072674642500153710ustar 00000000000000[package] name = "test-strategy" version = "0.3.1" authors = ["frozenlib"] license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/frozenlib/test-strategy" documentation = "https://docs.rs/test-strategy/" keywords = ["macro", "testing", "proptest"] categories = ["development-tools::testing"] description = "Procedural macro to easily write higher-order strategies in proptest." edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] proc-macro = true [dependencies] syn = { version = "2.0.13", features = ["visit", "full", "extra-traits"] } quote = "1.0.26" proc-macro2 = "1.0.55" structmeta = "0.2.0" [dev-dependencies] proptest = "1.1.0" trybuild = "1.0.80" tokio = { version = "1.28.1", features = ["rt-multi-thread"] } test-strategy-0.3.1/LICENSE-APACHE000064400000000000000000000261350072674642500144270ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. test-strategy-0.3.1/LICENSE-MIT000064400000000000000000000020520072674642500141270ustar 00000000000000MIT License Copyright (c) 2020 FrozenLib Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. test-strategy-0.3.1/README.md000064400000000000000000000412300072674642500137530ustar 00000000000000# test-strategy [![Crates.io](https://img.shields.io/crates/v/test-strategy.svg)][test-strategy] [![Docs.rs](https://docs.rs/test-strategy/badge.svg)](https://docs.rs/test-strategy/) [![Actions Status](https://github.com/frozenlib/test-strategy/workflows/CI/badge.svg)](https://github.com/frozenlib/test-strategy/actions) This crate provides two procedural macros, `#[derive(Arbitrary)]` and `#[proptest]`. Each of these macros is an alternative to the following proptest's official macros. | [test-strategy][] | [proptest][] | [proptest-derive][] | | ------------------------------------------ | ----------------------------- | ----------------------------------- | | [`#[derive(Arbitrary)]`](#derivearbitrary) | | [`#[derive(Arbitrary)]`][offical-a] | | [`#[proptest]`](#proptest) | [`proptest ! { }`][offical-m] | | [test-strategy]: https://crates.io/crates/test-strategy [proptest]: https://crates.io/crates/proptest [proptest-derive]: https://crates.io/crates/proptest-derive [offical-m]: https://altsysrq.github.io/rustdoc/proptest/latest/proptest/macro.proptest.html [offical-a]: https://altsysrq.github.io/proptest-book/proptest-derive/modifiers.html The macros provided by this crate have the following advantages over the proptest's official macros. - Supports higher-order strategies. (`#[derive(Arbitrary)]` and `#[proptest]`) - Code formatting is not disabled. (`#[proptest]`) However, the syntax of this crate's macros are not compatible with the syntax of the official macros. ## Install Add this to your Cargo.toml: ```toml [dependencies] test-strategy = "0.3.1" proptest = "1.0.0" ``` ## Example You can use `#[derive(Arbitrary)]` to automatically implement proptest's `Arbitrary` trait. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInputStruct { x: u32, #[strategy(1..10u32)] y: u32, #[strategy(0..#y)] z: u32, } #[derive(Arbitrary, Debug)] enum TestInputEnum { A, B, #[weight(3)] C, X(u32), Y(#[strategy(0..10u32)] u32), } ``` You can define a property test by adding `#[proptest]` to the function. ```rust use test_strategy::proptest; #[proptest] fn my_test(_x: u32, #[strategy(1..10u32)] y: u32, #[strategy(0..#y)] z: u32) { assert!(1 <= y && y < 10); assert!(z <= y); } ``` ## Attributes Attributes can be written in the following positions. | attribute | function | struct | enum | variant | field | function parameter | | --------------------------------------------------- | -------- | ------ | ---- | ------- | ----- | ------------------ | | [`#[strategy]`](#strategy) | | | | | ✔ | ✔ | | [`#[any]`](#any) | | | | | ✔ | ✔ | | [`#[weight]`](#weight) | | | | ✔ | | | | [`#[map]`](#map) | | | | | ✔ | ✔ | | [`#[filter]`](#filter) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | [`#[by_ref]`](#by_ref) | | | | | ✔ | ✔ | | [`#[arbitrary(args = T)]`](#arbitraryargs--t) | | ✔ | ✔ | | | | | [`#[arbitrary(bound(...))]`](#arbitraryboundt1-t2-) | | ✔ | ✔ | ✔ | ✔ | | | [`#[arbitrary(dump)]`](#arbitrarydump) | | ✔ | ✔ | | | | | [`#[proptest]`](#proptest) | ✔ | | | | | | | [`#[proptest(async = ...)]`](#proptestasync--) | ✔ | | | | | | | [`#[proptest(dump)]`](#proptestdump) | ✔ | | | | | | ## `#[derive(Arbitrary)]` You can implement `proptest::arbitrary::Arbitrary` automatically by adding `#[derive(Arbitrary)]` to struct or enum declaration. By default, all fields are set using the strategy obtained by `proptest::arbitrary::any()`. So the following two codes are equivalent. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { x: u32, y: u32, } ``` ```rust use proptest::{ arbitrary::{any, Arbitrary}, strategy::{BoxedStrategy, Strategy}, }; #[derive(Debug)] struct TestInput { x: u32, y: u32, } impl Arbitrary for TestInput { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { let x = any::(); let y = any::(); (x, y).prop_map(|(x, y)| Self { x, y }).boxed() } } ``` ## `#[strategy]` You can specify a strategy to generate values for the field by adding `#[strategy(...)]` to the field. In the following example, the value of field `x` will be less than 20. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[strategy(0..20u32)] x: u32, } ``` In `#[strategy]`, the values of other fields can be used by following `#` to the name of the field. In the following example, the value of `y` is less than or equal to `x`. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { x: u32, #[strategy(0..=#x)] y: u32, } ``` ## `#[any]` Instead of writing `#[strategy(any_with::(expr))]`, you can write `#[any(expr)]`. ```rust use proptest::collection::size_range; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestInput { #[any(size_range(0..16).lift())] x: Vec, } ``` Instead of writing an expression to be passed to `any_with`, you can write only the value of the field to be changed from the default value. Therefore, the following `TestInputA`, `TestInputB` and `TestInputC` are equivalent. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInputA { #[any(InnerArgs { upper : 20, ..InnerArgs::default() })] a: Inner, } #[derive(Arbitrary, Debug)] struct TestInputB { #[any(InnerArgs::default(), upper = 20)] a: Inner, } #[derive(Arbitrary, Debug)] struct TestInputC { #[any(upper = 20)] a: Inner, } #[derive(Default)] struct InnerArgs { lower: i32, upper: i32, } #[derive(Arbitrary, Debug)] #[arbitrary(args = InnerArgs)] struct Inner { #[strategy(args.lower..args.upper)] x: i32, } ``` ## `#[weight]` By default, all variants appear with equal probability. You can add `#[weight]` to the variant to change the probability of the variant appearing. In the following example, `TestInput::B` is twice as likely to appear as `TestInput::A`. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] enum TestInput { A, #[weight(2)] B, } ``` If you add `#[weight(0)]` to a variant, the variant does not appear, so you can use a type in that variant that cannot be used as `Arbitrary`. ```rust use test_strategy::Arbitrary; #[derive(Debug)] struct NotArbitrary; #[derive(Arbitrary, Debug)] enum TestInput { A, #[allow(dead_code)] #[weight(0)] // Removing this `#[weight(0)]` will cause a compile error. B(NotArbitrary), } ``` ## `#[map]` Instead of using `prop_map` in `#[strategy(...)]`, `#[map(...)]` can be used. The following codes mean the same thing. ```rust use proptest::arbitrary::any; use proptest::strategy::Strategy; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput1 { #[strategy(any::().prop_map(|x| x + 1))] x: u32, } #[derive(Arbitrary, Debug)] struct TestInput2 { #[strategy(any::())] #[map(|x| x + 1)] x: u32, } #[derive(Arbitrary, Debug)] struct TestInput3 { #[map(|x: u32| x + 1)] x: u32, } ``` References to other fields in the function applied to `prop_map` or `#[map(...)]` will generate different strategies. Referencing another field in `#[strategy(...)]` will expand it to `prop_flat_map`, even if it is in `prop_map`. ```rust use proptest::arbitrary::any; use proptest::strategy::{Just, Strategy}; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct T1 { x: u32, #[strategy(any::().prop_map(move |y| #x + y))] y: u32, } // The code above generates the following strategy. let t1 = any::() .prop_flat_map(|x| (Just(x), any::().prop_map(move |y| x + y))) .prop_map(|(x, y)| T1 { x, y }); ``` On the other hand, if you refer to another field in `#[map]`, it will expand to `prop_map`. ```rust use proptest::arbitrary::any; use proptest::strategy::Strategy; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct T2 { x: u32, #[map(|y: u32| #x + y)] y: u32, } // The code above generates the following strategy. let t2 = (any::(), any::()).prop_map(|(x, y)| T2 { x, y }); ``` If the input and output types of the function specified in `#[map]` are different, the value type of the strategy set in `#[strategy]` is the type of the function's input, not the type of the field. ```rust use proptest::arbitrary::any; use proptest::sample::Index; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct T1 { #[strategy(any::())] #[map(|i: Index| i.index(10))] x: usize, } // `#[strategy(any::())]` can be omitted. #[derive(Arbitrary, Debug)] struct T2 { #[map(|i: Index| i.index(10))] x: usize, } ``` ## `#[filter]` By adding `#[filter]` , you can limit the values generated. In the following examples, x is an even number. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[filter(#x % 2 == 0)] x: u32, } ``` You can also use multiple variables in a predicate. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] #[filter((#x + #y) % 2 == 0)] struct T1 { x: u32, y: u32, } #[derive(Arbitrary, Debug)] struct T2 { x: u32, #[filter((#x + #y) % 2 == 0)] y: u32, } ``` You can use the value of a structure or enum in the filter by using `#self`. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] #[filter((#self.x + #self.y) % 2 == 0)] struct TestInput { x: u32, y: u32, } ``` If the expression specified for `#[filter]` does not contain a variable named by appending # to its own field name, the expression is treated as a predicate function, rather than an expression that returns a bool. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[filter(is_even)] x: u32, } fn is_even(x: &u32) -> bool { x % 2 == 0 } #[derive(Arbitrary, Debug)] struct T2 { a: u32, // Since `#a` exists but `#b` does not, it is treated as a predicate function. #[filter(|&x| x > #a)] b: u32, } ``` Similarly, an expression that does not contain `#self` in the `#[filter(...)]` that it attaches to a type is treated as a predicate function. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] #[filter(is_even)] struct T { x: u32, } fn is_even(t: &T) -> bool { t.x % 2 == 0 } ``` You can specify a filter name by passing two arguments to `#[filter]`. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[filter("x is even", #x % 2 == 0)] x: u32, } ``` ## `#[by_ref]` By default, if you use a variable with `#[strategy]`, `#[any]`, `#[map]` or `#[filter]` with `#` attached to it, the cloned value is set. Adding `#[by_ref]` to the field makes it use the reference instead of the cloned value. ```rust use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[by_ref] #[strategy(1..10u32)] x: u32, #[strategy(0..*#x)] y: u32, } ``` ## `#[arbitrary]` ### `#[arbitrary(args = T)]` Specifies the type of `Arbitrary::Parameters`. You can use the `Rc` value of this type in `#[strategy]`, `#[any]`, or `#[filter]` with the variable name `args`. ```rust use test_strategy::Arbitrary; #[derive(Debug, Default)] struct TestInputArgs { x_max: u32, } #[derive(Arbitrary, Debug)] #[arbitrary(args = TestInputArgs)] struct TestInput { #[strategy(0..=args.x_max)] x: u32, } ``` ### `#[arbitrary(bound(T1, T2, ..))]` By default, if the type of field for which `#[strategy]` is not specified contains a generic parameter, that type is set to trait bounds. Therefore, the following `TestInputA` and `TestInputB` are equivalent. ```rust use proptest::{ arbitrary::any, arbitrary::Arbitrary, strategy::BoxedStrategy, strategy::Strategy, }; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInputA { x: T, } #[derive(Debug)] struct TestInputB { x: T, } impl Arbitrary for TestInputB { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { any::().prop_map(|x| Self { x }).boxed() } } ``` Types of fields with `#[strategy]` do not set trait bounds automatically, so you need to set trait bound manually with `#[arbitrary(bound(T))]`. ```rust use proptest::arbitrary::any_with; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T))] struct TestInput { #[strategy(any_with::(Default::default()))] x: T, } ``` You can also specify where predicate instead of type. ```rust use proptest::arbitrary::{any_with, Arbitrary}; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T : Arbitrary + 'static))] struct TestInput { #[strategy(any_with::(Default::default()))] x: T, } ``` `..` means automatically generated trait bounds. The following example uses a manually specified trait bounds in addition to the automatically generated trait bounds. ```rust use proptest::arbitrary::any_with; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T1, ..))] struct TestInput { #[strategy(any_with::(Default::default()))] x: T1, y: T2, } ``` ### `#[arbitrary(dump)]` Causes a compile error and outputs the code generated by `#[derive(Arbitrary)]` as an error message. ## `#[proptest]` `#[proptest]` is the attribute used instead of `#[test]` when defining a property test. The following example defines a test that takes a variety of integers as input. ```rust use test_strategy::proptest; #[proptest] fn my_test(_input: i32) { // ... } ``` You can add `#[strategy]`, `#[any]`, `#[filter]`, `#[by_ref]` to the parameter of the function with `# [proptest]`. ```rust use test_strategy::proptest; #[proptest] fn my_test2(#[strategy(10..20)] _input: i32) { // ... } ``` You can change the configuration of a property test by setting the argument of `#[proptest]` attribute to a value of `proptest::prelude::ProptestConfig` type. ```rust use proptest::prelude::ProptestConfig; use test_strategy::proptest; #[proptest(ProptestConfig { cases : 1000, ..ProptestConfig::default() })] fn my_test_with_config(_input: i32) { // ... } ``` As with `#[any]`, you can also set only the value of the field to be changed from the default value. The example below is equivalent to the one above. ```rust use proptest::prelude::ProptestConfig; use test_strategy::proptest; #[proptest(ProptestConfig::default(), cases = 1000)] fn my_test_with_config_2(_input: i32) { // ... } #[proptest(cases = 1000)] fn my_test_with_config_3(_input: i32) { // ... } ``` ### `#[proptest(async = ...)]` Async functions can be tested by setting `async = ...` to the argument of `#[proptest]`. The following values are allowed after `async =`. The value specifies the asynchronous runtime used for the test. - "tokio" ```toml [dev-dependencies] test-strategy = "0.3" proptest = "1.1.0" tokio = { version = "1.28.1", features = ["rt-multi-thread"] } ``` ```rust use test_strategy::proptest; use proptest::prop_assert; #[proptest(async = "tokio")] async fn my_test_async() { async { }.await; prop_assert!(true); } ``` ### `#[proptest(dump)]` You can use `#[proptest(dump)]` and output the code generated by `#[proptest]` as an compile error message. ```compile_fail #[proptest(dump)] fn my_test(_input: i32) { // ... } ``` ## License This project is dual licensed under Apache-2.0/MIT. See the two LICENSE-\* files for details. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. test-strategy-0.3.1/examples/in_not_cfg_test.rs000064400000000000000000000002060072674642500200220ustar 00000000000000fn main() {} #[test_strategy::proptest] fn arg(x: u8) {} #[test_strategy::proptest(cases = 100)] fn arg_and_config(x: u8) {} test-strategy-0.3.1/src/arbitrary.rs000064400000000000000000001242510072674642500156350ustar 00000000000000use crate::syn_utils::{ impl_trait_result, span_in_args, to_valid_ident, Arg, Args, FieldKey, GenericParamSet, SharpVals, }; use crate::{bound::*, syn_utils::parse_from_attrs}; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use std::collections::BTreeMap; use std::{collections::HashMap, fmt::Write, mem::take}; use structmeta::*; use syn::{ parse_quote, parse_str, spanned::Spanned, Attribute, Data, DataEnum, DataStruct, DeriveInput, Expr, Fields, Ident, Lit, Member, Path, Result, Type, }; use syn::{parse_quote_spanned, Meta, Pat}; pub fn derive_arbitrary(input: DeriveInput) -> Result { let args: ArbitraryArgsForType = parse_from_attrs(&input.attrs, "arbitrary")?; let type_parameters = if let Some(ty) = &args.args { quote_spanned!(ty.span()=> type Parameters = #ty;) } else { quote! { type Parameters = (); } }; let mut bounds = Bounds::from_data(args.bound); let expr = match &input.data { Data::Struct(data) => expr_for_struct(&input, data, &mut bounds)?, Data::Enum(data) => expr_for_enum(&input, data, &mut bounds)?, _ => bail!( input.span(), "`#[derive(Arbitrary)]` supports only enum and struct." ), }; impl_trait_result( &input, &parse_quote!(proptest::arbitrary::Arbitrary), &bounds.build_wheres(quote!(proptest::arbitrary::Arbitrary + 'static)), quote! { #type_parameters type Strategy = proptest::strategy::BoxedStrategy; #[allow(clippy::redundant_closure_call)] fn arbitrary_with(args: ::Parameters) -> Self::Strategy { #[allow(dead_code)] fn _filter_fn(f: impl Fn(&T) -> bool) -> impl Fn(&T) -> bool { f } #[allow(dead_code)] fn _filter_fn_once(f: impl FnOnce(&T) -> bool) -> impl FnOnce(&T) -> bool { f } #[allow(dead_code)] fn _map_to_any U>( _: impl Fn() -> F, ) -> impl proptest::strategy::Strategy { proptest::arbitrary::any::() } #[allow(dead_code)] fn _map_to_any_with_init U>( _: impl Fn() -> F, p: ::Parameters, ) -> ::Parameters { p } #[allow(dead_code)] fn _map_to_any_with U>( _: impl Fn() -> F, args: ::Parameters, ) -> impl proptest::strategy::Strategy { proptest::arbitrary::any_with::(args) } #[allow(unused_variables)] let args = std::rc::Rc::new(args); proptest::strategy::Strategy::boxed(#expr) } }, args.dump.value(), ) } fn expr_for_struct( input: &DeriveInput, data: &DataStruct, bounds: &mut Bounds, ) -> Result { let generics = GenericParamSet::new(&input.generics); expr_for_fields( parse_quote!(Self), &generics, &data.fields, &input.attrs, true, bounds, ) } fn expr_for_enum(input: &DeriveInput, data: &DataEnum, bounds: &mut Bounds) -> Result { if data.variants.is_empty() { bail!(Span::call_site(), "zero variant enum was not supported."); } let generics = GenericParamSet::new(&input.generics); let mut exprs = Vec::new(); for variant in &data.variants { let args: ArbitraryArgsForFieldOrVariant = parse_from_attrs(&variant.attrs, "arbitrary")?; let mut weight = None; for attr in &variant.attrs { if attr.path().is_ident("weight") { if weight.is_some() { bail!(attr.span(), "`#[weight]` can specify only once."); } weight = Some(attr.parse_args::()?); } } let weight = if let Some(arg) = weight { if arg.is_zero() { continue; } else { let expr = arg.0; quote_spanned!(expr.span()=> _to_weight(#expr)) } } else { quote!(1) }; let variant_ident = &variant.ident; let mut bounds = bounds.child(args.bound); let expr = expr_for_fields( parse_quote!(Self::#variant_ident), &generics, &variant.fields, &variant.attrs, false, &mut bounds, )?; exprs.push(quote! {#weight=> #expr}); } let s: Ident = parse_quote!(_s); let filter_lets = Filter::from_enum_attrs_make_let(&input.attrs, &s)?; Ok(quote! { { #[allow(dead_code)] fn _to_weight(weight: u32) -> u32 { weight } let #s = proptest::prop_oneof![#(#exprs,)*]; #filter_lets #s } }) } fn expr_for_fields( self_path: Path, generics: &GenericParamSet, fields: &Fields, attrs: &[Attribute], filter_allow_fn: bool, bounds: &mut Bounds, ) -> Result { let b = StrategyBuilder::from_fields(self_path, fields, attrs, filter_allow_fn)?; b.get_bound_types(generics, bounds)?; b.build() } #[derive(StructMeta)] struct WeightArg(Expr); impl WeightArg { fn is_zero(&self) -> bool { if let Expr::Lit(lit) = &self.0 { if let Lit::Int(lit) = &lit.lit { if let Ok(value) = lit.base10_parse::() { return value == 0; } } } false } } #[derive(StructMeta)] struct AnyArgs { #[struct_meta(unnamed)] initializer: Option, setters: HashMap>, } impl Default for AnyArgs { fn default() -> Self { Self::empty() } } impl AnyArgs { fn empty() -> Self { Self { initializer: None, setters: HashMap::new(), } } fn into_strategy(self, ty: &StrategyValueType) -> TokenStream { if self.initializer.is_none() && self.setters.is_empty() { ty.any() } else { let init = self .initializer .unwrap_or_else(|| parse_quote!(std::default::Default::default())) .to_token_stream(); if self.setters.is_empty() { ty.any_with(init) } else { let mut setters: Vec<_> = self.setters.into_iter().collect(); setters.sort_by(|v0, v1| v0.0.cmp(&v1.0)); let setters = setters.into_iter().map(|(name, expr)| { let member = Member::Named(to_valid_ident(&name).unwrap()); let expr = &expr.value; quote!(_any_args.#member = #expr;) }); let any_with = ty.any_with(quote!(_any_args)); let any_with_args_let = ty.any_with_args_let(quote!(_any_args), init); quote! { { #any_with_args_let; #(#setters)* #any_with } } } } } } #[derive(StructMeta, Default)] struct ArbitraryArgsForType { args: Option, bound: Option>, dump: Flag, } #[derive(StructMeta, Default)] struct ArbitraryArgsForFieldOrVariant { bound: Option>, } #[derive(Clone)] struct Filter { whence: Expr, expr: Expr, } impl Filter { fn parse(span: Span, args: Args) -> Result { let mut values = Vec::new(); for arg in args { match arg { Arg::Value(value) => { values.push(value); } Arg::NameValue { .. } => bail!(arg.span(), "named argument was not supported."), } } let whence; let expr; match values.len() { 1 => { let mut iter = values.into_iter(); expr = iter.next().unwrap(); let whence_str = expr.to_token_stream().to_string(); whence = parse_quote!(#whence_str); } 2 => { let mut iter = values.into_iter(); whence = iter.next().unwrap(); expr = iter.next().unwrap(); } _ => bail!( span, "expected `#[filter(whence, fun)]` or `#[filter(fun)]`." ), } Ok(Self { whence, expr }) } fn from_enum_attrs_make_let(attrs: &[Attribute], var: &Ident) -> Result { let mut results = TokenStream::new(); for attr in attrs { if attr.path().is_ident("filter") { let mut sharp_vals = SharpVals::new(false, true); let mut filter = Filter::parse(attr.span(), sharp_vals.expand_args(&attr.meta)?)?; if sharp_vals.self_span.is_none() { filter = filter.with_fn_arg_self(); } results.extend(filter.make_let_as_expr(var, "e!(_self), "e!())); } } Ok(results) } fn with_fn_arg(&self, arg: &Ident, arg_by_ref: bool, arg_ty: &Type) -> Self { let span = self.expr.span(); let expr = &self.expr; let arg = if arg_by_ref { quote!(#arg) } else { quote!(&#arg) }; let expr = parse_quote_spanned!(span=> (_filter_fn_once::<#arg_ty>(#expr))(#arg)); Self { expr, whence: self.whence.clone(), } } fn with_fn_arg_self(&self) -> Self { self.with_fn_arg(&parse_quote!(_self), true, &parse_quote!(Self)) } fn make_let_as_expr(&self, var: &Ident, ps: &TokenStream, lets: &TokenStream) -> TokenStream { let expr = self.expr.to_token_stream(); Self::make_let_with(var, &self.whence, ps, lets, expr) } fn make_let_as_fn(&self, var: &Ident, arg_ty: &Type) -> TokenStream { let span = self.expr.span(); let expr = &self.expr; let fun = parse_quote_spanned!(span=> _filter_fn::<#arg_ty>(#expr)); Self::make_let_with_fun(var, &self.whence, fun) } fn make_let_with( var: &Ident, whence: &Expr, ps: &TokenStream, lets: &TokenStream, expr: TokenStream, ) -> TokenStream { let fun = quote_spanned! {expr.span()=> move |#ps| { #lets #expr }}; Self::make_let_with_fun(var, whence, fun) } fn make_let_with_fun(var: &Ident, whence: &Expr, fun: TokenStream) -> TokenStream { quote_spanned! {fun.span()=> let #var = { #[allow(unused_variables)] let args = as std::clone::Clone>::clone(&args); proptest::strategy::Strategy::prop_filter(#var, #whence, #fun) }; } } } #[derive(Clone)] struct UnaryFilter { filter: Filter, arg_exists: bool, arg: Ident, arg_by_ref: bool, arg_ty: Type, } impl UnaryFilter { fn make_let_expr(&self, var: &Ident, ps: &TokenStream, lets: &TokenStream) -> TokenStream { self.to_expr_filter().make_let_as_expr(var, ps, lets) } fn make_let_fn(&self, var: &Ident) -> TokenStream { if self.arg_exists { let arg = &self.arg; let arg_ty = &self.arg_ty; let lets = if self.arg_by_ref { quote!() } else { quote!(let #arg = <#arg_ty as std::clone::Clone>::clone(#arg);) }; self.filter.make_let_as_expr(var, "e!(#arg), &lets) } else { self.filter.make_let_as_fn(var, &self.arg_ty) } } fn to_expr_filter(&self) -> Filter { if self.arg_exists { self.filter.clone() } else { self.filter .with_fn_arg(&self.arg, self.arg_by_ref, &self.arg_ty) } } } struct FieldsFilter { filter: Filter, _vals: Vec, } struct StrategyBuilder { ts: TokenStream, items: Vec, self_path: Path, fields: Fields, filters_fields: Vec, filters_self: Vec, } impl StrategyBuilder { fn from_fields( self_path: Path, fields: &Fields, attrs: &[Attribute], filter_allow_self: bool, ) -> Result { let mut ts = TokenStream::new(); let mut fs = Vec::new(); let mut by_refs = Vec::new(); let mut key_to_idx = HashMap::new(); for (idx, field) in fields.iter().enumerate() { key_to_idx.insert(FieldKey::from_field(idx, field), idx); fs.push(field); let mut by_ref = false; for attr in &field.attrs { if attr.path().is_ident("by_ref") { by_ref = true; match &attr.meta { Meta::Path(_) => {} _ => { let span = span_in_args(&attr.meta); bail!(span, "Arguments are not allowed."); } } } } by_refs.push(by_ref); } let mut items_field = Vec::new(); let mut items_other = Vec::new(); let mut filters_fields = Vec::new(); for (idx, field) in fields.iter().enumerate() { let key = FieldKey::from_field(idx, field); let mut expr_strategy = None::; let mut expr_map = None::; let mut filters_field = Vec::new(); let mut sharp_vals_strategy = SharpVals::new(true, false); let mut sharp_vals_map = SharpVals::new(true, false); let by_ref = by_refs[idx]; let mut is_any = false; let mut strategy_value_type = StrategyValueType::Type(field.ty.clone()); for attr in &field.attrs { if attr.path().is_ident("map") { if expr_map.is_some() { bail!(attr.span(), "`#[map]` can be specified only once."); } let args: Args = sharp_vals_map.expand_args_or_default(&attr.meta)?; let expr = args.expect_single_value(attr.span())?; let ty = &field.ty; let input = key.to_dummy_ident(); expr_map = Some(StrategyExpr::new( quote_spanned!(expr.span()=> #ty), quote_spanned!(expr.span()=> (#expr)(#input)), true, )); if let Some(ty) = input_type(expr) { strategy_value_type = StrategyValueType::Type(ty.clone()); } else { let dependency_map = to_idxs(&sharp_vals_map.vals, &key_to_idx)?; let mut lets = Vec::new(); for idx in dependency_map { let field = &fs[idx]; let key = FieldKey::from_field(idx, field); let ident = key.to_dummy_ident(); let ty = &field.ty; let ty = if by_refs[idx] { quote!(&#ty) } else { quote!(#ty) }; lets.push(quote!(let #ident : #ty = unreachable!();)) } strategy_value_type = StrategyValueType::Map(quote! { || { #[allow(clippy::diverging_sub_expression)] #[allow(unreachable_code)] { #(#lets)* #expr } }}); } } } for attr in &field.attrs { let is_strategy_attr = attr.path().is_ident("strategy"); let is_any_attr = attr.path().is_ident("any"); if expr_strategy.is_some() && (is_strategy_attr || is_any_attr) { bail!( attr.span(), "`#[any]` and `#[strategy]` can only be specified once in total." ); } if is_strategy_attr { let args: Args = sharp_vals_strategy.expand_args_or_default(&attr.meta)?; let expr = args.expect_single_value(attr.span())?; let ty = strategy_value_type.get(); let func_ident = Ident::new(&format!("_strategy_of_{key}"), expr.span()); ts.extend(quote_spanned! {ty.span()=> #[allow(dead_code)] #[allow(non_snake_case)] fn #func_ident>(s: S) -> impl proptest::strategy::Strategy { s } }); expr_strategy = Some(StrategyExpr::new( quote!(_), quote_spanned!(expr.span()=> #func_ident::<#ty, _>( { #[allow(unused_variables)] let args = std::ops::Deref::deref(&args); #expr })), false, )); } if is_any_attr { is_any = true; let any_attr: AnyArgs = sharp_vals_strategy.expand_args_or_default(&attr.meta)?; expr_strategy = Some(StrategyExpr::new( quote!(), any_attr.into_strategy(&strategy_value_type), false, )); } } for attr in &field.attrs { if attr.path().is_ident("filter") { let mut sharp_vals = SharpVals::new(true, false); let filter = Filter::parse(attr.span(), sharp_vals.expand_args(&attr.meta)?)?; let arg = key.to_dummy_ident(); let uf = UnaryFilter { filter, arg_exists: sharp_vals.vals.contains_key(&key), arg_by_ref: by_ref, arg_ty: field.ty.clone(), arg, }; if sharp_vals.vals.is_empty() || (uf.arg_exists && sharp_vals.vals.len() == 1) { filters_field.push(uf); } else { let mut vals = to_idxs(&sharp_vals.vals, &key_to_idx)?; if !uf.arg_exists { vals.push(idx); vals.sort_unstable(); } let filter = uf.to_expr_filter(); filters_fields.push(FieldsFilter { filter, _vals: vals, }); } } } let mut expr_strategy = if let Some(expr_strategy) = expr_strategy { expr_strategy } else { is_any = true; let ty = strategy_value_type.get(); StrategyExpr::new( quote!(#ty), AnyArgs::empty().into_strategy(&strategy_value_type), false, ) }; let dependency_strategy = to_idxs(&sharp_vals_strategy.vals, &key_to_idx)?; let arbitrary_type = if is_any { if let StrategyValueType::Type(ty) = strategy_value_type { Some(ty) } else { None } } else { None }; if let Some(mut expr_map) = expr_map { let base_idx = fields.len() + items_other.len(); let mut dependency_map = to_idxs(&sharp_vals_map.vals, &key_to_idx)?; dependency_map.push(base_idx); expr_map.filters = filters_field; items_field.push(StrategyItem::new( idx, key.clone(), by_ref, true, Some(base_idx), arbitrary_type, expr_map, dependency_map, )); items_other.push(StrategyItem::new( base_idx, key, false, false, None, None, expr_strategy, dependency_strategy, )); } else { expr_strategy.filters = filters_field; items_field.push(StrategyItem::new( idx, key, by_ref, true, None, arbitrary_type, expr_strategy, dependency_strategy, )); } } let mut filters_self = Vec::new(); for attr in attrs { if attr.path().is_ident("filter") { let mut sharp_vals = SharpVals::new(true, filter_allow_self); let mut filter = Filter::parse(attr.span(), sharp_vals.expand_args(&attr.meta)?)?; if !sharp_vals.vals.is_empty() { let vals = to_idxs(&sharp_vals.vals, &key_to_idx)?; filters_fields.push(FieldsFilter { filter, _vals: vals, }); } else if filter_allow_self { if sharp_vals.self_span.is_none() { filter = filter.with_fn_arg_self(); } filters_self.push(filter); } else { let span = span_in_args(&attr.meta); bail!(span, "Filters that reference `self` in the variant (filters with no reference to the field) cannot be set.") } } } let fields = fields.clone(); items_field.extend(items_other); Ok(Self { ts, items: items_field, self_path, fields, filters_fields, filters_self, }) } fn get_bound_types(&self, generics: &GenericParamSet, bounds: &mut Bounds) -> Result<()> { if !bounds.can_extend { return Ok(()); } for (idx, field) in self.fields.iter().enumerate() { let args: ArbitraryArgsForFieldOrVariant = parse_from_attrs(&field.attrs, "arbitrary")?; let mut bounds = bounds.child(args.bound); if bounds.can_extend { if let Some(ty) = &self.items[idx].arbitrary_type { if generics.contains_in_type(ty) { bounds.ty.push(ty.clone()); } } } } Ok(()) } fn build(mut self) -> Result { if self.items.is_empty() { let constructor = build_constructor(&self.self_path, &self.fields, quote!()); return Ok(quote! { proptest::strategy::LazyJust::new(|| #constructor ) }); } for item in &mut self.items { item.try_create_independent_strategy(&mut self.ts); } while !self.is_exists_all_fields() { if !self.try_create_dependent_strategy() { let mut cd_str = String::new(); let cd_idxs = self.get_cyclic_dependency().unwrap(); for &cd_idx in &cd_idxs { let item = &self.items[cd_idx]; if item.is_field { write!(&mut cd_str, "{} -> ", &item.key).unwrap(); } } for &cd_idx in &cd_idxs { let item = &self.items[cd_idx]; if item.is_field { write!(&mut cd_str, "{}", &item.key).unwrap(); break; } } bail!(Span::call_site(), "found cyclic dependency. ({0})", cd_str); } } self.merge_all_groups(); let var = &self.items[0].strategy_ident(); let ps = self.pat_group_vars(0); for filter in &self.filters_fields { let lets = self.let_group_vars(0, true); self.ts .extend(filter.filter.make_let_as_expr(var, &ps, &lets)); } let mut args = Vec::new(); for idx in 0..self.fields.len() { let key = &self.items[idx].key; let value = key.to_dummy_ident(); args.push(if let Some(key) = key.to_valid_ident() { quote!(#key : #value) } else { quote!(#value) }); } let constructor = build_constructor(&self.self_path, &self.fields, quote!(#(#args, )*)); self.ts.extend(quote! { let #var = proptest::strategy::Strategy::prop_map(#var, |#ps| #constructor); }); for filter in &self.filters_self { self.ts .extend(filter.make_let_as_expr(var, "e!(_self), "e!())); } self.ts.extend(quote!(#var)); let ts = self.ts; Ok(quote! { { #ts } }) } fn try_create_dependent_strategy(&mut self) -> bool { let mut created = false; for idx in 0..self.items.len() { if self.is_exists(idx) { continue; } let group_next = self.items[idx] .dependency .iter() .map(|&idx| self.resolve_group_next_input(idx)) .min() .unwrap_or(None); if let Some(group_next) = group_next { self.set_group_next_new(idx, group_next); created = true; } } for idx in 0..self.items.len() { if let Some(group_next) = self.resolve_group_next_input(idx) { self.set_group_next(idx, group_next); } } for idx in 0..self.items.len() { self.register_group_dependency(idx); } for idx in 0..self.items.len() { if !self.is_exists(idx) { self.register_group_next_items(idx); } } for idx in 0..self.items.len() { if self.is_exists(idx) { self.register_group_next_items(idx); } } for idx in 0..self.items.len() { if self.is_group_next_new(idx) { let mut inputs = Vec::new(); let mut ps = Vec::new(); let mut exprs = Vec::new(); for &input_idx in &self.items[idx].group_dependency { inputs.push(self.items[input_idx].strategy_ident()); ps.push(self.pat_group_vars(input_idx)); } let inputs = cons_tuple(&inputs); let ps = cons_tuple(&ps); for &group_item_next in &self.items[idx].group_items_next { exprs.push(self.strategy_expr(group_item_next)); } let ident = self.items[idx].strategy_ident(); let exprs = if exprs.iter().all(|e| e.is_jast) { let var = Ident::new("_s", Span::call_site()); let lets = self.let_group_vars(idx, true); let mut filter_lets = TokenStream::new(); for expr in &exprs { for fitler in &expr.filters { filter_lets.extend(fitler.make_let_expr(&var, &ps, &lets)); } } let exprs: Vec<_> = exprs.iter().map(|e| &e.expr).collect(); let exprs = cons_tuple(&exprs); quote! { { let #var = { #[allow(unused_variables)] let args = as std::clone::Clone>::clone(&args); proptest::strategy::Strategy::prop_map((#inputs), move |#ps| #exprs) }; #filter_lets #var } } } else { let exprs = cons_tuple(&exprs); quote!(proptest::strategy::Strategy::prop_flat_map(#inputs, move |#ps| #exprs)) }; self.ts.extend(quote! { let #ident = { #[allow(unused_variables)] let args = as std::clone::Clone>::clone(&args); #exprs }; }); } } for idx in 0..self.items.len() { self.items[idx].group = self.items[idx].group_next; self.items[idx].offset = self.items[idx].offset_next.take(); self.items[idx].group_offset = None; self.items[idx].group_items = take(&mut self.items[idx].group_items_next); self.items[idx].group_dependency.clear(); } created } fn resolve_group_next_input(&self, idx: usize) -> Option { let item_ref = &self.items[idx]; item_ref.group?; let group_next = item_ref.group_next?; if group_next == idx { return Some(idx); } self.resolve_group_next_input(group_next) } fn set_group_next_new(&mut self, idx: usize, group_next: usize) { if let Some(base_idx) = self.items[idx].base_idx { self.items[base_idx].is_dropped = true; } self.set_group_next(idx, group_next); for i in 0..self.items[idx].dependency.len() { self.set_group_next(self.items[idx].dependency[i], group_next) } } fn set_group_next(&mut self, group: usize, group_next: usize) { let group_next_old = self.items[group].group_next; if group_next_old == Some(group_next) { return; } if let Some(group_next_old) = group_next_old { if group_next_old != group { self.set_group_next(group_next_old, group_next) } } self.items[group].group_next = Some(group_next); } fn merge_all_groups(&mut self) { for idx in 0..self.items.len() { self.items[idx].group_next = Some(0); } self.merge_groups(); } fn merge_groups(&mut self) { for idx in 0..self.items.len() { self.items[idx].group_next = Some(0); } for idx in 0..self.items.len() { self.register_group_dependency(idx); self.register_group_next_items(idx); } for idx in 0..self.items.len() { if self.is_group_next_new(idx) { let mut inputs = Vec::new(); let mut ps = Vec::new(); let mut exprs = Vec::new(); for &input_idx in &self.items[idx].group_dependency { inputs.push(self.items[input_idx].strategy_ident()); ps.push(self.pat_group_vars(input_idx)); } for &group_item_next in &self.items[idx].group_items_next { exprs.push(self.items[group_item_next].key.to_dummy_ident()); } let var = self.items[idx].strategy_ident(); let inputs = cons_tuple(&inputs); let ps = cons_tuple(&ps); let exprs = cons_tuple(&exprs); self.ts.extend(quote! { let #var = proptest::strategy::Strategy::prop_map(#inputs, |#ps| #exprs); }); } } for idx in 0..self.items.len() { self.items[idx].group = self.items[idx].group_next; self.items[idx].offset = self.items[idx].offset_next.take(); self.items[idx].group_offset = None; self.items[idx].group_items = take(&mut self.items[idx].group_items_next); self.items[idx].group_dependency.clear(); } } fn register_group_dependency(&mut self, idx: usize) { if let Some(group_next) = self.items[idx].group_next { if self.is_group(idx) { self.items[idx].group_offset = Some(self.items[group_next].group_dependency.len()); self.items[group_next].group_dependency.push(idx); } } } fn register_group_next_items(&mut self, idx: usize) { let item = &self.items[idx]; if !item.is_dropped { if let Some(group_next) = item.group_next { debug_assert!(item.offset_next.is_none()); self.items[idx].offset_next = Some(self.items[group_next].group_items_next.len()); self.items[group_next].group_items_next.push(idx); } } } fn get_cyclic_dependency(&self) -> Option> { let mut results = Vec::new(); let mut to_offset = HashMap::new(); if self.get_cyclic_dependency_impl(0, &mut results, &mut to_offset) { Some(results) } else { None } } fn get_cyclic_dependency_impl( &self, idx: usize, results: &mut Vec, to_offset: &mut HashMap, ) -> bool { if let Some(&offset) = to_offset.get(&idx) { results.drain(0..offset); return true; } to_offset.insert(idx, results.len()); results.push(idx); for &dep in &self.items[idx].dependency { if self.get_cyclic_dependency_impl(dep, results, to_offset) { return true; } } results.pop(); to_offset.remove(&idx); false } fn is_exists_all_fields(&self) -> bool { (0..self.fields.len()).all(|idx| self.is_exists(idx)) } fn is_exists(&self, idx: usize) -> bool { let item = &self.items[idx]; item.group.is_some() || item.is_dropped } fn is_group(&self, idx: usize) -> bool { self.items[idx].group == Some(idx) } fn is_group_next_new(&self, idx: usize) -> bool { let item = &self.items[idx]; item.group_next == Some(idx) && item.group_items_next != item.group_items } fn pat_group_vars(&self, idx: usize) -> TokenStream { assert!(self.is_group(idx)); let mut ps = Vec::new(); for &idx in &self.items[idx].group_items { ps.push(self.items[idx].key.to_dummy_ident()); } cons_tuple(&ps) } fn let_group_vars(&self, idx: usize, from_ref: bool) -> TokenStream { let mut lets = Vec::new(); for &idx in &self.items[idx].group_items { lets.push(self.items[idx].let_sharp_val(from_ref)); } quote!(#(#lets)*) } fn strategy_expr(&self, idx: usize) -> StrategyExpr { if self.is_exists(idx) { let ident = self.items[idx].key.to_dummy_ident(); StrategyExpr::new(quote!(_), quote!(std::clone::Clone::clone(&#ident)), true) } else { let item = &self.items[idx]; let mut lets = Vec::new(); for &dep in &item.dependency { lets.push(self.items[dep].let_sharp_val(false)); } lets.push(quote! { #[allow(unused_variables)] let args = std::ops::Deref::deref(&args); }); let expr = &item.expr.expr; StrategyExpr { expr: quote! { { #(#lets)* #expr } }, filters: item.expr.filters.clone(), ty: item.expr.ty.clone(), is_jast: item.expr.is_jast, } } } } struct StrategyItem { idx: usize, key: FieldKey, by_ref: bool, is_field: bool, base_idx: Option, arbitrary_type: Option, expr: StrategyExpr, dependency: Vec, is_dropped: bool, group: Option, group_next: Option, offset: Option, offset_next: Option, group_items: Vec, group_items_next: Vec, group_dependency: Vec, group_offset: Option, } impl StrategyItem { #[allow(clippy::too_many_arguments)] fn new( idx: usize, key: FieldKey, by_ref: bool, is_field: bool, base_idx: Option, arbitrary_type: Option, expr: StrategyExpr, dependency: Vec, ) -> Self { Self { idx, key, by_ref, is_field, base_idx, arbitrary_type, expr, dependency, is_dropped: false, group: None, group_next: None, offset: None, offset_next: None, group_items: Vec::new(), group_items_next: Vec::new(), group_dependency: Vec::new(), group_offset: None, } } fn try_create_independent_strategy(&mut self, ts: &mut TokenStream) -> bool { if self.group.is_none() && self.dependency.is_empty() { let ident = self.strategy_ident(); let expr = &self.expr; ts.extend(quote!(let #ident = #expr;)); self.group = Some(self.idx); self.group_next = self.group; self.offset = Some(0); self.offset_next = None; self.group_items.push(self.idx); true } else { false } } fn strategy_ident(&self) -> Ident { parse_str(&format!("strategy_{}", self.idx)).unwrap() } fn let_sharp_val(&self, from_ref: bool) -> TokenStream { let ident = self.key.to_dummy_ident(); let expr = if from_ref { quote!(#ident) } else { quote!(&#ident) }; let expr = if self.by_ref { expr } else { quote!(std::clone::Clone::clone(#expr)) }; quote!(let #ident = #expr;) } } enum StrategyValueType { Type(Type), Map(TokenStream), } impl StrategyValueType { fn get(&self) -> Type { match self { StrategyValueType::Type(ty) => ty.clone(), StrategyValueType::Map(_) => parse_quote!(_), } } fn any(&self) -> TokenStream { match self { StrategyValueType::Type(ty) => quote!(proptest::arbitrary::any::<#ty>()), StrategyValueType::Map(expr) => quote!(_map_to_any(#expr)), } } fn any_with_args_let(&self, var: TokenStream, init: TokenStream) -> TokenStream { match self { StrategyValueType::Type(ty) => { quote!(let mut #var :<#ty as proptest::arbitrary::Arbitrary>::Parameters = #init;) } StrategyValueType::Map(expr) => { quote!(let mut #var = _map_to_any_with_init(#expr, #init);) } } } fn any_with(&self, args: TokenStream) -> TokenStream { match self { StrategyValueType::Type(ty) => quote!(proptest::arbitrary::any_with::<#ty>(#args)), StrategyValueType::Map(expr) => quote!(_map_to_any_with(#expr, #args)), } } } struct StrategyExpr { expr: TokenStream, filters: Vec, ty: TokenStream, is_jast: bool, } impl StrategyExpr { fn new(ty: TokenStream, expr: TokenStream, is_jast: bool) -> Self { Self { ty, expr, is_jast, filters: Vec::new(), } } } impl ToTokens for StrategyExpr { fn to_tokens(&self, tokens: &mut TokenStream) { let expr = &self.expr; let expr = if self.is_jast { let ty = &self.ty; quote!(proptest::strategy::Just::<#ty>(#expr)) } else { quote!(#expr) }; let expr = if self.filters.is_empty() { expr } else { let mut filter_lets = Vec::new(); let var: Ident = parse_quote! { _s }; for filter in &self.filters { filter_lets.push(filter.make_let_fn(&var)); } quote! { { let #var = #expr; #(#filter_lets)* #var } } }; tokens.extend(expr); } } fn build_constructor(path: &Path, fields: &Fields, args: TokenStream) -> TokenStream { let args = match fields { Fields::Named(_) => quote! { {#args} }, Fields::Unnamed(_) => quote! { (#args) }, Fields::Unit => quote! {}, }; quote!(#path #args) } fn to_idxs( vals: &BTreeMap, key_to_idx: &HashMap, ) -> Result> { let mut idxs = Vec::new(); for (key, &span) in vals { if let Some(&idx) = key_to_idx.get(key) { idxs.push(idx); } else { bail!(span, "cannot find value `#{}` in this scope.", key); } } idxs.sort_unstable(); Ok(idxs) } fn input_type(expr: &Expr) -> Option<&Type> { if let Expr::Closure(closure) = expr { let inputs = &closure.inputs; if inputs.len() == 1 { if let Pat::Type(t) = &inputs[0] { return Some(&t.ty); } } } None } fn cons_tuple(es: &[impl ToTokens]) -> TokenStream { match es { [] => quote!(()), [e0] => quote!(#e0), [e0, e1] => quote!((#e0, #e1)), [e0, el @ ..] => { let el = cons_tuple(el); quote!((#e0, #el)) } } } test-strategy-0.3.1/src/bound.rs000064400000000000000000000053530072674642500147460ustar 00000000000000use proc_macro2::TokenStream; use std::ops::{Deref, DerefMut}; use syn::Token; use syn::{ parse::{discouraged::Speculative, Parse, ParseStream}, parse_quote, Result, Type, WherePredicate, }; #[allow(clippy::large_enum_variant)] pub enum Bound { Type(Type), Predicate(WherePredicate), Default(Token![..]), } impl Parse for Bound { fn parse(input: ParseStream) -> Result { if input.peek(Token![..]) { return Ok(Self::Default(input.parse()?)); } let fork = input.fork(); match fork.parse() { Ok(p) => { input.advance_to(&fork); Ok(Self::Predicate(p)) } Err(e) => { if let Ok(ty) = input.parse() { Ok(Self::Type(ty)) } else { Err(e) } } } } } pub struct Bounds { pub ty: Vec, pub pred: Vec, pub can_extend: bool, } impl Bounds { pub fn new(can_extend: bool) -> Self { Bounds { ty: Vec::new(), pred: Vec::new(), can_extend, } } pub fn from_data(bound: Option>) -> Self { if let Some(bound) = bound { let mut bs = Self::new(false); for b in bound { bs.push(b); } bs } else { Self::new(true) } } fn push(&mut self, bound: Bound) { match bound { Bound::Type(ty) => self.ty.push(ty), Bound::Predicate(pred) => self.pred.push(pred), Bound::Default(_) => self.can_extend = true, } } pub fn child(&mut self, bound: Option>) -> BoundsChild { let bounds = if self.can_extend { Self::from_data(bound) } else { Self::new(false) }; BoundsChild { owner: self, bounds, } } pub fn build_wheres(self, type_param_bounds: TokenStream) -> Vec { let mut pred = self.pred; for ty in self.ty { pred.push(parse_quote!(#ty : #type_param_bounds)); } pred } } pub struct BoundsChild<'a> { owner: &'a mut Bounds, bounds: Bounds, } impl<'a> Deref for BoundsChild<'a> { type Target = Bounds; fn deref(&self) -> &Self::Target { &self.bounds } } impl<'a> DerefMut for BoundsChild<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.bounds } } impl<'a> Drop for BoundsChild<'a> { fn drop(&mut self) { if self.owner.can_extend { self.owner.ty.append(&mut self.bounds.ty); self.owner.pred.append(&mut self.bounds.pred); } } } test-strategy-0.3.1/src/lib.rs000064400000000000000000000465560072674642500144170ustar 00000000000000// #![include_doc("../README.md", start("This crate provides two procedural macros, `#[derive(Arbitrary)]` and `#[proptest]`."))] //! This crate provides two procedural macros, `#[derive(Arbitrary)]` and `#[proptest]`. //! //! Each of these macros is an alternative to the following proptest's official macros. //! //! | [test-strategy][] | [proptest][] | [proptest-derive][] | //! | ------------------------------------------ | ----------------------------- | ----------------------------------- | //! | [`#[derive(Arbitrary)]`](#derivearbitrary) | | [`#[derive(Arbitrary)]`][offical-a] | //! | [`#[proptest]`](#proptest) | [`proptest ! { }`][offical-m] | | //! //! [test-strategy]: https://crates.io/crates/test-strategy //! [proptest]: https://crates.io/crates/proptest //! [proptest-derive]: https://crates.io/crates/proptest-derive //! [offical-m]: https://altsysrq.github.io/rustdoc/proptest/latest/proptest/macro.proptest.html //! [offical-a]: https://altsysrq.github.io/proptest-book/proptest-derive/modifiers.html //! //! The macros provided by this crate have the following advantages over the proptest's official macros. //! //! - Supports higher-order strategies. (`#[derive(Arbitrary)]` and `#[proptest]`) //! - Code formatting is not disabled. (`#[proptest]`) //! //! However, the syntax of this crate's macros are not compatible with the syntax of the official macros. //! //! ## Install //! //! Add this to your Cargo.toml: //! //! ```toml //! [dependencies] //! test-strategy = "0.3.1" //! proptest = "1.0.0" //! ``` //! //! ## Example //! //! You can use `#[derive(Arbitrary)]` to automatically implement proptest's `Arbitrary` trait. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInputStruct { //! x: u32, //! //! #[strategy(1..10u32)] //! y: u32, //! //! #[strategy(0..#y)] //! z: u32, //! } //! //! #[derive(Arbitrary, Debug)] //! enum TestInputEnum { //! A, //! B, //! #[weight(3)] //! C, //! X(u32), //! Y(#[strategy(0..10u32)] u32), //! } //! ``` //! //! You can define a property test by adding `#[proptest]` to the function. //! //! ```rust //! use test_strategy::proptest; //! //! #[proptest] //! fn my_test(_x: u32, #[strategy(1..10u32)] y: u32, #[strategy(0..#y)] z: u32) { //! assert!(1 <= y && y < 10); //! assert!(z <= y); //! } //! ``` //! //! ## Attributes //! //! Attributes can be written in the following positions. //! //! | attribute | function | struct | enum | variant | field | function parameter | //! | --------------------------------------------------- | -------- | ------ | ---- | ------- | ----- | ------------------ | //! | [`#[strategy]`](#strategy) | | | | | ✔ | ✔ | //! | [`#[any]`](#any) | | | | | ✔ | ✔ | //! | [`#[weight]`](#weight) | | | | ✔ | | | //! | [`#[map]`](#map) | | | | | ✔ | ✔ | //! | [`#[filter]`](#filter) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | //! | [`#[by_ref]`](#by_ref) | | | | | ✔ | ✔ | //! | [`#[arbitrary(args = T)]`](#arbitraryargs--t) | | ✔ | ✔ | | | | //! | [`#[arbitrary(bound(...))]`](#arbitraryboundt1-t2-) | | ✔ | ✔ | ✔ | ✔ | | //! | [`#[arbitrary(dump)]`](#arbitrarydump) | | ✔ | ✔ | | | | //! | [`#[proptest]`](#proptest) | ✔ | | | | | | //! | [`#[proptest(async = ...)]`](#proptestasync--) | ✔ | | | | | | //! | [`#[proptest(dump)]`](#proptestdump) | ✔ | | | | | | //! //! ## `#[derive(Arbitrary)]` //! //! You can implement `proptest::arbitrary::Arbitrary` automatically by adding `#[derive(Arbitrary)]` to struct or enum declaration. //! //! By default, all fields are set using the strategy obtained by `proptest::arbitrary::any()`. //! //! So the following two codes are equivalent. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! x: u32, //! y: u32, //! } //! ``` //! //! ```rust //! use proptest::{ //! arbitrary::{any, Arbitrary}, //! strategy::{BoxedStrategy, Strategy}, //! }; //! //! #[derive(Debug)] //! struct TestInput { //! x: u32, //! y: u32, //! } //! impl Arbitrary for TestInput { //! type Parameters = (); //! type Strategy = BoxedStrategy; //! //! fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { //! let x = any::(); //! let y = any::(); //! (x, y).prop_map(|(x, y)| Self { x, y }).boxed() //! } //! } //! ``` //! //! ## `#[strategy]` //! //! You can specify a strategy to generate values for the field by adding `#[strategy(...)]` to the field. //! //! In the following example, the value of field `x` will be less than 20. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! #[strategy(0..20u32)] //! x: u32, //! } //! ``` //! //! In `#[strategy]`, the values of other fields can be used by following `#` to the name of the field. //! //! In the following example, the value of `y` is less than or equal to `x`. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! x: u32, //! #[strategy(0..=#x)] //! y: u32, //! } //! ``` //! //! ## `#[any]` //! //! Instead of writing `#[strategy(any_with::(expr))]`, you can write `#[any(expr)]`. //! //! ```rust //! use proptest::collection::size_range; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug, PartialEq)] //! struct TestInput { //! #[any(size_range(0..16).lift())] //! x: Vec, //! } //! ``` //! //! Instead of writing an expression to be passed to `any_with`, you can write only the value of the field to be changed from the default value. //! //! Therefore, the following `TestInputA`, `TestInputB` and `TestInputC` are equivalent. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInputA { //! #[any(InnerArgs { upper : 20, ..InnerArgs::default() })] //! a: Inner, //! } //! #[derive(Arbitrary, Debug)] //! struct TestInputB { //! #[any(InnerArgs::default(), upper = 20)] //! a: Inner, //! } //! #[derive(Arbitrary, Debug)] //! struct TestInputC { //! #[any(upper = 20)] //! a: Inner, //! } //! //! #[derive(Default)] //! struct InnerArgs { //! lower: i32, //! upper: i32, //! } //! //! #[derive(Arbitrary, Debug)] //! #[arbitrary(args = InnerArgs)] //! struct Inner { //! #[strategy(args.lower..args.upper)] //! x: i32, //! } //! ``` //! //! ## `#[weight]` //! //! By default, all variants appear with equal probability. //! //! You can add `#[weight]` to the variant to change the probability of the variant appearing. //! //! In the following example, `TestInput::B` is twice as likely to appear as `TestInput::A`. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! enum TestInput { //! A, //! //! #[weight(2)] //! B, //! } //! ``` //! //! If you add `#[weight(0)]` to a variant, the variant does not appear, so you can use a type in that variant that cannot be used as `Arbitrary`. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Debug)] //! struct NotArbitrary; //! //! #[derive(Arbitrary, Debug)] //! enum TestInput { //! A, //! //! #[allow(dead_code)] //! #[weight(0)] // Removing this `#[weight(0)]` will cause a compile error. //! B(NotArbitrary), //! } //! ``` //! //! ## `#[map]` //! //! Instead of using `prop_map` in `#[strategy(...)]`, `#[map(...)]` can be used. //! //! The following codes mean the same thing. //! //! ```rust //! use proptest::arbitrary::any; //! use proptest::strategy::Strategy; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput1 { //! #[strategy(any::().prop_map(|x| x + 1))] //! x: u32, //! } //! //! #[derive(Arbitrary, Debug)] //! struct TestInput2 { //! #[strategy(any::())] //! #[map(|x| x + 1)] //! x: u32, //! } //! //! #[derive(Arbitrary, Debug)] //! struct TestInput3 { //! #[map(|x: u32| x + 1)] //! x: u32, //! } //! ``` //! //! References to other fields in the function applied to `prop_map` or `#[map(...)]` will generate different strategies. //! //! Referencing another field in `#[strategy(...)]` will expand it to `prop_flat_map`, even if it is in `prop_map`. //! //! ```rust //! use proptest::arbitrary::any; //! use proptest::strategy::{Just, Strategy}; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct T1 { //! x: u32, //! //! #[strategy(any::().prop_map(move |y| #x + y))] //! y: u32, //! } //! // The code above generates the following strategy. //! let t1 = any::() //! .prop_flat_map(|x| (Just(x), any::().prop_map(move |y| x + y))) //! .prop_map(|(x, y)| T1 { x, y }); //! ``` //! //! On the other hand, if you refer to another field in `#[map]`, it will expand to `prop_map`. //! //! ```rust //! use proptest::arbitrary::any; //! use proptest::strategy::Strategy; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct T2 { //! x: u32, //! //! #[map(|y: u32| #x + y)] //! y: u32, //! } //! // The code above generates the following strategy. //! let t2 = (any::(), any::()).prop_map(|(x, y)| T2 { x, y }); //! ``` //! //! If the input and output types of the function specified in `#[map]` are different, the value type of the strategy set in `#[strategy]` is the type of the function's input, not the type of the field. //! //! ```rust //! use proptest::arbitrary::any; //! use proptest::sample::Index; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct T1 { //! #[strategy(any::())] //! #[map(|i: Index| i.index(10))] //! x: usize, //! } //! //! // `#[strategy(any::())]` can be omitted. //! #[derive(Arbitrary, Debug)] //! struct T2 { //! #[map(|i: Index| i.index(10))] //! x: usize, //! } //! ``` //! //! ## `#[filter]` //! //! By adding `#[filter]` , you can limit the values generated. //! //! In the following examples, x is an even number. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! #[filter(#x % 2 == 0)] //! x: u32, //! } //! ``` //! //! You can also use multiple variables in a predicate. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! #[filter((#x + #y) % 2 == 0)] //! struct T1 { //! x: u32, //! y: u32, //! } //! //! #[derive(Arbitrary, Debug)] //! struct T2 { //! x: u32, //! #[filter((#x + #y) % 2 == 0)] //! y: u32, //! } //! ``` //! //! You can use the value of a structure or enum in the filter by using `#self`. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! #[filter((#self.x + #self.y) % 2 == 0)] //! struct TestInput { //! x: u32, //! y: u32, //! } //! ``` //! //! If the expression specified for `#[filter]` does not contain a variable named by appending # to its own field name, the expression is treated as a predicate function, rather than an expression that returns a bool. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! #[filter(is_even)] //! x: u32, //! } //! fn is_even(x: &u32) -> bool { //! x % 2 == 0 //! } //! //! #[derive(Arbitrary, Debug)] //! struct T2 { //! a: u32, //! //! // Since `#a` exists but `#b` does not, it is treated as a predicate function. //! #[filter(|&x| x > #a)] //! b: u32, //! } //! ``` //! //! Similarly, an expression that does not contain `#self` in the `#[filter(...)]` that it attaches to a type is treated as a predicate function. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! #[filter(is_even)] //! struct T { //! x: u32, //! } //! fn is_even(t: &T) -> bool { //! t.x % 2 == 0 //! } //! ``` //! //! You can specify a filter name by passing two arguments to `#[filter]`. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! #[filter("x is even", #x % 2 == 0)] //! x: u32, //! } //! ``` //! //! ## `#[by_ref]` //! //! By default, if you use a variable with `#[strategy]`, `#[any]`, `#[map]` or `#[filter]` with `#` attached to it, the cloned value is set. //! //! Adding `#[by_ref]` to the field makes it use the reference instead of the cloned value. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInput { //! #[by_ref] //! #[strategy(1..10u32)] //! x: u32, //! //! #[strategy(0..*#x)] //! y: u32, //! } //! ``` //! //! ## `#[arbitrary]` //! //! ### `#[arbitrary(args = T)]` //! //! Specifies the type of `Arbitrary::Parameters`. //! //! You can use the `Rc` value of this type in `#[strategy]`, `#[any]`, or `#[filter]` with the variable name `args`. //! //! ```rust //! use test_strategy::Arbitrary; //! //! #[derive(Debug, Default)] //! struct TestInputArgs { //! x_max: u32, //! } //! //! #[derive(Arbitrary, Debug)] //! #[arbitrary(args = TestInputArgs)] //! struct TestInput { //! #[strategy(0..=args.x_max)] //! x: u32, //! } //! ``` //! //! ### `#[arbitrary(bound(T1, T2, ..))]` //! //! By default, if the type of field for which `#[strategy]` is not specified contains a generic parameter, that type is set to trait bounds. //! //! Therefore, the following `TestInputA` and `TestInputB` are equivalent. //! //! ```rust //! use proptest::{ //! arbitrary::any, arbitrary::Arbitrary, strategy::BoxedStrategy, strategy::Strategy, //! }; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug)] //! struct TestInputA { //! x: T, //! } //! //! #[derive(Debug)] //! struct TestInputB { //! x: T, //! } //! impl Arbitrary for TestInputB { //! type Parameters = (); //! type Strategy = BoxedStrategy; //! //! fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { //! any::().prop_map(|x| Self { x }).boxed() //! } //! } //! ``` //! //! Types of fields with `#[strategy]` do not set trait bounds automatically, so you need to set trait bound manually with `#[arbitrary(bound(T))]`. //! //! ```rust //! use proptest::arbitrary::any_with; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug, PartialEq)] //! #[arbitrary(bound(T))] //! struct TestInput { //! #[strategy(any_with::(Default::default()))] //! x: T, //! } //! ``` //! //! You can also specify where predicate instead of type. //! //! ```rust //! use proptest::arbitrary::{any_with, Arbitrary}; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug, PartialEq)] //! #[arbitrary(bound(T : Arbitrary + 'static))] //! struct TestInput { //! #[strategy(any_with::(Default::default()))] //! x: T, //! } //! ``` //! //! `..` means automatically generated trait bounds. //! //! The following example uses a manually specified trait bounds in addition to the automatically generated trait bounds. //! //! ```rust //! use proptest::arbitrary::any_with; //! use test_strategy::Arbitrary; //! //! #[derive(Arbitrary, Debug, PartialEq)] //! #[arbitrary(bound(T1, ..))] //! struct TestInput { //! #[strategy(any_with::(Default::default()))] //! x: T1, //! //! y: T2, //! } //! ``` //! //! ### `#[arbitrary(dump)]` //! //! Causes a compile error and outputs the code generated by `#[derive(Arbitrary)]` as an error message. //! //! ## `#[proptest]` //! //! `#[proptest]` is the attribute used instead of `#[test]` when defining a property test. //! //! The following example defines a test that takes a variety of integers as input. //! //! ```rust //! use test_strategy::proptest; //! //! #[proptest] //! fn my_test(_input: i32) { //! // ... //! } //! ``` //! //! You can add `#[strategy]`, `#[any]`, `#[filter]`, `#[by_ref]` to the parameter of the function with `# [proptest]`. //! //! ```rust //! use test_strategy::proptest; //! //! #[proptest] //! fn my_test2(#[strategy(10..20)] _input: i32) { //! // ... //! } //! ``` //! //! You can change the configuration of a property test by setting the argument of `#[proptest]` attribute to a value of `proptest::prelude::ProptestConfig` type. //! //! ```rust //! use proptest::prelude::ProptestConfig; //! use test_strategy::proptest; //! //! //! #[proptest(ProptestConfig { cases : 1000, ..ProptestConfig::default() })] //! fn my_test_with_config(_input: i32) { //! // ... //! } //! ``` //! //! As with `#[any]`, you can also set only the value of the field to be changed from the default value. //! //! The example below is equivalent to the one above. //! //! ```rust //! use proptest::prelude::ProptestConfig; //! use test_strategy::proptest; //! //! #[proptest(ProptestConfig::default(), cases = 1000)] //! fn my_test_with_config_2(_input: i32) { //! // ... //! } //! //! #[proptest(cases = 1000)] //! fn my_test_with_config_3(_input: i32) { //! // ... //! } //! ``` //! //! ### `#[proptest(async = ...)]` //! //! Async functions can be tested by setting `async = ...` to the argument of `#[proptest]`. //! //! The following values are allowed after `async =`. //! The value specifies the asynchronous runtime used for the test. //! //! - "tokio" //! //! ```toml //! [dev-dependencies] //! test-strategy = "0.3" //! proptest = "1.1.0" //! tokio = { version = "1.28.1", features = ["rt-multi-thread"] } //! ``` //! //! ```rust //! use test_strategy::proptest; //! use proptest::prop_assert; //! //! #[proptest(async = "tokio")] //! async fn my_test_async() { //! async { }.await; //! prop_assert!(true); //! } //! ``` //! //! ### `#[proptest(dump)]` //! //! You can use `#[proptest(dump)]` and output the code generated by `#[proptest]` as an compile error message. //! //! ```compile_fail //! #[proptest(dump)] //! fn my_test(_input: i32) { //! // ... //! } //! ``` // #![include_doc("../README.md", end("## License"))] extern crate proc_macro; #[macro_use] mod syn_utils; mod arbitrary; mod bound; mod proptest_fn; use syn::{parse_macro_input, DeriveInput, ItemFn}; use syn_utils::into_macro_output; #[proc_macro_attribute] pub fn proptest( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let item_fn = parse_macro_input!(item as ItemFn); into_macro_output(proptest_fn::build_proptest(attr.into(), item_fn)) } #[proc_macro_derive( Arbitrary, attributes(arbitrary, strategy, any, map, filter, weight, by_ref) )] pub fn derive_arbitrary(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); into_macro_output(arbitrary::derive_arbitrary(input)) } test-strategy-0.3.1/src/proptest_fn.rs000064400000000000000000000140250072674642500161760ustar 00000000000000use crate::syn_utils::{Arg, Args}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{ parse2, parse_quote, parse_str, spanned::Spanned, token, Block, Field, FieldMutability, FnArg, Ident, ItemFn, LitStr, Pat, Result, Visibility, }; pub fn build_proptest(attr: TokenStream, mut item_fn: ItemFn) -> Result { let mut attr_args = None; if !attr.is_empty() { attr_args = Some(parse2::(attr)?); } let mut dump = false; item_fn.attrs.retain(|attr| { if attr.path().is_ident("proptest_dump") { dump = true; false } else { true } }); let (mut attr_args, config_args) = TestFnAttrArgs::from(attr_args.unwrap_or_default())?; dump |= attr_args.dump; let args_type_str = format!("_{}Args", to_camel_case(&item_fn.sig.ident.to_string())); let args_type_ident: Ident = parse_str(&args_type_str).unwrap(); let args = item_fn .sig .inputs .iter() .map(TestFnArg::from) .collect::>>()?; let args_pats = args.iter().map(|arg| arg.pat()); let block = &item_fn.block; if item_fn.sig.asyncness.is_none() { attr_args.r#async = None; } let block = if let Some(a) = attr_args.r#async { item_fn.sig.asyncness = None; a.apply(block) } else { quote!(#block) }; let block = quote! { { let #args_type_ident { #(#args_pats,)* } = input; #block } }; item_fn.sig.inputs = parse_quote! { input: #args_type_ident }; item_fn.block = Box::new(parse2(block)?); let args_fields = args.iter().map(|arg| &arg.field); let config = to_proptest_config(config_args); let ts = quote! { #[cfg(test)] #[derive(test_strategy::Arbitrary, Debug)] struct #args_type_ident { #(#args_fields,)* } #[cfg(test)] proptest::proptest! { #config #[test] #item_fn } }; if dump { panic!("{}", ts); } Ok(ts) } fn to_proptest_config(args: Args) -> TokenStream { if args.is_empty() { return quote!(); } let mut base_expr = None; let mut inits = Vec::new(); for arg in args { match arg { Arg::Value(value) => base_expr = Some(value), Arg::NameValue { name, value, .. } => inits.push(quote!(#name : #value)), } } let base_expr = base_expr.unwrap_or_else(|| { parse_quote!(::default()) }); quote! { #![proptest_config(proptest::test_runner::Config { #(#inits,)* .. #base_expr })] } } struct TestFnArg { field: Field, mutability: Option, } impl TestFnArg { fn from(arg: &FnArg) -> Result { if let FnArg::Typed(arg) = arg { if let Pat::Ident(ident) = arg.pat.as_ref() { if ident.attrs.is_empty() && ident.by_ref.is_none() && ident.subpat.is_none() { return Ok(Self { field: Field { attrs: arg.attrs.clone(), vis: Visibility::Inherited, mutability: FieldMutability::None, ident: Some(ident.ident.clone()), colon_token: Some(arg.colon_token), ty: arg.ty.as_ref().clone(), }, mutability: ident.mutability, }); } } else { bail!(arg.pat.span(), "argument pattern not supported."); } } bail!( arg.span(), "argument {} is not supported.", arg.to_token_stream() ); } fn pat(&self) -> TokenStream { let mutability = &self.mutability; let ident = &self.field.ident; quote!(#mutability #ident) } } #[derive(Debug, Clone, Copy)] enum Async { Tokio, } impl Async { fn apply(&self, block: &Block) -> TokenStream { match self { Async::Tokio => { quote! { let ret: ::core::result::Result<_, proptest::test_runner::TestCaseError> = tokio::runtime::Runtime::new() .unwrap() .block_on(async move { #block Ok(()) }); ret?; } } } } } impl syn::parse::Parse for Async { fn parse(input: syn::parse::ParseStream) -> Result { let s: LitStr = input.parse()?; match s.value().as_str() { "tokio" => Ok(Async::Tokio), _ => bail!(s.span(), "expected `tokio`."), } } } struct TestFnAttrArgs { r#async: Option, dump: bool, } impl TestFnAttrArgs { fn from(args: Args) -> Result<(Self, Args)> { let mut config_args = Args::new(); let mut this = TestFnAttrArgs { r#async: None, dump: false, }; for arg in args { if let Arg::NameValue { name, value, .. } = &arg { if name == "async" { this.r#async = Some(parse2(value.to_token_stream())?); continue; } } if let Arg::Value(value) = &arg { if value == &parse_quote!(dump) { this.dump = true; continue; } } config_args.0.push(arg); } Ok((this, config_args)) } } fn to_camel_case(s: &str) -> String { let mut upper = true; let mut r = String::new(); for c in s.chars() { if c == '_' { upper = true; } else if upper { r.push_str(&c.to_uppercase().to_string()); upper = false; } else { r.push(c); } } r } test-strategy-0.3.1/src/syn_utils.rs000064400000000000000000000256410072674642500156720ustar 00000000000000use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree}; use quote::{quote, ToTokens}; use std::{ collections::{BTreeMap, HashSet}, iter::once, ops::Deref, }; use structmeta::{Parse, ToTokens}; use syn::{ ext::IdentExt, parenthesized, parse::{Parse, ParseStream}, parse2, parse_str, punctuated::Punctuated, spanned::Spanned, token::{Comma, Paren}, visit::{visit_path, visit_type, Visit}, Attribute, DeriveInput, Expr, Field, GenericParam, Generics, Ident, Lit, Meta, Path, Result, Token, Type, WherePredicate, }; macro_rules! bail { ($span:expr, $message:literal $(,)?) => { return std::result::Result::Err(syn::Error::new($span, $message)) }; ($span:expr, $err:expr $(,)?) => { return std::result::Result::Err(syn::Error::new($span, $err)) }; ($span:expr, $fmt:expr, $($arg:tt)*) => { return std::result::Result::Err(syn::Error::new($span, std::format!($fmt, $($arg)*))) }; } pub fn into_macro_output(input: Result) -> proc_macro::TokenStream { match input { Ok(s) => s, Err(e) => e.to_compile_error(), } .into() } pub struct Parenthesized { pub paren_token: Option, pub content: T, } impl Parse for Parenthesized { fn parse(input: ParseStream) -> Result { let content; let paren_token = Some(parenthesized!(content in input)); let content = content.parse()?; Ok(Self { paren_token, content, }) } } impl Deref for Parenthesized { type Target = T; fn deref(&self) -> &Self::Target { &self.content } } #[derive(Parse)] pub struct Args(#[parse(terminated)] pub Punctuated); impl Args { pub fn new() -> Self { Self(Punctuated::new()) } pub fn expect_single_value(&self, span: Span) -> Result<&Expr> { if self.len() != 1 { bail!( span, "expect 1 arguments, but supplied {} arguments.", self.len() ); } match &self[0] { Arg::Value(expr) => Ok(expr), Arg::NameValue { .. } => bail!(span, "expected unnamed argument."), } } } impl Default for Args { fn default() -> Self { Self::new() } } impl Deref for Args { type Target = Punctuated; fn deref(&self) -> &Self::Target { &self.0 } } impl IntoIterator for Args { type Item = Arg; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } #[derive(ToTokens, Parse)] pub enum Arg { NameValue { #[parse(peek, any)] name: Ident, #[parse(peek)] eq_token: Token![=], value: Expr, }, Value(Expr), } pub struct SharpVals { allow_vals: bool, allow_self: bool, pub vals: BTreeMap, pub self_span: Option, } impl SharpVals { pub fn new(allow_vals: bool, allow_self: bool) -> Self { Self { allow_vals, allow_self, vals: BTreeMap::new(), self_span: None, } } pub fn expand(&mut self, input: TokenStream) -> Result { let mut tokens = Vec::new(); let mut iter = input.into_iter().peekable(); while let Some(t) = iter.next() { match &t { TokenTree::Group(g) => { tokens.push(TokenTree::Group(Group::new( g.delimiter(), self.expand(g.stream())?, ))); continue; } TokenTree::Punct(p) => { if p.as_char() == '#' && p.spacing() == Spacing::Alone { if let Some(token) = iter.peek() { if let Some(key) = FieldKey::try_from_token(token) { let span = token.span(); let allow = if &key == "self" { self.self_span.get_or_insert(span); self.allow_self } else { self.vals.entry(key.clone()).or_insert(span); self.allow_vals }; if !allow { bail!(span, "cannot use `#{}` in this position.", key); } if self.self_span.is_some() { if let Some(key) = self.vals.keys().next() { bail!(span, "cannot use both `#self` and `#{}`", key); } } let mut ident = key.to_dummy_ident(); ident.set_span(span); tokens.extend(ident.to_token_stream()); iter.next(); continue; } } } } _ => {} } tokens.extend(once(t)); } Ok(tokens.into_iter().collect()) } pub fn expand_args(&mut self, meta: &Meta) -> Result { match meta { Meta::List(m) => parse2(self.expand(m.tokens.clone())?), Meta::Path(_) | Meta::NameValue(_) => { let span = span_in_args(meta); bail!(span, "expected arguments.") } } } pub fn expand_args_or_default(&mut self, meta: &Meta) -> Result { match meta { Meta::List(m) => parse2(self.expand(m.tokens.clone())?), Meta::Path(_) => Ok(T::default()), Meta::NameValue(m) => bail!( m.eq_token.span(), "`name = value` style attribute was not supported." ), } } } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub enum FieldKey { Named(String), Unnamed(usize), } impl FieldKey { pub fn from_ident(ident: &Ident) -> Self { Self::Named(ident.unraw().to_string()) } pub fn from_field(idx: usize, field: &Field) -> Self { if let Some(ident) = &field.ident { Self::from_ident(ident) } else { Self::Unnamed(idx) } } pub fn try_from_token(token: &TokenTree) -> Option { match token { TokenTree::Ident(ident) => Some(Self::from_ident(ident)), TokenTree::Literal(token) => { if let Lit::Int(lit) = Lit::new(token.clone()) { if lit.suffix().is_empty() { if let Ok(idx) = lit.base10_parse() { return Some(Self::Unnamed(idx)); } } } None } _ => None, } } pub fn to_dummy_ident(&self) -> Ident { Ident::new(&format!("_{self}"), Span::call_site()) } pub fn to_valid_ident(&self) -> Option { match self { Self::Named(name) => to_valid_ident(name).ok(), Self::Unnamed(..) => None, } } } impl PartialEq for FieldKey { fn eq(&self, other: &str) -> bool { match self { FieldKey::Named(name) => name == other, _ => false, } } } impl std::fmt::Display for FieldKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Named(name) => name.fmt(f), Self::Unnamed(idx) => idx.fmt(f), } } } pub struct GenericParamSet { idents: HashSet, } impl GenericParamSet { pub fn new(generics: &Generics) -> Self { let mut idents = HashSet::new(); for p in &generics.params { match p { GenericParam::Type(t) => { idents.insert(t.ident.unraw()); } GenericParam::Const(t) => { idents.insert(t.ident.unraw()); } _ => {} } } Self { idents } } fn contains(&self, ident: &Ident) -> bool { self.idents.contains(&ident.unraw()) } pub fn contains_in_type(&self, ty: &Type) -> bool { struct Visitor<'a> { generics: &'a GenericParamSet, result: bool, } impl<'a, 'ast> Visit<'ast> for Visitor<'a> { fn visit_path(&mut self, i: &'ast syn::Path) { if i.leading_colon.is_none() { if let Some(s) = i.segments.iter().next() { if self.generics.contains(&s.ident) { self.result = true; } } } visit_path(self, i); } } let mut visitor = Visitor { generics: self, result: false, }; visit_type(&mut visitor, ty); visitor.result } } pub fn impl_trait( input: &DeriveInput, trait_path: &Path, wheres: &[WherePredicate], contents: TokenStream, ) -> TokenStream { let ty = &input.ident; let (impl_g, ty_g, where_clause) = input.generics.split_for_impl(); let mut wheres = wheres.to_vec(); if let Some(where_clause) = where_clause { wheres.extend(where_clause.predicates.iter().cloned()); } let where_clause = if wheres.is_empty() { quote! {} } else { quote! { where #(#wheres,)*} }; quote! { #[automatically_derived] impl #impl_g #trait_path for #ty #ty_g #where_clause { #contents } } } pub fn impl_trait_result( input: &DeriveInput, trait_path: &Path, wheres: &[WherePredicate], contents: TokenStream, dump: bool, ) -> Result { let ts = impl_trait(input, trait_path, wheres, contents); if dump { panic!("macro result: \n{ts}"); } Ok(ts) } pub fn to_valid_ident(s: &str) -> Result { if let Ok(ident) = parse_str(s) { Ok(ident) } else { parse_str(&format!("r#{s}")) } } pub fn parse_from_attrs(attrs: &[Attribute], name: &str) -> Result { let mut a = None; for attr in attrs { if attr.path().is_ident(name) { if a.is_some() { bail!(attr.span(), "attribute `{}` can specified only once", name); } a = Some(attr); } } if let Some(a) = a { a.parse_args() } else { Ok(T::default()) } } pub fn span_in_args(meta: &Meta) -> Span { match meta { Meta::Path(_) => meta.span(), Meta::List(m) => m.delimiter.span().span(), Meta::NameValue(m) => m.value.span(), } } test-strategy-0.3.1/tests/arbitrary.rs000064400000000000000000001046030072674642500162070ustar 00000000000000#![allow(clippy::derive_partial_eq_without_eq)] mod test_helpers; use proptest::{ arbitrary::{any, any_with, Arbitrary}, collection::size_range, prop_oneof, strategy::{Just, LazyJust, Strategy}, }; use std::fmt::Debug; use test_helpers::*; use test_strategy::Arbitrary; #[test] fn unit_struct() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct; assert_arbitrary(LazyJust::new(|| TestStruct)); } #[test] fn tuple_struct_no_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(); assert_arbitrary(LazyJust::new(TestStruct)); } #[test] fn struct_no_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct {} assert_arbitrary(LazyJust::new(|| TestStruct {})); } #[test] fn struct_no_attr_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { x: u16, } assert_arbitrary(any::().prop_map(|x| TestStruct { x })); } #[test] fn struct_no_attr_field_x2() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { x: u16, y: u8, } assert_arbitrary((any::(), any::()).prop_map(|(x, y)| TestStruct { x, y })); } #[test] fn struct_field_raw_ident() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { r#x: u16, } assert_arbitrary(any::().prop_map(|x| TestStruct { x })); } #[test] fn struct_field_raw_keyword_ident() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { r#fn: u16, } assert_arbitrary(any::().prop_map(|r#fn| TestStruct { r#fn })); } #[test] fn struct_over_12_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { a0: u32, a1: u32, a2: u32, a3: u32, a4: u32, a5: u32, a6: u32, a7: u32, a8: u32, a9: u32, a10: u32, a11: u32, a12: u32, } } #[test] fn tuple_struct_no_attr_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(u16); assert_arbitrary(any::().prop_map(TestStruct)); } #[test] fn tuple_struct_no_attr_field_x2() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(u16, u8); assert_arbitrary((any::(), any::()).prop_map(|(x, y)| TestStruct(x, y))); } #[test] fn strategy_struct() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..16u16)] x: u16, } assert_arbitrary((1..16u16).prop_map(|x| TestStruct { x })); } #[test] fn strategy_struct_field_x2() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..16u16)] x: u16, #[strategy(10..20u8)] y: u8, } assert_arbitrary((1..16u16, 10..20u8).prop_map(|(x, y)| TestStruct { x, y })); } #[test] fn strategy_rank2() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] x: u32, #[strategy(0..#x)] y: u32, } assert_arbitrary( (1..10u32) .prop_flat_map(|x| (0..x, Just(x))) .prop_map(|(y, x)| TestStruct { x, y }), ); } #[test] fn strategy_rank3() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] x: u32, #[strategy(0..#x)] y: u32, #[strategy(0..#y*2+1)] z: u32, } assert_arbitrary( (1..10u32) .prop_flat_map(|x| (0..x, Just(x))) .prop_flat_map(|(y, x)| (0..y * 2 + 1, Just(x), Just(y))) .prop_map(|(z, x, y)| TestStruct { x, y, z }), ); } #[test] fn strategy_underline() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..16u16)] _x: u16, } } #[test] fn strategy_rev() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(0..#x)] y: u32, #[strategy(1..10u32)] x: u32, } assert_arbitrary( (1..10u32) .prop_flat_map(|x| (0..x, Just(x))) .prop_map(|(y, x)| TestStruct { x, y }), ); } #[test] fn strategy_tuple_struct() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(#[strategy(1..10u32)] u32, #[strategy(0..#0)] u32); assert_arbitrary( (1..10u32) .prop_flat_map(|x| (0..x, Just(x))) .prop_map(|(y, x)| TestStruct(x, y)), ); } #[test] fn strategy_multiple_dependency() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(0..10)] x: i32, #[strategy(0..20)] y: i32, #[strategy(0..#x*#y+1)] z: i32, } assert_arbitrary( (0..10, 0..20) .prop_flat_map(|(x, y)| (0..x * y + 1, Just(x), Just(y))) .prop_map(|(z, x, y)| TestStruct { x, y, z }), ); } #[test] fn strategy_cross_dependency() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(0..10)] a: i32, #[strategy(0..20)] b: i32, #[strategy(0..30)] c: i32, #[strategy(0..#a * #b + 1)] x: i32, #[strategy(0..#b + #c + 10)] y: i32, } assert_arbitrary( (0..10, 0..20, 0..30) .prop_flat_map(|(a, b, c)| (0..a * b + 1, 0..b + c + 10, Just(a), Just(b), Just(c))) .prop_map(|(x, y, a, b, c)| TestStruct { a, b, c, x, y }), ); } #[test] fn strategy_by_ref() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[by_ref] #[strategy(1..10u32)] x: u32, #[strategy(0..*#x)] y: u32, } assert_arbitrary( (1..10u32) .prop_flat_map(|x| (0..x, Just(x))) .prop_map(|(y, x)| TestStruct { x, y }), ); } #[test] fn strategy_raw_keyword_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] r#fn: u32, } assert_arbitrary((1..10u32).prop_map(|r#fn| TestStruct { r#fn })); } #[test] fn strategy_raw_keyword_sharp_val() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] r#fn: u32, #[strategy(0..#fn)] y: u32, } assert_arbitrary( (1..10u32) .prop_flat_map(|x| (0..x, Just(x))) .prop_map(|(y, r#fn)| TestStruct { r#fn, y }), ); } #[test] fn any_struct_field() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[any] x: u16, } assert_arbitrary(any::().prop_map(|x| TestStruct { x })); } #[test] fn any_with_args_expr() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[any(size_range(0..16).lift())] x: Vec, } assert_arbitrary(any_with::>(size_range(0..16).lift()).prop_map(|x| TestStruct { x })); } mod sub_mod { #[allow(dead_code)] #[derive(Default)] pub struct TestArgsNoConstruct { a: u32, } impl TestArgsNoConstruct { pub fn new() -> Self { Self { a: 0 } } } } #[test] fn any_with_args_expr_private_constructor() { #[derive(Arbitrary, Debug)] #[arbitrary(args = sub_mod::TestArgsNoConstruct)] struct Inner { #[allow(dead_code)] x: u32, } #[derive(Arbitrary, Debug)] struct TestInput { #[any(sub_mod::TestArgsNoConstruct::new())] #[allow(dead_code)] inner: Inner, } } #[test] fn any_with_args_struct_field() { #[derive(Default)] struct TestArgs { a_max: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct Inner { #[strategy(0..args.a_max)] a: i32, } #[derive(Arbitrary, Debug, PartialEq)] struct Outer { #[any(a_max = 50)] inner: Inner, } assert_arbitrary((0..50).prop_map(|a| Outer { inner: Inner { a } })); } #[test] fn any_with_args_enum_field() { #[derive(Default)] struct TestArgs { a_max: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] enum Inner { A { #[strategy(0..args.a_max)] a: i32, }, } #[derive(Arbitrary, Debug, PartialEq)] struct Outer { #[any(a_max = 50)] inner: Inner, } assert_arbitrary((0..50).prop_map(|a| Outer { inner: Inner::A { a }, })); } #[test] fn any_with_args_field_x2() { #[derive(Default)] struct TestArgs { a_max1: i32, a_max2: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct Inner { #[strategy(0..args.a_max1 + args.a_max2)] a: i32, } #[derive(Arbitrary, Debug, PartialEq)] struct Outer { #[any(a_max1 = 50, a_max2 = 10)] inner: Inner, } assert_arbitrary((0..60).prop_map(|a| Outer { inner: Inner { a } })); } #[test] fn any_with_args_expr_field() { #[derive(Default)] struct TestArgs { a_max1: i32, a_max2: i32, } impl TestArgs { fn new() -> Self { Self { a_max1: 10, a_max2: 20, } } } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct Inner { #[strategy(0..args.a_max1 + args.a_max2)] a: i32, } #[derive(Arbitrary, Debug, PartialEq)] struct Outer { #[any(TestArgs::new(), a_max2 = 30)] inner: Inner, } assert_arbitrary((0..40).prop_map(|a| Outer { inner: Inner { a } })); } #[test] fn any_with_keyword() { #[derive(Default)] struct TestArgs { r#fn: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct Inner { #[strategy(0..args.r#fn)] a: i32, } #[derive(Arbitrary, Debug, PartialEq)] struct Outer { #[any(fn = 50)] inner: Inner, } assert_arbitrary((0..50).prop_map(|a| Outer { inner: Inner { a } })); } #[test] fn enum_unit_variant_1() { #[derive(Arbitrary, Debug, PartialEq)] enum TestEnum { X, } assert_arbitrary(LazyJust::new(|| TestEnum::X)); } #[test] fn enum_unit_variant_2() { #[derive(Arbitrary, Debug, PartialEq, Clone)] enum TestEnum { X, Y, } assert_arbitrary(prop_oneof![Just(TestEnum::X), Just(TestEnum::Y)]); } #[test] fn enum_weight() { #[derive(Arbitrary, Debug, PartialEq, Clone)] enum TestEnum { #[weight(1)] X, #[weight(2)] Y, } assert_arbitrary(prop_oneof![1=>Just(TestEnum::X), 2=>Just(TestEnum::Y)]); } #[test] fn enum_weight_0() { #[derive(Debug, PartialEq, Clone)] struct NotArbitrary; #[allow(dead_code)] #[derive(Arbitrary, Debug, PartialEq, Clone)] enum TestEnum { X, #[weight(0)] Y(NotArbitrary), } assert_arbitrary(Just(TestEnum::X)); } #[test] fn filter_struct_field_fn() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] #[filter(is_even)] x: u32, } fn is_even(value: &u32) -> bool { value % 2 == 0 } assert_arbitrary( (1..10u32) .prop_filter("is_even", is_even) .prop_map(|x| TestStruct { x }), ); } #[test] fn filter_struct_field_fn_dependency() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { a: u32, #[filter(is_larger_than(#a))] b: u32, } fn is_larger_than(t: u32) -> impl Fn(&u32) -> bool { move |&value| value > t } assert_arbitrary( (any::(), any::()) .prop_filter("is_larger_than(a)", |&(a, b)| is_larger_than(a)(&b)) .prop_map(|(a, b)| TestStruct { a, b }), ); } #[test] fn filter_struct_field_fn_dependency_closure() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { a: u32, #[filter(|&b| b > #a)] b: u32, } assert_arbitrary( (any::(), any::()) .prop_filter("b > a", |&(a, b)| b > a) .prop_map(|(a, b)| TestStruct { a, b }), ); } #[test] fn filter_struct_field_sharp_val() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] #[filter(is_even(&#x))] x: u32, } fn is_even(value: &u32) -> bool { value % 2 == 0 } assert_arbitrary( (1..10u32) .prop_filter("is_even", is_even) .prop_map(|x| TestStruct { x }), ); } #[test] fn filter_tuple_struct_field_sharp_val() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct( #[strategy(1..10u32)] #[filter(is_even(�))] u32, ); fn is_even(value: &u32) -> bool { value % 2 == 0 } assert_arbitrary( (1..10u32) .prop_filter("is_even", is_even) .prop_map(TestStruct), ); } #[test] fn filter_struct_field_closure() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] #[filter(|val| val % 2 == 0)] x: u32, } assert_arbitrary( (1..10u32) .prop_filter("is_even", |val| val % 2 == 0) .prop_map(|x| TestStruct { x }), ); } #[test] fn filter_struct_sharp_vals() { #[derive(Arbitrary, Debug, PartialEq)] #[filter(#x > #y)] struct TestStruct { x: u32, y: u32, } assert_arbitrary( (any::(), any::()) .prop_filter("x > y", |&(x, y)| x > y) .prop_map(|(x, y)| TestStruct { x, y }), ); } #[test] fn filter_struct_sharp_self() { #[derive(Arbitrary, Debug, PartialEq)] #[filter(#self.x > #self.y)] struct TestStruct { x: u32, y: u32, } assert_arbitrary( (any::(), any::()) .prop_filter("x > y", |&(x, y)| x > y) .prop_map(|(x, y)| TestStruct { x, y }), ); } #[test] fn filter_struct_fn() { #[derive(Arbitrary, Debug, PartialEq)] #[filter(|s| s.x > s.y)] struct TestStruct { x: u32, y: u32, } assert_arbitrary( (any::(), any::()) .prop_filter("x > y", |&(x, y)| x > y) .prop_map(|(x, y)| TestStruct { x, y }), ); } #[test] fn filter_enum_fn() { #[derive(Arbitrary, Debug, PartialEq, Clone)] #[filter(Self::is_valid)] enum TestEnum { A(i32), B(i32), } impl TestEnum { fn is_valid(&self) -> bool { match self { Self::A(x) => x % 2 == 0, Self::B(x) => x % 2 != 0, } } } assert_arbitrary( prop_oneof![ any::().prop_map(TestEnum::A), any::().prop_map(TestEnum::B), ] .prop_filter("is_valid", TestEnum::is_valid), ); } #[test] fn filter_enum_sharp_self() { #[derive(Arbitrary, Debug, PartialEq, Clone)] #[filter(#self.is_valid())] enum TestEnum { A(i32), B(i32), } impl TestEnum { fn is_valid(&self) -> bool { match self { Self::A(x) => x % 2 == 0, Self::B(x) => x % 2 != 0, } } } assert_arbitrary( prop_oneof![ any::().prop_map(TestEnum::A), any::().prop_map(TestEnum::B), ] .prop_filter("is_valid", TestEnum::is_valid), ); } #[test] fn filter_variant() { #[derive(Arbitrary, Debug, PartialEq, Clone)] enum TestEnum { #[filter(#x > #y)] A { x: i32, y: i32, }, B(i32), } assert_arbitrary(prop_oneof![ (any::(), any::()) .prop_filter("x > y", |&(x, y)| x > y) .prop_map(|(x, y)| TestEnum::A { x, y }), any::().prop_map(TestEnum::B), ]); } #[test] fn filter_raw_keyword() { #[derive(Arbitrary, Debug, PartialEq)] #[filter(#fn > #y)] struct TestStruct { r#fn: u32, y: u32, } assert_arbitrary( (any::(), any::()) .prop_filter("fn > y", |&(r#fn, y)| r#fn > y) .prop_map(|(r#fn, y)| TestStruct { r#fn, y }), ); } #[test] fn filter_with_whence() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] #[filter("is_even", is_even)] x: u32, } fn is_even(value: &u32) -> bool { value % 2 == 0 } assert_arbitrary( (1..10u32) .prop_filter("is_even", is_even) .prop_map(|x| TestStruct { x }), ); } #[test] fn filter_with_by_ref() { #[derive(Arbitrary, Debug, PartialEq)] #[filter(#x > *#y)] struct TestStruct { x: u32, #[by_ref] y: u32, } assert_arbitrary( (any::(), any::()) .prop_filter("x > y", |&(x, y)| x > y) .prop_map(|(x, y)| TestStruct { x, y }), ); } #[test] fn args_struct() { #[derive(Default)] struct TestArgs { x_max: u32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[strategy(0..args.x_max)] x: u32, } } #[test] fn args_with_strategy_sharp_val() { #[derive(Default)] struct TestArgs { y: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[strategy(0..10)] x: i32, #[strategy(0..#x + args.y)] y: i32, } } #[test] fn args_with_strategy_sharp_val_x2() { #[derive(Default)] struct TestArgs { a: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[strategy(0..10)] x: i32, #[strategy(0..#x + args.a)] y: i32, #[strategy(0..#y + args.a)] z: i32, } } #[test] fn args_with_struct_filter_sharp_val() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(#x % args.m != 0)] struct TestStruct { x: i32, } } #[test] fn args_with_struct_filter_sharp_val_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(#x % args.m != 0)] #[filter(#x % args.m != 1)] struct TestStruct { x: i32, } } #[test] fn args_with_struct_filter_sharp_self() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(#self.x % args.m != 0)] struct TestStruct { x: i32, } } #[test] fn args_with_struct_filter_sharp_self_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(#self.x % args.m != 0)] #[filter(#self.x % args.m != 1)] struct TestStruct { x: i32, } } #[test] fn args_with_struct_filter_fn() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(is_valid_fn(args.m))] struct TestStruct { x: i32, } fn is_valid_fn(_: i32) -> impl Fn(&TestStruct) -> bool { |_| true } } #[test] fn args_with_struct_filter_fn_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(is_valid_fn(args.m))] #[filter(is_valid_fn(args.m + 1))] struct TestStruct { x: i32, } fn is_valid_fn(_: i32) -> impl Fn(&TestStruct) -> bool { |_| true } } #[test] fn args_with_enum_filter_sharp_val() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(#self.is_valid(args.m))] enum TestEnum { A { x: i32 }, B, } impl TestEnum { fn is_valid(&self, m: i32) -> bool { match self { Self::A { x } => x % m != 0, Self::B => true, } } } } #[test] fn args_with_enum_filter_sharp_val_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] #[filter(#self.is_valid(args.m))] #[filter(#self.is_valid(args.m + 1))] enum TestEnum { A { x: i32 }, B, } impl TestEnum { fn is_valid(&self, m: i32) -> bool { match self { Self::A { x } => x % m != 0, Self::B => true, } } } } #[test] fn args_with_variant_filter_sharp_val() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] enum TestEnum { #[filter(#x % args.m != 0)] A { x: i32, }, B, } } #[test] fn args_with_variant_filter_sharp_val_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] enum TestEnum { #[filter(#x % args.m != 0)] #[filter(#x % args.m != 1)] A { x: i32, }, B, } } #[test] fn args_with_field_filter_sharp_val() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[filter(#x % args.m != 0)] x: i32, } } #[test] fn args_with_field_filter_sharp_val_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[filter(#x % args.m != 0)] #[filter(#x % args.m != 1)] x: i32, } } #[test] fn args_with_field_filter_fn() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[filter(is_larger_than(args.m))] x: i32, } fn is_larger_than(t: i32) -> impl Fn(&i32) -> bool { move |x: &i32| *x > t } } #[test] fn args_with_field_filter_fn_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[filter(is_larger_than(args.m))] #[filter(is_larger_than(args.m + 1))] x: i32, } fn is_larger_than(t: i32) -> impl Fn(&i32) -> bool { move |x: &i32| *x > t } } #[test] fn args_map() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[map(|x: i32| x + args.m)] x: i32, } } #[test] fn args_map_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[map(|x: i32| x + args.m)] x: i32, #[map(|y: i32| y + args.m)] y: i32, } } #[test] fn args_map_filter_val() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[map(|x: i32| x + args.m)] #[filter(#x > args.m)] x: i32, } } #[test] fn args_map_filter_val_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[map(|x: i32| x + args.m)] #[filter(#x > args.m)] #[filter(#x > args.m + 1)] x: i32, } } #[test] fn args_map_filter_fn_val() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[map(|x: i32| x + args.m)] #[filter(|&x: &i32| x > args.m)] x: i32, } } #[test] fn args_map_filter_fn_val_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[map(|x: i32| x + args.m)] #[filter(|&x: &i32| x > args.m)] #[filter(|&x: &i32| x > args.m + 1)] x: i32, } } #[test] fn args_flat_map_x2() { #[derive(Default)] struct TestArgs { m: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { a0: i32, a1: i32, #[strategy(#a0..#a1 + args.m)] b0: i32, #[strategy(#a0..#a1 + args.m)] b1: i32, } } #[test] fn auto_bound_tuple_struct() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(T); assert_arbitrary(any::().prop_map(TestStruct)); assert_arbitrary(any::().prop_map(TestStruct)); } #[test] fn auto_bound_tuple_enum() { #[derive(Arbitrary, Debug, PartialEq)] enum TestEnum { A(T), } assert_arbitrary(any::().prop_map(TestEnum::A)); assert_arbitrary(any::().prop_map(TestEnum::A)); } #[test] fn auto_bound_any_attribute() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(#[any(std::default::Default::default())] T); assert_arbitrary(any::().prop_map(TestStruct)); assert_arbitrary(any::().prop_map(TestStruct)); } #[test] fn auto_bound_x2() { #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct(T1, T2); assert_arbitrary((any::(), any::()).prop_map(|(x, y)| TestStruct(x, y))); } #[test] fn auto_bound_map_input() { #[derive(Debug, PartialEq, Clone)] struct Wrapped(T); #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T: Clone, ..))] struct TestStruct { #[by_ref] #[map(|t: T| Wrapped(t))] t: Wrapped, } } #[test] fn manual_bound_type() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T))] struct TestStruct { #[strategy(any::())] x: T, } assert_arbitrary(any::().prop_map(|x| TestStruct { x })); assert_arbitrary(any::().prop_map(|x| TestStruct { x })); } #[test] fn manual_bound_type_x2() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T1, T2))] struct TestStruct { #[strategy(any::())] x: T1, #[strategy(any::())] y: T2, } assert_arbitrary((any::(), any::()).prop_map(|(x, y)| TestStruct { x, y })); } #[test] fn manual_bound_type_with_auto_bound() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T1, ..))] struct TestStruct { #[strategy(any::())] x: T1, y: T2, } assert_arbitrary((any::(), any::()).prop_map(|(x, y)| TestStruct { x, y })); } #[test] fn manual_bound_predicate() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T : Arbitrary + 'static))] struct TestStruct { #[strategy(any::())] x: T, } assert_arbitrary(any::().prop_map(|x| TestStruct { x })); assert_arbitrary(any::().prop_map(|x| TestStruct { x })); } #[test] fn manual_bound_predicate_x2() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T1 : Arbitrary + 'static, T2 : Arbitrary + 'static))] struct TestStruct { #[strategy(any::())] x: T1, #[strategy(any::())] y: T2, } assert_arbitrary((any::(), any::()).prop_map(|(x, y)| TestStruct { x, y })); } #[test] fn manual_bound_field() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T : Copy + Arbitrary + 'static))] struct Inner(T); #[derive(Arbitrary, Debug, PartialEq)] pub struct Outer { #[arbitrary(bound(T : Debug + Copy + Arbitrary + 'static))] x: Inner, } assert_arbitrary(any::().prop_map(|x| Outer { x: Inner(x) })); } #[test] fn manual_bound_variant() { #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T : Copy + Arbitrary + 'static))] pub struct Inner(T); #[derive(Arbitrary, Debug, PartialEq)] pub enum Outer { #[arbitrary(bound(T : Debug + Copy + Arbitrary + 'static))] X(Inner), } assert_arbitrary(any::().prop_map(|x| Outer::X(Inner(x)))); } #[test] fn enum_with_variant_named_parameters() { #[deny(warnings)] #[derive(Arbitrary, Debug, PartialEq, Clone)] enum MyEnum { Parameters, SomethingElse, } } #[test] fn map() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[map(|x: u32| x + 1)] x: u32, } assert_arbitrary(any::().prop_map(|x| X { x: x + 1 })); } #[test] fn map_dependency() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { a: u32, #[map(|x: u32| x ^ #a)] b: u32, } assert_arbitrary((any::(), any::()).prop_map(|x| X { a: x.0, b: x.0 ^ x.1, })); } #[test] fn map_dependency_by_ref() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[by_ref] a: u32, #[map(|x: u32| x ^ *#a)] b: u32, } assert_arbitrary((any::(), any::()).prop_map(|x| X { a: x.0, b: x.0 ^ x.1, })); } #[test] fn map_filter() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[map(|x: u32| x + 1)] #[filter(#x % 2 == 0)] x: u32, } assert_arbitrary( any::() .prop_map(|x| x + 1) .prop_filter("", |x| x % 2 == 0) .prop_map(|x| X { x }), ); } #[test] fn map_strategy() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[strategy(1..10u32)] #[map(|x: u32| x * 2)] x: u32, } assert_arbitrary((1..10u32).prop_map(|x| X { x: x * 2 })); } #[test] fn map_strategy_infer() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[strategy(1..10u32)] #[map(|x| x * 2)] x: u32, } assert_arbitrary((1..10u32).prop_map(|x| X { x: x * 2 })); } #[test] fn map_strategy_any() { fn x2(x: u32) -> u32 { x * 2 } #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[map(x2)] x: u32, } assert_arbitrary((any::()).prop_map(|x| X { x: x * 2 })); } #[test] fn map_strategy_any_with() { fn x2(x: A) -> u32 { x.0 * 2 } #[derive(Arbitrary, Debug, PartialEq, Clone)] #[arbitrary(args = u32)] struct A(#[strategy(0..*args)] u32); #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[any(10)] #[map(x2)] x: u32, } assert_arbitrary((0..10u32).prop_map(|x| X { x: x * 2 })); } #[test] fn map_strategy_any_with_setter() { fn x2(x: A) -> u32 { x.0 * 2 } #[derive(Default)] struct Arg { val: u32, } #[derive(Arbitrary, Debug, PartialEq, Clone)] #[arbitrary(args = Arg)] struct A(#[strategy(0..args.val)] u32); #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[any(val = 10)] #[map(x2)] x: u32, } assert_arbitrary((0..10u32).prop_map(|x| X { x: x * 2 })); } #[test] fn map_strategy_any_dependency() { fn xor(arg: u32) -> impl Fn(u32) -> u32 { move |x| x ^ arg } #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { a: u32, #[map(xor(#a))] b: u32, } assert_arbitrary((any::(), any::()).prop_map(|x| X { a: x.0, b: x.0 ^ x.1, })); } #[test] fn map_strategy_any_dependency_by_ref() { fn xor(arg: u32) -> impl Fn(u32) -> u32 { move |x| x ^ arg } #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[by_ref] a: u32, #[map(xor(*#a))] b: u32, } assert_arbitrary((any::(), any::()).prop_map(|x| X { a: x.0, b: x.0 ^ x.1, })); } #[test] fn map_with_by_ref() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[by_ref] #[map(|x: u32| x + 1)] x: u32, } assert_arbitrary(any::().prop_map(|x| X { x: x + 1 })); } #[test] fn map_with_by_ref_and_dependency() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { x: u32, #[by_ref] #[map(|y: u32| #x ^ y)] y: u32, } assert_arbitrary((any::(), any::()).prop_map(|(x, y)| X { x, y: x ^ y })); } #[test] fn map_7_field() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[map(|x: u32| x + 1)] a1: u32, #[map(|x: u32| x + 2)] a2: u32, #[map(|x: u32| x + 3)] a3: u32, #[map(|x: u32| x + 4)] a4: u32, #[map(|x: u32| x + 5)] a5: u32, #[map(|x: u32| x + 6)] a6: u32, #[map(|x: u32| x + 7)] a7: u32, } } #[test] fn depend_on_map() { #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[strategy(0..5u32)] #[map(|x: u32| x + 5)] a: u32, #[strategy(0..#a)] b: u32, } assert_arbitrary( (0..5u32) .prop_flat_map(|x| (Just(x + 5), 0..x + 5)) .prop_map(|x| X { a: x.0, b: x.1 }), ); } #[test] fn index() { use proptest::sample::Index; #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[strategy(1..20usize)] a: usize, #[map(|i: Index| i.index(#a))] b: usize, } assert_arbitrary((1..20usize, any::()).prop_map(|x| X { a: x.0, b: x.1.index(x.0), })); } #[test] fn filter_with_strategy_and_map() { use proptest::sample::Index; #[derive(Arbitrary, Debug, PartialEq, Clone)] struct X { #[strategy(10..100usize)] a: usize, #[strategy(0..#a)] x: usize, #[map(|i: Index|i.index(#a))] #[filter(#y % 2 == 0)] y: usize, } let s = (10..100usize, any::()) .prop_flat_map(|x| { ( 0..x.0, Just(x.1.index(x.0)).prop_filter("", |y| y % 2 == 0), Just(x.0), Just(x.1), ) }) .prop_map(|x| X { a: x.2, x: x.0, y: x.1, }); assert_arbitrary(s); } test-strategy-0.3.1/tests/compile_fail/args_not_impl_default.rs000064400000000000000000000003260072674642500231710ustar 00000000000000use test_strategy::Arbitrary; struct TestArgs { x_max: u32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = TestArgs)] struct TestStruct { #[strategy(0..args.x_max)] x: u32, } fn main() {} test-strategy-0.3.1/tests/compile_fail/args_not_impl_default.stderr000064400000000000000000000011470072674642500240520ustar 00000000000000error[E0277]: the trait bound `TestArgs: Default` is not satisfied --> tests/compile_fail/args_not_impl_default.rs:6:20 | 6 | #[arbitrary(args = TestArgs)] | ^^^^^^^^ the trait `Default` is not implemented for `TestArgs` | note: required by a bound in `proptest::arbitrary::Arbitrary::Parameters` --> $CARGO/proptest-1.2.0/src/arbitrary/traits.rs | | type Parameters: Default; | ^^^^^^^ required by this bound in `Arbitrary::Parameters` help: consider annotating `TestArgs` with `#[derive(Default)]` | 2 + #[derive(Default)] 3 | struct TestArgs { | test-strategy-0.3.1/tests/compile_fail/cyclic_dependency.rs000064400000000000000000000002450072674642500222740ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestStruct { #[strategy(1..#y)] x: u32, #[strategy(1..#x)] y: u32, } fn main() {} test-strategy-0.3.1/tests/compile_fail/cyclic_dependency.stderr000064400000000000000000000004200072674642500231460ustar 00000000000000error: found cyclic dependency. (x -> y -> x) --> $DIR/cyclic_dependency.rs:2:10 | 2 | #[derive(Arbitrary, Debug)] | ^^^^^^^^^ | = note: this error originates in the derive macro `Arbitrary` (in Nightly builds, run with -Z macro-backtrace for more info) test-strategy-0.3.1/tests/compile_fail/filter_enum_sharp_val.rs000064400000000000000000000002450072674642500232000ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq, Clone)] #[filter(#x > #y)] enum TestEnum { A { x: i32, y: i32 }, B(i32), } fn main() {} test-strategy-0.3.1/tests/compile_fail/filter_enum_sharp_val.stderr000064400000000000000000000001740072674642500240600ustar 00000000000000error: cannot use `#x` in this position. --> $DIR/filter_enum_sharp_val.rs:3:11 | 3 | #[filter(#x > #y)] | ^ test-strategy-0.3.1/tests/compile_fail/invalid_filter_field.rs000064400000000000000000000002740072674642500227700ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(1..10u32)] #[filter(bad_filter)] x: u32, } fn bad_filter() {} fn main() {} test-strategy-0.3.1/tests/compile_fail/invalid_filter_field.stderr000064400000000000000000000011730072674642500236460ustar 00000000000000error[E0593]: function is expected to take 1 argument, but it takes 0 arguments --> tests/compile_fail/invalid_filter_field.rs:5:14 | 5 | #[filter(bad_filter)] | ^^^^^^^^^^ expected function that takes 1 argument ... 8 | fn bad_filter() {} | --------------- takes 0 arguments | note: required by a bound in `_filter_fn` --> tests/compile_fail/invalid_filter_field.rs:2:10 | 2 | #[derive(Arbitrary, Debug, PartialEq)] | ^^^^^^^^^ required by this bound in `_filter_fn` = note: this error originates in the derive macro `Arbitrary` (in Nightly builds, run with -Z macro-backtrace for more info) test-strategy-0.3.1/tests/compile_fail/invalid_filter_field_expr.rs000064400000000000000000000002320072674642500240200ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[filter(#x)] x: u32, } fn bad_filter() {} fn main() {} test-strategy-0.3.1/tests/compile_fail/invalid_filter_field_expr.stderr000064400000000000000000000002440072674642500247020ustar 00000000000000error[E0308]: mismatched types --> tests/compile_fail/invalid_filter_field_expr.rs:4:15 | 4 | #[filter(#x)] | ^ expected `bool`, found `u32` test-strategy-0.3.1/tests/compile_fail/invalid_strategy.rs000064400000000000000000000002050072674642500221740ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestStruct { #[strategy(0..10usize)] x: u8, } fn main() {} test-strategy-0.3.1/tests/compile_fail/invalid_strategy.stderr000064400000000000000000000011220072674642500230520ustar 00000000000000error[E0271]: type mismatch resolving ` as Strategy>::Value == u8` --> tests/compile_fail/invalid_strategy.rs:4:16 | 4 | #[strategy(0..10usize)] | ^--------- | | | expected `u8`, found `usize` | this tail expression is of type `Range` | note: required by a bound in `_strategy_of_x` --> tests/compile_fail/invalid_strategy.rs:5:8 | 4 | #[strategy(0..10usize)] | - required by a bound in this function 5 | x: u8, | ^^ required by this bound in `_strategy_of_x` test-strategy-0.3.1/tests/compile_fail/invalid_weight.rs000064400000000000000000000002360072674642500216250ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq, Clone)] enum TestEnum { #[weight(1.1)] X, #[weight(2)] Y, } fn main() {} test-strategy-0.3.1/tests/compile_fail/invalid_weight.stderr000064400000000000000000000010430072674642500225010ustar 00000000000000error[E0308]: mismatched types --> tests/compile_fail/invalid_weight.rs:4:14 | 4 | #[weight(1.1)] | ^^^ | | | expected `u32`, found floating-point number | arguments to this function are incorrect | note: function defined here --> tests/compile_fail/invalid_weight.rs:2:10 | 2 | #[derive(Arbitrary, Debug, PartialEq, Clone)] | ^^^^^^^^^ = note: this error originates in the derive macro `Arbitrary` (in Nightly builds, run with -Z macro-backtrace for more info) test-strategy-0.3.1/tests/compile_fail/self_dependency.rs000064400000000000000000000002010072674642500217470ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestStruct { #[strategy(1..#x)] x: u32, } fn main() {} test-strategy-0.3.1/tests/compile_fail/self_dependency.stderr000064400000000000000000000004110072674642500226310ustar 00000000000000error: found cyclic dependency. (x -> x) --> $DIR/self_dependency.rs:2:10 | 2 | #[derive(Arbitrary, Debug)] | ^^^^^^^^^ | = note: this error originates in the derive macro `Arbitrary` (in Nightly builds, run with -Z macro-backtrace for more info) test-strategy-0.3.1/tests/compile_fail/sharp_val_not_found_field.rs000064400000000000000000000002140072674642500240210ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestStruct { #[strategy(0..#y)] x: u32, } fn main() {} test-strategy-0.3.1/tests/compile_fail/sharp_val_not_found_field.stderr000064400000000000000000000002210072674642500246760ustar 00000000000000error: cannot find value `#y` in this scope. --> $DIR/sharp_val_not_found_field.rs:4:20 | 4 | #[strategy(0..#y)] | ^ test-strategy-0.3.1/tests/compile_fail/sharp_val_span.rs000064400000000000000000000002510072674642500216250ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[strategy(1..10u32)] x: u32, #[strategy(0..*#x)] y: u32, } fn main() {} test-strategy-0.3.1/tests/compile_fail/sharp_val_span.stderr000064400000000000000000000002310072674642500225020ustar 00000000000000error[E0614]: type `u32` cannot be dereferenced --> tests/compile_fail/sharp_val_span.rs:8:19 | 8 | #[strategy(0..*#x)] | ^^^ test-strategy-0.3.1/tests/compile_fail/variant_filter_has_no_fields.rs000064400000000000000000000004130072674642500245130ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq, Clone)] enum TestEnum { #[filter(Self::is_match)] A { x: i32, y: i32, }, B(i32), } impl TestEnum { fn is_match(self) -> bool { true } } fn main() {} test-strategy-0.3.1/tests/compile_fail/variant_filter_has_no_fields.stderr000064400000000000000000000003600072674642500253730ustar 00000000000000error: Filters that reference `self` in the variant (filters with no reference to the field) cannot be set. --> tests/compile_fail/variant_filter_has_no_fields.rs:4:13 | 4 | #[filter(Self::is_match)] | ^^^^^^^^^^^^^^^^ test-strategy-0.3.1/tests/compile_fail/weight_attr_more_arg.rs000064400000000000000000000002270072674642500230240ustar 00000000000000use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq, Clone)] enum WeightAttrMoreArg { #[weight(1, 2)] X, Y, } fn main() {} test-strategy-0.3.1/tests/compile_fail/weight_attr_more_arg.stderr000064400000000000000000000001640072674642500237030ustar 00000000000000error: too many arguments. --> $DIR/weight_attr_more_arg.rs:4:17 | 4 | #[weight(1, 2)] | ^ test-strategy-0.3.1/tests/compile_fail.rs000064400000000000000000000001570072674642500166320ustar 00000000000000#[test] #[ignore] fn compile_fail() { trybuild::TestCases::new().compile_fail("tests/compile_fail/*.rs") } test-strategy-0.3.1/tests/proptest_fn.rs000064400000000000000000000027160072674642500165550ustar 00000000000000use proptest::{prelude::ProptestConfig, prop_assert}; use test_strategy::proptest; #[proptest] fn example(_x: u32, #[strategy(1..10u32)] y: u32, #[strategy(0..#y)] z: u32) { assert!(1 <= y); assert!(y < 10); assert!(z <= y); } #[proptest] fn param_x1(_x: u32) {} #[proptest] fn param_x2(_x: u32, _y: u32) {} #[proptest] fn param_mut(mut _x: u32) { _x = 0; } #[proptest] fn param_mut_x2(mut _x: u32, mut _y: u32) { _x = 0; _y = 0; } #[proptest] fn with_strategy(#[strategy(2..10u32)] x: u32) { assert!(2 <= x); assert!(x < 10); } #[proptest] fn with_map( #[strategy(2..10u32)] #[map(|x| x + 2)] x: u32, ) { assert!(4 <= x); assert!(x < 12); } #[proptest(ProptestConfig { timeout: 3, ..ProptestConfig::default() })] #[should_panic] fn config_expr() { std::thread::sleep(std::time::Duration::from_millis(30)); } #[proptest(timeout = 3)] #[should_panic] fn config_field() { std::thread::sleep(std::time::Duration::from_millis(30)); } #[proptest(ProptestConfig::default(), timeout = 3)] #[should_panic] fn config_expr_and_field() { std::thread::sleep(std::time::Duration::from_millis(30)); } #[proptest(async = "tokio")] async fn tokio_test() { tokio::task::spawn(async {}).await.unwrap() } #[proptest(async = "tokio")] async fn tokio_test_no_copy_arg(#[strategy("a+")] s: String) { prop_assert!(s.contains('a')); } #[proptest(async = "tokio")] async fn tokio_test_prop_assert() { prop_assert!(true); } test-strategy-0.3.1/tests/readme_arbitrary.rs000064400000000000000000000147660072674642500175360ustar 00000000000000mod test_helpers; use test_helpers::*; #[test] fn example_1() { use proptest::{ arbitrary::any, arbitrary::Arbitrary, strategy::BoxedStrategy, strategy::Strategy, }; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct MyStructA { x: u32, y: u32, } #[derive(Debug, PartialEq)] struct MyStructB { x: u32, y: u32, } impl Arbitrary for MyStructB { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { let x = any::(); let y = any::(); (x, y).prop_map(|(x, y)| Self { x, y }).boxed() } } assert_arbitrary(any::().prop_map(|MyStructB { x, y }| MyStructA { x, y })); } #[test] fn any_expr() { use proptest::collection::size_range; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestStructA { #[any(size_range(0..16).lift())] x: Vec, } use proptest::arbitrary::any_with; #[derive(Arbitrary, Debug, PartialEq)] struct TestStructB { #[strategy(any_with::>(size_range(0..16).lift()))] x: Vec, } use proptest::arbitrary::any; use proptest::strategy::Strategy; assert_eq_strategy( any::(), any::().prop_map(|TestStructB { x }| TestStructA { x }), ); } #[test] fn any_field() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestInputA { #[any(InnerArgs { upper : 20, ..InnerArgs::default() })] a: Inner, } #[derive(Arbitrary, Debug, PartialEq)] struct TestInputB { #[any(upper = 20)] a: Inner, } #[derive(Arbitrary, Debug, PartialEq)] struct TestInputC { #[any(InnerArgs::default(), upper = 20)] a: Inner, } #[derive(Default)] struct InnerArgs { lower: i32, upper: i32, } #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(args = InnerArgs)] struct Inner { #[strategy(args.lower..args.upper)] x: i32, } use proptest::arbitrary::any; use proptest::strategy::Strategy; assert_eq_strategy( any::(), any::().prop_map(|TestInputB { a }| TestInputA { a }), ); assert_eq_strategy( any::(), any::().prop_map(|TestInputC { a }| TestInputA { a }), ); } #[test] fn weight() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] enum TestInput { A, #[weight(2)] B, } } #[test] fn weight_0() { use test_strategy::Arbitrary; #[derive(Debug)] struct NotArbitrary; #[derive(Arbitrary, Debug)] enum TestInput { A, #[allow(dead_code)] #[weight(0)] B(NotArbitrary), } } #[test] fn filter_field_expr() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[filter(#x % 2 == 0)] #[allow(dead_code)] x: u32, } } #[test] fn filter_struct_expr() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] #[filter((#x + #y) % 2 == 0)] struct TestInput { #[allow(dead_code)] x: u32, #[allow(dead_code)] y: u32, } } #[test] fn filter_struct_expr_self() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] #[filter((#self.x + #self.y) % 2 == 0)] struct TestInput { x: u32, y: u32, } } #[test] fn filter_field_fn() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[filter(is_even)] #[allow(dead_code)] x: u32, } fn is_even(x: &u32) -> bool { x % 2 == 0 } } #[test] fn filter_struct_fn() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] #[filter(x_is_even)] struct TestInput { x: u32, } fn x_is_even(input: &TestInput) -> bool { input.x % 2 == 0 } } #[test] fn filter_name() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[filter("filter name", #x % 2 == 0)] #[allow(dead_code)] x: u32, } } #[test] fn by_ref_strategy() { use test_strategy::Arbitrary; #[derive(Arbitrary, Debug)] struct TestInput { #[by_ref] #[strategy(1..10u32)] #[allow(dead_code)] x: u32, #[strategy(0..*#x)] #[allow(dead_code)] y: u32, } } #[test] fn arbitrary_args() { use test_strategy::Arbitrary; #[derive(Debug, Default)] struct TestInputArgs { x_max: u32, } #[derive(Arbitrary, Debug)] #[arbitrary(args = TestInputArgs)] struct TestInput { #[strategy(0..=args.x_max)] #[allow(dead_code)] x: u32, } } #[test] fn bound_auto() { use proptest::{ arbitrary::any, arbitrary::Arbitrary, strategy::BoxedStrategy, strategy::Strategy, }; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] struct TestInputA { x: T, } #[derive(Debug, PartialEq)] struct TestInputB { x: T, } impl Arbitrary for TestInputB { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { any::().prop_map(|x| Self { x }).boxed() } } assert_arbitrary(any::>().prop_map(|TestInputB { x }| TestInputA { x })); } #[test] fn bound_both() { use proptest::arbitrary::any_with; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T1, ..))] struct TestInput { #[strategy(any_with::(Default::default()))] x: T1, y: T2, } } #[test] fn bound_manual_type() { use proptest::arbitrary::any_with; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T))] struct TestInput { #[strategy(any_with::(Default::default()))] x: T, } } #[test] fn bound_manual_predicate() { use proptest::arbitrary::{any_with, Arbitrary}; use test_strategy::Arbitrary; #[derive(Arbitrary, Debug, PartialEq)] #[arbitrary(bound(T : Arbitrary + 'static))] struct TestInput { #[strategy(any_with::(Default::default()))] x: T, } } test-strategy-0.3.1/tests/readme_proptest.rs000064400000000000000000000011150072674642500173770ustar 00000000000000use test_strategy::proptest; #[proptest] fn my_test(_input: i32) { // ... } #[proptest] fn my_test2(#[strategy(10..20)] _input: i32) { // ... } use proptest::prelude::ProptestConfig; #[proptest(ProptestConfig { cases : 1000, ..ProptestConfig::default() })] fn my_test_with_config(_input: i32) { // ... } #[proptest(ProptestConfig::default(), cases = 1000)] fn my_test_with_config_2(_input: i32) { // ... } #[proptest(cases = 1000)] fn my_test_with_config_3(_input: i32) { // ... } // #[proptest] // #[proptest_dump] // fn my_test(_input: i32) { // // ... // } test-strategy-0.3.1/tests/test_helpers.rs000064400000000000000000000025640072674642500167140ustar 00000000000000use proptest::{ arbitrary::any, arbitrary::Arbitrary, strategy::Strategy, strategy::ValueTree, test_runner::TestRunner, }; use std::fmt::Debug; enum Op { Simplify, Complicate, } pub fn assert_arbitrary(e: impl Strategy) { assert_eq_strategy(any::(), e); } pub fn assert_eq_strategy( l: impl Strategy, r: impl Strategy, ) { let mut ops = vec![Op::Simplify]; for _ in 0..1000 { ops.push(Op::Simplify); ops.push(Op::Complicate); } assert_eq_strategy_ops(l, r, ops) } fn assert_eq_strategy_ops( l: impl Strategy, r: impl Strategy, ops: Vec, ) { let mut l_runner = TestRunner::deterministic(); let mut r_runner = TestRunner::deterministic(); let mut l_tree = l.new_tree(&mut l_runner).unwrap(); let mut r_tree = r.new_tree(&mut r_runner).unwrap(); let mut step = 0; for op in ops { let l_value = l_tree.current(); let r_value = r_tree.current(); assert_eq!(l_value, r_value, "value: {step}"); step += 1; match op { Op::Simplify => assert_eq!(l_tree.simplify(), r_tree.simplify(), "step: {step}"), Op::Complicate => assert_eq!(l_tree.complicate(), r_tree.complicate(), "step: {step}"), } } }