From 41cd56fa549879e39e8c793d1eb7d0239a993a39 Mon Sep 17 00:00:00 2001 From: jutty Date: Sun, 5 Apr 2026 15:30:53 -0300 Subject: [PATCH] Experiment with proptest --- .gitignore | 1 + .justfile | 10 + Cargo.lock | 841 +++++++++++++++++++++++++++++ Cargo.toml | 6 +- proptest-regressions/os/debian.txt | 8 + proptest-regressions/state.txt | 7 + rust-toolchain.toml | 2 + src/os/debian.rs | 290 ++++++++-- src/state.rs | 16 + 9 files changed, 1130 insertions(+), 51 deletions(-) create mode 100644 proptest-regressions/os/debian.txt create mode 100644 proptest-regressions/state.txt create mode 100644 rust-toolchain.toml diff --git a/.gitignore b/.gitignore index ea8c4bf..cffdf3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +mutants.out diff --git a/.justfile b/.justfile index c06b4db..1068505 100644 --- a/.justfile +++ b/.justfile @@ -2,3 +2,13 @@ watch command="run" args="": DEBUG=${DEBUG:-} watchexec -c -w src -- cargo {{ command }} {{ args }} alias w := watch + +[script] +verify: + for solver in cadical bitwuzla cvc5 kissat minisat z3; do + printf '\n => trying solver %s\n\n' $solver; sleep 1 + timeout 20m \ + cargo kani --solver $solver \ + && return + done + diff --git a/Cargo.lock b/Cargo.lock index c3ccd82..404961a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,847 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "blake3" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures", +] + +[[package]] +name = "cc" +version = "1.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f" + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "libredox" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +dependencies = [ + "bitflags", + "libc", + "plain", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "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.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tori" version = "0.8.0" +dependencies = [ + "blake3", + "proptest", + "serde", + "tar", + "toml", + "zstd", +] + +[[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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 2bcacbf..0e2cd89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,16 @@ homepage = "https://tori.jutty.dev/" documentation = "https://tori.jutty.dev/docs/" edition = "2024" -rust-version = "1.94.0" +rust-version = "1.93.0" + +[dev-dependencies] +proptest = "1.0.0" [lints.rust] nonstandard-style = "warn" future-incompatible = "warn" keyword-idents = "warn" +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } [lints.clippy] allow_attributes = "warn" diff --git a/proptest-regressions/os/debian.txt b/proptest-regressions/os/debian.txt new file mode 100644 index 0000000..4d5e141 --- /dev/null +++ b/proptest-regressions/os/debian.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 0e5ef021926f939a8f9ec6a567268b6c3a819fcdf37876eeacb224f4af5c6e14 # shrinks to package_1 = "aAa-a-A-_aA0", package_2 = "a00aAA0-a__0", package_3 = "", package_4 = "oꙀΩ", package_5 = "aᵸΎ", package_6 = "aaꙀ῝" +cc 1ecd871d0e22c01c131bf5f011756cf5fb5e02d480488512175269af52d40c20 # shrinks to package_1 = "-", package_2 = "_", package_3 = "_", package_4 = "a\u{487}ῖ", package_5 = "aᲀὈ", package_6 = "aꙀ𐆠" diff --git a/proptest-regressions/state.txt b/proptest-regressions/state.txt new file mode 100644 index 0000000..c23822d --- /dev/null +++ b/proptest-regressions/state.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 65cb746d9ef96f86b1b9571680e8b00a26b5a6c88c55c0cd57b34494c41d4073 # shrinks to s1 = "\"" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/os/debian.rs b/src/os/debian.rs index 4eace3b..54707c3 100644 --- a/src/os/debian.rs +++ b/src/os/debian.rs @@ -46,6 +46,58 @@ impl Packages for Apt { "dpkg-query", &["--show", "--showformat", "${Package} ${Status}\\n"], ))?; + + let auto_set: HashSet = self + .automatic()? + .into_iter() + .map(|package| package.name().to_owned()) + .collect(); + + Ok(Apt::determine_manual(&raw_all, &auto_set)) + } + + fn automatic(&self) -> Result, pkg::Error> { + Ok(Apt::determine_auto(&read_to_string( + "/var/lib/apt/extended_states", + )?)) + } + + fn variant(&self) -> &PackagerVariant { + &self.variant + } +} + +impl Apt { + fn haul( + operation: &Operation, + packages: &[Package], + config: &Configuration, + ) -> Result { + if packages.is_empty() { + println!("Package selection is empty: Nothing to {operation}"); + return Ok(Transaction::default()); + } + + let rollback_operation = match operation { + Operation::Install => Operation::Uninstall, + Operation::Uninstall => Operation::Install, + }; + + let run_args: Vec<&str> = iter::once(operation.into()) + .chain(packages.iter().map(|p| p.into())) + .collect(); + + let rollback_args: Vec<&str> = iter::once(rollback_operation.into()) + .chain(packages.iter().map(|p| p.into())) + .collect(); + + let run = Command::new("apt", &run_args).escalate(config)?; + let rollback = Command::new("apt", &rollback_args).escalate(config)?; + let transaction_command = TransactionCommand::new(run, rollback); + Ok(Transaction::single(&transaction_command)) + } + + fn determine_manual(raw_all: &str, auto_set: &HashSet) -> Vec { let all = raw_all.lines().filter_map(|line| { let pair = line.split_once(' '); match pair { @@ -58,11 +110,6 @@ impl Packages for Apt { } }); - let auto_set: HashSet = self - .automatic()? - .into_iter() - .map(|package| package.name().to_owned()) - .collect(); let mut manual_packages: Vec = all .into_iter() .filter(|name| !auto_set.contains(name)) @@ -70,13 +117,10 @@ impl Packages for Apt { .collect(); manual_packages.sort(); - Ok(manual_packages) + manual_packages } - fn automatic(&self) -> Result, pkg::Error> { - let path = "/var/lib/apt/extended_states"; - let extended_states = read_to_string(path)?; - + fn determine_auto(extended_states: &str) -> Vec { let lines: Vec<&str> = extended_states .lines() .filter(|line| !line.is_empty()) @@ -138,50 +182,18 @@ impl Packages for Apt { continue; }; - packages.push(Package::new_with_manual(name_value, auto_value == "0")); + if auto_value == "1" { + packages.push(Package::new_with_manual(name_value, auto_value == "0")); + } else { + elog(&format!( + "Skipping: Package {name_value} has an auto-installed value different from 1" + )); + } } } packages.sort(); - Ok(packages) - } - - fn variant(&self) -> &PackagerVariant { - &self.variant - } -} - -impl Apt { - fn haul( - operation: &Operation, - packages: &[Package], - config: &Configuration, - ) -> Result { - if packages.is_empty() { - println!("Package selection is empty: Nothing to {operation}"); - return Ok(Transaction::default()); - } - - // TODO This works as it is stated and is interesting as part of the - // PoC, but doesn't really make sense to install something that wasn't - // installed in the first place as the "rollback" of a failed uninstall - let rollback_operation = match operation { - Operation::Install => Operation::Uninstall, - Operation::Uninstall => Operation::Install, - }; - - let run_args: Vec<&str> = iter::once(operation.into()) - .chain(packages.iter().map(|p| p.into())) - .collect(); - - let rollback_args: Vec<&str> = iter::once(rollback_operation.into()) - .chain(packages.iter().map(|p| p.into())) - .collect(); - - let run = Command::new("apt", &run_args).escalate(config)?; - let rollback = Command::new("apt", &rollback_args).escalate(config)?; - let transaction_command = TransactionCommand::new(run, rollback); - Ok(Transaction::single(&transaction_command)) + packages } } @@ -214,3 +226,181 @@ impl std::fmt::Display for Operation { write!(f, "{s}") } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn determine_manual_given_empty_auto_set() { + let raw_all = "avocado install ok installed\n\ + turnip install ok installed\n\ + carrot install ok installed\n\ + sunflower install ok installed\n\ + pumpkin install ok installed\n\ + squash install ok installed"; + + let auto_set: HashSet = HashSet::default(); + + let manual = Apt::determine_manual(raw_all, &auto_set); + assert_eq!( + manual, + vec![ + "avocado".into(), + "carrot".into(), + "pumpkin".into(), + "squash".into(), + "sunflower".into(), + "turnip".into(), + ] + ); + } + + #[test] + fn determine_manual_given_nonempty_auto_set() { + let raw_all = "avocado install ok installed\n\ + turnip install ok installed\n\ + carrot install ok installed\n\ + sunflower install ok installed\n\ + pumpkin install ok installed\n\ + squash install ok installed"; + + let mut auto_set: HashSet = HashSet::default(); + auto_set.insert("sunflower".to_string()); + auto_set.insert("turnip".to_string()); + + let manual = Apt::determine_manual(raw_all, &auto_set); + assert_eq!( + manual, + vec![ + "avocado".into(), + "carrot".into(), + "pumpkin".into(), + "squash".into(), + ] + ); + } + + #[test] + fn determine_manual_given_empty_raw_input() { + let raw_all = ""; + + let mut auto_set: HashSet = HashSet::default(); + auto_set.insert("sunflower".to_string()); + auto_set.insert("turnip".to_string()); + + let manual = Apt::determine_manual(raw_all, &auto_set); + assert!(manual.is_empty()); + } +} + +#[cfg(test)] +mod proptests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn determine_manual_given_empty_auto_set( + package_1 in "[a-zA-Z0-9-_]{1,24}", + package_2 in "[a-zA-Z0-9-_]{1,24}", + package_3 in "[a-zA-Z0-9-_]{1,24}", + package_4 in "[a-zA-Z0-9-_]{1,24}", + package_5 in "[a-zA-Z0-9-_]{1,24}", + package_6 in "[a-zA-Z0-9-_]{1,24}", + ) { + let raw_all = format!("{package_1} install ok installed\n\ + {package_2} install ok installed\n\ + {package_3} install ok installed\n\ + {package_4} install ok installed\n\ + {package_5} install ok installed\n\ + {package_6} install ok installed"); + + let auto_set: HashSet = HashSet::default(); + + let manual = Apt::determine_manual(&raw_all, &auto_set); + + let mut actual: Vec = manual + .iter().map(|p| p.name().to_string()).collect(); + actual.sort(); + let mut expected = vec![ + package_1, + package_2, + package_3, + package_4, + package_5, + package_6, + ]; + expected.sort(); + assert_eq!(actual, expected); + } + } + + proptest! { + #[test] + fn determine_manual_given_nonempty_auto_set( + package_1 in "[a-zA-Z0-9-_]{1,24}", + package_2 in "[a-zA-Z0-9-_]{1,24}", + package_3 in "[a-zA-Z0-9-_]{1,24}", + package_4 in "[a-z]{1,8}\\p{Cyrillic}{1,8}\\p{Greek}{1,24}", + package_5 in "[a-z]{1,8}\\p{Cyrillic}{1,8}\\p{Greek}{1,24}", + package_6 in "[a-z]{1,8}\\p{Cyrillic}{1,8}\\p{Greek}{1,24}", + ) { + let mut args = [&package_1, + &package_2, + &package_3, + &package_4, + &package_5, + &package_6 + ]; + let (_, dupes) = &args.partition_dedup(); + prop_assume!(dupes.is_empty()); + + let raw_all = format!("{package_1} install ok installed\n\ + {package_2} install ok installed\n\ + {package_3} install ok installed\n\ + {package_4} install ok installed\n\ + {package_5} install ok installed\n\ + {package_6} install ok installed"); + + println!("raw_all: <{raw_all}>"); + + let mut auto_set: HashSet = HashSet::default(); + auto_set.insert(package_1); + auto_set.insert(package_3); + auto_set.insert(package_5); + println!("auto_set: <{auto_set:#?}>"); + + let manual = Apt::determine_manual(&raw_all, &auto_set); + println!("manual: <{manual:#?}>"); + + let mut actual: Vec = manual + .iter().map(|p| p.name().to_string()).collect(); + actual.sort(); + let mut expected = vec![package_2, package_4, package_6]; + expected.sort(); + assert_eq!(actual, expected); + } + } + + proptest! { + #[test] + fn determine_manual_given_empty_raw_input( + auto_package_1 in "[a-zA-Z0-9-_]{1,24}", + auto_package_2 in "[a-zA-Z0-9-_]{1,24}", + auto_package_3 in "", + auto_package_4 in "[a-z]{1,4}\\p{Cyrillic}{1,4}\\p{Greek}{1,24}", + ) { + let raw_all = ""; + + let mut auto_set: HashSet = HashSet::default(); + auto_set.insert(auto_package_1); + auto_set.insert(auto_package_2); + auto_set.insert(auto_package_3); + auto_set.insert(auto_package_4); + + let manual = Apt::determine_manual(raw_all, &auto_set); + assert!(manual.is_empty()); + } + } +} diff --git a/src/state.rs b/src/state.rs index 430f769..ae245e2 100644 --- a/src/state.rs +++ b/src/state.rs @@ -87,3 +87,19 @@ fn strip_quotes(original: &str) -> String { }; no_suffix.to_string() } + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn strip_quotes_props( + s1 in "[\\PC]{1,24}" + ) { + let stripped = strip_quotes(&format!(r#""{s1}""#)); + assert_eq!(stripped, s1); + } + } +}