pax_global_header00006660000000000000000000000064151625164400014516gustar00rootroot0000000000000052 comment=9bafe07343742c860ff53bf66075648f8ebb54a5 rapid-1.3.0/000077500000000000000000000000001516251644000126165ustar00rootroot00000000000000rapid-1.3.0/.editorconfig000066400000000000000000000001711516251644000152720ustar00rootroot00000000000000root = True [*] charset = utf-8 end_of_line = lf insert_final_newline = true [*.go] indent_style = tab indent_size = 8 rapid-1.3.0/.gitattributes000066400000000000000000000000141516251644000155040ustar00rootroot00000000000000*.go eol=lf rapid-1.3.0/.github/000077500000000000000000000000001516251644000141565ustar00rootroot00000000000000rapid-1.3.0/.github/workflows/000077500000000000000000000000001516251644000162135ustar00rootroot00000000000000rapid-1.3.0/.github/workflows/ci.yml000066400000000000000000000012441516251644000173320ustar00rootroot00000000000000name: CI on: push: pull_request: schedule: - cron: "0 */13 * * *" jobs: ci: name: CI strategy: matrix: go: ['1.23', '1.24', '1.25', '1.26'] os: ['ubuntu-latest', 'windows-latest', 'macOS-latest'] runs-on: ${{ matrix.os }} steps: - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Check out code into the Go module directory uses: actions/checkout@v4 - name: Run gofmt run: test -z "$(go fmt .)" shell: bash - name: Test run: go test -race - name: Bench run: go test -run=Benchmark -bench=. rapid-1.3.0/.github/workflows/codeql-analysis.yml000066400000000000000000000015241516251644000220300ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] schedule: - cron: '39 22 * * 0' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] steps: - name: Checkout repository uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Build Go code run: go build ./... - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{matrix.language}}" rapid-1.3.0/.gitignore000066400000000000000000000001651516251644000146100ustar00rootroot00000000000000/.idea/ /.vscode/ *.swp /rapid.test /rapid.test.exe /cpu.prof /mem.prof /profile* /testdata/ /doc.html /vis-*.html rapid-1.3.0/CONTRIBUTING.md000066400000000000000000000011141516251644000150440ustar00rootroot00000000000000# Issues Any issues reported are greatly appreciated. Please consider opening an issue not only in case you have encountered a bug, but if *anything* (be it API, functionality, workflow, docs, ...) looks like it can be improved. # Pull requests Please avoid "improve the code style" kind of pull requests; in particular `if block ends with a return statement, so drop this else and outdent its block` suggestion of `golint` should be ignored. If you intend to work on anything non-trivial, please open an issue first, to discuss the design and implementation before writing any code. rapid-1.3.0/LICENSE000066400000000000000000000405271516251644000136330ustar00rootroot00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. rapid-1.3.0/README.md000066400000000000000000000171111516251644000140760ustar00rootroot00000000000000# rapid [![PkgGoDev][godev-img]][godev] [![CI][ci-img]][ci] Rapid is a Go library for property-based testing. Rapid checks that properties you define hold for a large number of automatically generated test cases. If a failure is found, rapid automatically minimizes the failing test case before presenting it. ## Features - Imperative Go API with type-safe data generation using generics - Data generation biased to explore "small" values and edge cases more thoroughly - Fully automatic minimization of failing test cases - Persistence and automatic re-running of minimized failing test cases - Support for state machine ("stateful" or "model-based") testing - No dependencies outside the Go standard library *If you like rapid but are looking for a Rust alternative, check out [chaos_theory](https://github.com/flyingmutant/chaos_theory)*. ## Examples Here is what a trivial test using rapid looks like ([playground](https://go.dev/play/p/QJhOzo_BByz)): ```go package rapid_test import ( "slices" "testing" "pgregory.net/rapid" ) func TestSortStrings(t *testing.T) { rapid.Check(t, func(t *rapid.T) { s := rapid.SliceOf(rapid.String()).Draw(t, "s") slices.Sort(s) if !slices.IsSorted(s) { t.Fatalf("unsorted after sort: %v", s) } }) } ``` More complete examples: - `ParseDate` function test: [source code](./example_function_test.go), [playground](https://go.dev/play/p/tZFU8zv8AUl) - `Queue` state machine test: [source code](./example_statemachine_test.go), [playground](https://go.dev/play/p/cxEh4deG-4n) ## Comparison Rapid aims to bring to Go the power and convenience [Hypothesis](https://github.com/HypothesisWorks/hypothesis) brings to Python. Compared to [testing.F.Fuzz](https://pkg.go.dev/testing#F.Fuzz), rapid shines in generating complex structured data, including state machine tests, but lacks coverage-guided feedback and mutations. Note that with [`MakeFuzz`](https://pkg.go.dev/pgregory.net/rapid#MakeFuzz), any rapid test can be used as a fuzz target for the standard fuzzer. Compared to [gopter](https://pkg.go.dev/github.com/leanovate/gopter), rapid provides a much simpler API (queue test in [rapid](./example_statemachine_test.go) vs [gopter](https://github.com/leanovate/gopter/blob/90cc76d7f1b21637b4b912a7c19dea3efe145bb2/commands/example_circularqueue_test.go)), is much smarter about data generation and is able to minimize failing test cases fully automatically, without any user code. As for [testing/quick](https://pkg.go.dev/testing/quick), it lacks both convenient data generation facilities and any form of test case minimization, which are two main things to look for in a property-based testing library. ## FAQ ### What is property-based testing? Suppose we've written arithmetic functions `add`, `subtract` and `multiply` and want to test them. Traditional testing approach is example-based — we come up with example inputs and outputs, and verify that the system behavior matches the examples: ```go func TestArithmetic_Example(t *testing.T) { t.Run("add", func(t *testing.T) { examples := [][3]int{ {0, 0, 0}, {0, 1, 1}, {2, 2, 4}, // ... } for _, e := range examples { if add(e[0], e[1]) != e[2] { t.Fatalf("add(%v, %v) != %v", e[0], e[1], e[2]) } } }) t.Run("subtract", func(t *testing.T) { /* ... */ }) t.Run("multiply", func(t *testing.T) { /* ... */ }) } ``` In comparison, with property-based testing we define higher-level properties that should hold for arbitrary input. Each time we run a property-based test, properties are checked on a new set of pseudo-random data: ```go func TestArithmetic_Property(t *testing.T) { rapid.Check(t, func(t *rapid.T) { var ( a = rapid.Int().Draw(t, "a") b = rapid.Int().Draw(t, "b") c = rapid.Int().Draw(t, "c") ) if add(a, 0) != a { t.Fatalf("add() does not have 0 as identity") } if add(a, b) != add(b, a) { t.Fatalf("add() is not commutative") } if add(a, add(b, c)) != add(add(a, b), c) { t.Fatalf("add() is not associative") } if multiply(a, add(b, c)) != add(multiply(a, b), multiply(a, c)) { t.Fatalf("multiply() is not distributive over add()") } // ... }) } ``` Property-based tests are more powerful and concise than example-based ones — and are also much more fun to write. As an additional benefit, coming up with general properties of the system often improves the design of the system itself. ### What properties should I test? As you've seen from the examples above, it depends on the system you are testing. Usually a good place to start is to put yourself in the shoes of your user and ask what are the properties the user will rely on (often unknowingly or implicitly) when building on top of your system. That said, here are some broadly applicable and often encountered properties to keep in mind: - function does not panic on valid input data - behavior of two algorithms or data structures is identical - all variants of the `decode(encode(x)) == x` roundtrip ### How does rapid work? At its core, rapid does a fairly simple thing: generates pseudo-random data based on the specification you provide, and check properties that you define on the generated data. Checking is easy: you simply write `if` statements and call something like `t.Fatalf` when things look wrong. Generating is a bit more involved. When you construct a `Generator`, nothing happens: `Generator` is just a specification of how to `Draw` the data you want. When you call `Draw`, rapid will take some bytes from its internal random bitstream, use them to construct the value based on the `Generator` specification, and track how the random bytes used correspond to the value (and its subparts). This knowledge about the structure of the values being generated, as well as their relationship with the parts of the bitstream allows rapid to intelligently and automatically minify any failure found. ### What about fuzzing? Property-based testing focuses on quick feedback loop: checking the properties on a small but diverse set of pseudo-random inputs in a fractions of a second. In comparison, fuzzing focuses on slow, often multi-day, brute force input generation that maximizes the coverage. Both approaches are useful. Property-based tests are used alongside regular example-based tests during development, and fuzzing is used to search for edge cases and security vulnerabilities. With [`MakeFuzz`](https://pkg.go.dev/pgregory.net/rapid#MakeFuzz), any rapid test can be used as a fuzz target. ## Usage Just run `go test` as usual, it will pick up also all `rapid` tests. There are a number of optional flags to influence rapid behavior, run `go test -args -h` and look at the flags with the `-rapid.` prefix. You can then pass such flags as usual. For example: ```sh go test -rapid.checks=10_000 ``` Every flag with the `-rapid.` prefix also has a matching `RAPID_` environment variable (replace the dot with an underscore and uppercase the name). For instance, `RAPID_CHECKS=10 go test` sets the default for `-rapid.checks=10`, and an explicit flag still overrides the environment default when both are present. ## Status Rapid is stable: tests using rapid should continue to work with all future rapid releases with the same major version. Possible exceptions to this rule are API changes that replace the concrete type of parameter with an interface type, or other similar mostly non-breaking changes. ## License Rapid is licensed under the [Mozilla Public License Version 2.0](./LICENSE). [godev-img]: https://pkg.go.dev/badge/pgregory.net/rapid [godev]: https://pkg.go.dev/pgregory.net/rapid [ci-img]: https://github.com/flyingmutant/rapid/workflows/CI/badge.svg [ci]: https://github.com/flyingmutant/rapid/actions rapid-1.3.0/TODO.md000066400000000000000000000117111516251644000137060ustar00rootroot00000000000000# TODO ## Generators - times, durations, locations - complex numbers - big numbers - ip addresses & masks - subset-of-slice - runes with rune/range blacklist - recursive (base + extend) ## Shrinking - make it OK to pivot to a different error when shrinking - right now, we require for traceback to remain the same to continue shrinking, which is probably limiting - floats: maybe shrink towards lower *biased* exponent? - just like we have lower+delete pass to deal with situations like generation/sampling, we need to have a pass for choice - idea: lower (the "choice" block) + fill some region with random data - to try to reproduce with a simpler choice - this should work both OneOf and floats (where exponent is kind of a OneOf key) - questions: - how to deal with misalignment? - how to determine the group to randomize? - e.g. right now for floats it is not an explicit group but rather a bunch of nearby blocks - use fewer bits for genFloat01 to make shrinking a bit faster - shrink duplicates together - generalize to arbitrary "offsets" for pairs - better caching - detect when we are generating already generated values and abort early - not all value groups are standalone! - standalone might be too coarse, maybe should be replaced with a bunch of other traits - we are doing too much prop evaluations - partial sort does not swap e.g. int and int32 - when shrinking, if we try to lower the wanted bits of some uint64, we have a high chance to draw very low value - because high bits will be masked out - this can prevent shrinking, when we first lower block A (which e.g. selects the generator), then we draw next block B (which the lowered generator wants fewer bits of). Instead of getting a bit value for B and doing proper search, we end up getting a small one, and abandoning the generator shrink - for order-based passes, try alternating orders? - what order is a better default? - "prefix search" shrinking - when shrinking, why do we leave the tail the same? - we have "misalignment" problems and all that - generate random data instead! - generate random tails all the time - minimize bitstream mis-alignment during shrinking (try to make the shape as constant as possible) - better, make minimization not care about mis-alignment - sticky bitstream? - differentiate groups with structure vs groups without one for smarter shrinking - non-greedy shrink - allow to increase the data size *between shrink passes*, if the net result is good - e.g. allow sort to do arbitrary? swaps - rejection sampling during shrinking leads to data misalignment, is this a problem? - can we detect overruns early and re-roll only the last part of the bitstream? - maybe overwrite bitstream instead of prune? - to never have an un-pruned version - to guarantee? that we can draw values successfully while shrinking (working with bufBitStream) ## Misc - ability to run tests without shrinking (e.g. for running non-deterministic tests) - bitStream -> blockStream? - do not play with filter games for the state machine, just find all valid actions - when generating numbers in range, try to bias based on the min number, just like we bias repeat based on the min number? - because min number defines the "magnitude" of the whole thing, kind of? - so when we are generating numbers in [1000000; +inf) we do not stick with 1000000 too hard - more powerful assume/filter (look at what hypothesis is doing) - incorporate special case checking (bounds esp.) ## Wild ideas - global path-based generation (kind of like simplex method), which makes most of the generators hit corner cases simultaneously - recurrence-based generation, because it is hard to stumble upon interesting stuff purely by random - start generating already generated stuff, overriding random for some number of draws - zip the sequence with itself - random jumps of rng, back/forward - recurrence-based generation may actually be better than usual fuzzing! - because we operate on 64 bits at once, which in most cases correspond to "full value", we have e.g. a much better chance to reproduce a multi-byte sequence (exact or slightly altered) somewhere else - this is kind-of related to go-fuzz versifier in some way - we also can (and should) reuse whole chunks which can correspond to strings/lists/etc. - random markov chain which switches states like - generate new data - reuse existing data starting from - reuse existing data altering it like X - should transition probabilities be universal or depend on generators? - should they also determine where to jump to, so that we jump to "compatible" stuff only? - can tag words with compatibility classes - can just jump to previous starts of the generated blocks? - can explore/exploit trade-off help us decide when to generate random data, and when to reuse existing? - probably can do thompson sampling when we have online coverage information - arbiter-based distributed system tester rapid-1.3.0/collections.go000066400000000000000000000127131516251644000154670ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import "fmt" // ID returns its argument as is. ID is a helper for use with [SliceOfDistinct] and similar functions. func ID[V any](v V) V { return v } // SliceOf is a shorthand for [SliceOfN](elem, -1, -1). func SliceOf[E any](elem *Generator[E]) *Generator[[]E] { return SliceOfN(elem, -1, -1) } // SliceOfN creates a []E generator. If minLen >= 0, generated slices have minimum length of minLen. // If maxLen >= 0, generated slices have maximum length of maxLen. SliceOfN panics if maxLen >= 0 // and minLen > maxLen. func SliceOfN[E any](elem *Generator[E], minLen int, maxLen int) *Generator[[]E] { assertValidRange(minLen, maxLen) return newGenerator[[]E](&sliceGen[E, struct{}]{ minLen: minLen, maxLen: maxLen, elem: elem, }) } // SliceOfDistinct is a shorthand for [SliceOfNDistinct](elem, -1, -1, keyFn). func SliceOfDistinct[E any, K comparable](elem *Generator[E], keyFn func(E) K) *Generator[[]E] { return SliceOfNDistinct(elem, -1, -1, keyFn) } // SliceOfNDistinct creates a []E generator. Elements of each generated slice are distinct according to keyFn. // If minLen >= 0, generated slices have minimum length of minLen. If maxLen >= 0, generated slices // have maximum length of maxLen. SliceOfNDistinct panics if maxLen >= 0 and minLen > maxLen. // [ID] helper can be used as keyFn to generate slices of distinct comparable elements. func SliceOfNDistinct[E any, K comparable](elem *Generator[E], minLen int, maxLen int, keyFn func(E) K) *Generator[[]E] { assertValidRange(minLen, maxLen) return newGenerator[[]E](&sliceGen[E, K]{ minLen: minLen, maxLen: maxLen, elem: elem, keyFn: keyFn, }) } type sliceGen[E any, K comparable] struct { minLen int maxLen int elem *Generator[E] keyFn func(E) K } func (g *sliceGen[E, K]) String() string { if g.keyFn == nil { if g.minLen < 0 && g.maxLen < 0 { return fmt.Sprintf("SliceOf(%v)", g.elem) } else { return fmt.Sprintf("SliceOfN(%v, minLen=%v, maxLen=%v)", g.elem, g.minLen, g.maxLen) } } else { if g.minLen < 0 && g.maxLen < 0 { return fmt.Sprintf("SliceOfDistinct(%v, key=%T)", g.elem, g.keyFn) } else { return fmt.Sprintf("SliceOfNDistinct(%v, minLen=%v, maxLen=%v, key=%T)", g.elem, g.minLen, g.maxLen, g.keyFn) } } } func (g *sliceGen[E, K]) value(t *T) []E { repeat := newRepeat(g.minLen, g.maxLen, -1, g.elem.String()) var seen map[K]struct{} if g.keyFn != nil { seen = make(map[K]struct{}, repeat.avg()) } sl := make([]E, 0, repeat.avg()) for repeat.more(t.s) { e := g.elem.value(t) if g.keyFn == nil { sl = append(sl, e) } else { k := g.keyFn(e) if _, ok := seen[k]; ok { repeat.reject() } else { seen[k] = struct{}{} sl = append(sl, e) } } } return sl } // MapOf is a shorthand for [MapOfN](key, val, -1, -1). func MapOf[K comparable, V any](key *Generator[K], val *Generator[V]) *Generator[map[K]V] { return MapOfN(key, val, -1, -1) } // MapOfN creates a map[K]V generator. If minLen >= 0, generated maps have minimum length of minLen. // If maxLen >= 0, generated maps have maximum length of maxLen. MapOfN panics if maxLen >= 0 // and minLen > maxLen. func MapOfN[K comparable, V any](key *Generator[K], val *Generator[V], minLen int, maxLen int) *Generator[map[K]V] { assertValidRange(minLen, maxLen) return newGenerator[map[K]V](&mapGen[K, V]{ minLen: minLen, maxLen: maxLen, key: key, val: val, }) } // MapOfValues is a shorthand for [MapOfNValues](val, -1, -1, keyFn). func MapOfValues[K comparable, V any](val *Generator[V], keyFn func(V) K) *Generator[map[K]V] { return MapOfNValues(val, -1, -1, keyFn) } // MapOfNValues creates a map[K]V generator, where keys are generated by applying keyFn to values. // If minLen >= 0, generated maps have minimum length of minLen. If maxLen >= 0, generated maps // have maximum length of maxLen. MapOfNValues panics if maxLen >= 0 and minLen > maxLen. func MapOfNValues[K comparable, V any](val *Generator[V], minLen int, maxLen int, keyFn func(V) K) *Generator[map[K]V] { assertValidRange(minLen, maxLen) return newGenerator[map[K]V](&mapGen[K, V]{ minLen: minLen, maxLen: maxLen, val: val, keyFn: keyFn, }) } type mapGen[K comparable, V any] struct { minLen int maxLen int key *Generator[K] val *Generator[V] keyFn func(V) K } func (g *mapGen[K, V]) String() string { if g.key != nil { if g.minLen < 0 && g.maxLen < 0 { return fmt.Sprintf("MapOf(%v, %v)", g.key, g.val) } else { return fmt.Sprintf("MapOfN(%v, %v, minLen=%v, maxLen=%v)", g.key, g.val, g.minLen, g.maxLen) } } else { if g.minLen < 0 && g.maxLen < 0 { return fmt.Sprintf("MapOfValues(%v, key=%T)", g.val, g.keyFn) } else { return fmt.Sprintf("MapOfNValues(%v, minLen=%v, maxLen=%v, key=%T)", g.val, g.minLen, g.maxLen, g.keyFn) } } } func (g *mapGen[K, V]) value(t *T) map[K]V { label := g.val.String() if g.key != nil { label = g.key.String() + "," + label } repeat := newRepeat(g.minLen, g.maxLen, -1, label) m := make(map[K]V, repeat.avg()) for repeat.more(t.s) { var k K var v V if g.key != nil { k = g.key.value(t) v = g.val.value(t) } else { v = g.val.value(t) k = g.keyFn(v) } if _, ok := m[k]; ok { repeat.reject() } else { m[k] = v } } return m } rapid-1.3.0/collections_example_test.go000066400000000000000000000065741516251644000202510ustar00rootroot00000000000000// Copyright 2020 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "fmt" "pgregory.net/rapid" ) func ExampleSliceOf() { gen := rapid.SliceOf(rapid.Int()) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // [1 -1902 7 -236 14 -433 -1572631 -1 4219826 -50 1414 -3890044391133 -9223372036854775808 5755498240 -10 680558 10 -80458281 0 -27] // [-3 -2 -1 -3 -2172865589 -5 -2 -2503553836720] // [4 308 -2 21 -5843 3 1 78 6129321692 -59] // [590 -131 -15 -769 16 -1 14668 14 -1 -58784] // [] } func ExampleSliceOfN() { gen := rapid.SliceOfN(rapid.Int(), 5, 5) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // [1 -1902 7 -236 14] // [-3 -2 -1 -3 -2172865589] // [4 308 -2 21 -5843] // [590 -131 -15 -769 16] // [4629136912 270 141395 -129322425838843911 -7] } func ExampleSliceOfDistinct() { gen := rapid.SliceOfDistinct(rapid.IntMin(0), func(i int) int { return i % 2 }) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // [1] // [2 1] // [4 1] // [590] // [] } func ExampleSliceOfNDistinct() { gen := rapid.SliceOfNDistinct(rapid.IntMin(0), 2, 2, func(i int) int { return i % 2 }) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // [4219826 49] // [2 1] // [4 1] // [0 58783] // [4629136912 141395] } func ExampleMapOf() { gen := rapid.MapOf(rapid.Int(), rapid.StringMatching(`[a-z]+`)) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // map[1:nhlgqwasbggbaociac 561860:r] // map[-3752:pizpv -3:bacuabp 0:bi] // map[-33086515648293:gewf -264276:b -1313:a -258:v -4:b -2:fdhbzcz 4:ubfsdbowrja 1775:tcozav 8334:lvcprss 376914:braigey] // map[-350:h 590:coaaamcasnapgaad] // map[] } func ExampleMapOfN() { gen := rapid.MapOfN(rapid.Int(), rapid.StringMatching(`[a-z]+`), 5, 5) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // map[-130450326583:bd -2983:bbdbcs 1:nhlgqwasbggbaociac 31:kmdnpmcbuagzr 561860:r] // map[-82024404:d -3752:pizpv -3:bacuabp 0:bi 179745:rzkneb] // map[-33086515648293:gewf -258:v 4:ubfsdbowrja 1775:tcozav 8334:lvcprss] // map[-4280678227:j -25651:aafmd -3308:o -350:h 590:coaaamcasnapgaad] // map[-9614404661322:gsb -378:y 2:paai 4629136912:otg 1476419818092:qign] } func ExampleMapOfValues() { gen := rapid.MapOfValues(rapid.StringMatching(`[a-z]+`), func(s string) int { return len(s) }) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // map[2:dr 7:xguehfc 11:sbggbaociac] // map[2:bp 5:jarxz 6:ebzkwa] // map[1:j 2:aj 3:gjl 4:vayt 5:eeeqa 6:riacaa 7:stcozav 8:mfdhbzcz 9:fxmcadagf 10:bgsbraigey 15:gxongygnxqlovib] // map[2:ub 8:waraafmd 10:bfiqcaxazu 16:rjgqimcasnapgaad 17:gckfbljafcedhcvfc] // map[] } func ExampleMapOfNValues() { gen := rapid.MapOfNValues(rapid.StringMatching(`[a-z]+`), 5, 5, func(s string) int { return len(s) }) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // map[1:s 2:dr 3:anc 7:xguehfc 11:sbggbaociac] // map[1:b 2:bp 4:ydag 5:jarxz 6:ebzkwa] // map[1:j 3:gjl 5:eeeqa 7:stcozav 9:fxmcadagf] // map[2:ub 8:waraafmd 10:bfiqcaxazu 16:rjgqimcasnapgaad 17:gckfbljafcedhcvfc] // map[1:k 2:ay 3:wzb 4:dign 7:faabhcb] } rapid-1.3.0/collections_external_test.go000066400000000000000000000064131516251644000204300ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "reflect" "strconv" "testing" . "pgregory.net/rapid" ) func TestSliceOf(t *testing.T) { t.Parallel() gens := []*Generator[any]{ SliceOf(Bool()).AsAny(), SliceOf(Byte()).AsAny(), SliceOf(Int()).AsAny(), SliceOf(Uint()).AsAny(), } for _, g := range gens { t.Run(g.String(), MakeCheck(func(t *T) { v := g.Draw(t, "v") if rv(v).Kind() != reflect.Slice { t.Fatalf("got not a slice") } if rv(v).Len() == 0 { t.Skip("empty") } })) } } func TestSliceOfDistinct(t *testing.T) { t.Parallel() g := SliceOfDistinct(Int(), ID[int]) Check(t, func(t *T) { s := g.Draw(t, "s") m := map[int]struct{}{} for _, i := range s { m[i] = struct{}{} } if len(m) != len(s) { t.Fatalf("%v unique out of %v", len(m), len(s)) } }) } func TestSliceOfDistinctBy(t *testing.T) { t.Parallel() g := SliceOfDistinct(Int(), func(i int) string { return strconv.Itoa(i % 5) }) Check(t, func(t *T) { s := g.Draw(t, "s") m := map[int]struct{}{} for _, i := range s { m[i%5] = struct{}{} } if len(m) != len(s) { t.Fatalf("%v unique out of %v", len(m), len(s)) } }) } func TestMapOf(t *testing.T) { t.Parallel() gens := []*Generator[any]{ MapOf(Bool(), Int()).AsAny(), MapOf(Int(), Uint()).AsAny(), MapOf(Uint(), SliceOf(Bool())).AsAny(), } for _, g := range gens { t.Run(g.String(), MakeCheck(func(t *T) { v := g.Draw(t, "v") if rv(v).Kind() != reflect.Map { t.Fatalf("got not a map") } if rv(v).Len() == 0 { t.Skip("empty") } })) } } func TestMapOfValues(t *testing.T) { t.Parallel() g := MapOfValues(Custom(genStruct), func(s testStruct) int { return s.x }) Check(t, func(t *T) { m := g.Draw(t, "m") for k, v := range m { if k != v.x { t.Fatalf("got key %v with value %v", k, v) } } }) } func TestCollectionLenLimits(t *testing.T) { t.Parallel() genFuncs := []func(i, j int) *Generator[any]{ func(i, j int) *Generator[any] { return StringOfN(Int32Range('A', 'Z'), i, j, -1).AsAny() }, func(i, j int) *Generator[any] { return SliceOfN(Byte(), i, j).AsAny() }, func(i, j int) *Generator[any] { return SliceOfNDistinct(Byte(), i, j, ID[byte]).AsAny() }, func(i, j int) *Generator[any] { return SliceOfNDistinct(Int(), i, j, func(n int) int { return n % j }).AsAny() }, func(i, j int) *Generator[any] { return MapOfN(Int(), Int(), i, j).AsAny() }, func(i, j int) *Generator[any] { return MapOfNValues(Int(), i, j, ID[int]).AsAny() }, func(i, j int) *Generator[any] { return MapOfNValues(Int(), i, j, func(n int) int { return n % j }).AsAny() }, } for i, gf := range genFuncs { t.Run(strconv.Itoa(i), MakeCheck(func(t *T) { minLen := IntRange(0, 256).Draw(t, "minLen") maxLen := IntMin(minLen).Draw(t, "maxLen") s := rv(gf(minLen, maxLen).Draw(t, "s")) if s.Len() < minLen { t.Fatalf("got collection of length %v with minLen %v", s.Len(), minLen) } if s.Len() > maxLen { t.Fatalf("got collection of length %v with maxLen %v", s.Len(), maxLen) } })) } } rapid-1.3.0/collections_test.go000066400000000000000000000014361516251644000165260ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import "testing" func TestCollectionsWithImpossibleMinSize(t *testing.T) { t.Parallel() s := createRandomBitStream(t) gens := []*Generator[any]{ MapOfN(Bool(), Int(), 10, -1).AsAny(), SliceOfNDistinct(Int(), 10, -1, func(i int) int { return i % 5 }).AsAny(), } for _, g := range gens { t.Run(g.String(), func(t *testing.T) { _, err := recoverValue(g, newT(nil, s, false, nil)) if err == nil || !err.isInvalidData() { t.Fatalf("got error %v instead of invalid data", err) } }) } } rapid-1.3.0/combinators.go000066400000000000000000000141241516251644000154670ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "math" "strings" ) const tryLabel = "try" // Custom creates a generator which produces results of calling fn. In fn, values should be generated // by calling other generators; it is invalid to return a value from fn without using any other generator. // Custom is a primary way of creating user-defined generators. func Custom[V any](fn func(*T) V) *Generator[V] { return newGenerator[V](&customGen[V]{ fn: fn, }) } type customGen[V any] struct { fn func(*T) V } func (g *customGen[V]) String() string { var v V return fmt.Sprintf("Custom(%T)", v) } func (g *customGen[V]) value(t *T) V { return find(g.maybeValue, t, small) } func (g *customGen[V]) maybeValue(t *T) (V, bool) { t = newT(t.tb, t.s, flags.debug, nil) defer t.cleanup() defer func() { if r := recover(); r != nil { if _, ok := r.(invalidData); !ok { panic(r) } } }() return g.fn(t), true } // Deferred creates a generator which defers calling fn until attempting to produce a value. This allows // to define recursive generators. func Deferred[V any](fn func() *Generator[V]) *Generator[V] { return newGenerator[V](&deferredGen[V]{ fn: fn, }) } type deferredGen[V any] struct { g *Generator[V] fn func() *Generator[V] } func (g *deferredGen[V]) String() string { var v V return fmt.Sprintf("Deferred(%T)", v) } func (g *deferredGen[V]) value(t *T) V { if g.g == nil { g.g = g.fn() } return g.g.value(t) } func filter[V any](g *Generator[V], fn func(V) bool) *Generator[V] { return newGenerator[V](&filteredGen[V]{ g: g, fn: fn, }) } type filteredGen[V any] struct { g *Generator[V] fn func(V) bool } func (g *filteredGen[V]) String() string { return fmt.Sprintf("%v.Filter(...)", g.g) } func (g *filteredGen[V]) value(t *T) V { return find(g.maybeValue, t, small) } func (g *filteredGen[V]) maybeValue(t *T) (V, bool) { v := g.g.value(t) if g.fn(v) { return v, true } else { var zero V return zero, false } } func find[V any](gen func(*T) (V, bool), t *T, tries int) V { for n := 0; n < tries; n++ { i := t.s.beginGroup(tryLabel, false) v, ok := gen(t) t.s.endGroup(i, !ok) if ok { return v } } panic(invalidData(fmt.Sprintf("failed to find suitable value in %d tries", tries))) } // Map creates a generator producing fn(u) for each u produced by g. func Map[U any, V any](g *Generator[U], fn func(U) V) *Generator[V] { return newGenerator[V](&mappedGen[U, V]{ g: g, fn: fn, }) } type mappedGen[U any, V any] struct { g *Generator[U] fn func(U) V } func (g *mappedGen[U, V]) String() string { return fmt.Sprintf("Map(%v, %T)", g.g, g.fn) } func (g *mappedGen[U, V]) value(t *T) V { return g.fn(g.g.value(t)) } // Just creates a generator which always produces the given value. // Just(val) is a shorthand for [SampledFrom]([]V{val}). func Just[V any](val V) *Generator[V] { return SampledFrom([]V{val}) } // SampledFrom creates a generator which produces values from the given slice. // SampledFrom panics if slice is empty. func SampledFrom[S ~[]E, E any](slice S) *Generator[E] { assertf(len(slice) > 0, "slice should not be empty") return newGenerator[E](&sampledGen[E]{ slice: slice, }) } type sampledGen[E any] struct { slice []E } func (g *sampledGen[E]) String() string { if len(g.slice) == 1 { return fmt.Sprintf("Just(%v)", g.slice[0]) } else { return fmt.Sprintf("SampledFrom(%v %T)", len(g.slice), g.slice[0]) } } func (g *sampledGen[E]) value(t *T) E { i := genIndex(t.s, len(g.slice), true) return g.slice[i] } // Permutation creates a generator which produces permutations of the given slice. func Permutation[S ~[]E, E any](slice S) *Generator[S] { return newGenerator[S](&permGen[S, E]{ slice: slice, }) } type permGen[S ~[]E, E any] struct { slice S } func (g *permGen[S, E]) String() string { var zero E return fmt.Sprintf("Permutation(%v %T)", len(g.slice), zero) } func (g *permGen[S, E]) value(t *T) S { s := append(S(nil), g.slice...) n := len(s) m := n - 1 if m < 0 { m = 0 } // shrink-friendly variant of Fisher–Yates shuffle: shrinks to lower number of smaller distance swaps repeat := newRepeat(0, m, math.MaxInt, "permute") for i := 0; repeat.more(t.s); i++ { j, _, _ := genUintRange(t.s, uint64(i), uint64(n-1), false) s[i], s[j] = s[j], s[i] } return s } // OneOf creates a generator which produces each value by selecting one of gens and producing a value from it. // OneOf panics if gens is empty. func OneOf[V any](gens ...*Generator[V]) *Generator[V] { assertf(len(gens) > 0, "at least one generator should be specified") return newGenerator[V](&oneOfGen[V]{ gens: gens, }) } type oneOfGen[V any] struct { gens []*Generator[V] } func (g *oneOfGen[V]) String() string { strs := make([]string, len(g.gens)) for i, g := range g.gens { strs[i] = g.String() } return fmt.Sprintf("OneOf(%v)", strings.Join(strs, ", ")) } func (g *oneOfGen[V]) value(t *T) V { i := genIndex(t.s, len(g.gens), true) return g.gens[i].value(t) } // Ptr creates a *E generator. If allowNil is true, Ptr can return nil pointers. func Ptr[E any](elem *Generator[E], allowNil bool) *Generator[*E] { return newGenerator[*E](&ptrGen[E]{ elem: elem, allowNil: allowNil, }) } type ptrGen[E any] struct { elem *Generator[E] allowNil bool } func (g *ptrGen[E]) String() string { return fmt.Sprintf("Ptr(%v, allowNil=%v)", g.elem, g.allowNil) } func (g *ptrGen[E]) value(t *T) *E { pNonNil := float64(1) if g.allowNil { pNonNil = 0.5 } if flipBiasedCoin(t.s, pNonNil) { e := g.elem.value(t) return &e } else { return nil } } func asAny[V any](g *Generator[V]) *Generator[any] { return newGenerator[any](&asAnyGen[V]{ gen: g, }) } type asAnyGen[V any] struct { gen *Generator[V] } func (g *asAnyGen[V]) String() string { return fmt.Sprintf("%v.AsAny()", g.gen) } func (g *asAnyGen[V]) value(t *T) any { return g.gen.value(t) } rapid-1.3.0/combinators_example_test.go000066400000000000000000000043601516251644000202420ustar00rootroot00000000000000// Copyright 2020 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "fmt" "strconv" "pgregory.net/rapid" ) func ExampleCustom() { type point struct { x int y int } gen := rapid.Custom(func(t *rapid.T) point { return point{ x: rapid.IntRange(-100, 100).Draw(t, "x"), y: rapid.IntRange(-100, 100).Draw(t, "y"), } }) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // {-1 23} // {-3 -50} // {0 94} // {-2 -50} // {11 -57} } func recursive() *rapid.Generator[any] { return rapid.OneOf( rapid.Bool().AsAny(), rapid.SliceOfN(rapid.Deferred(recursive), 1, 2).AsAny(), ) } func ExampleDeferred() { gen := recursive() for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // [[[[false] false]]] // false // [[true [[[true]]]]] // true // true } func ExampleMap() { gen := rapid.Map(rapid.Int(), strconv.Itoa) for i := 0; i < 5; i++ { fmt.Printf("%#v\n", gen.Example(i)) } // Output: // "-3" // "-186981" // "4" // "-2" // "43" } func ExampleJust() { gen := rapid.Just(42) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // 42 // 42 // 42 // 42 // 42 } func ExampleSampledFrom() { gen := rapid.SampledFrom([]int{1, 2, 3}) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // 2 // 3 // 2 // 3 // 1 } func ExamplePermutation() { gen := rapid.Permutation([]int{1, 2, 3}) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // [2 3 1] // [3 2 1] // [2 1 3] // [3 2 1] // [1 2 3] } func ExampleOneOf() { gen := rapid.OneOf(rapid.Int32Range(1, 10).AsAny(), rapid.Float32Range(100, 1000).AsAny()) for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // 997.0737 // 10 // 475.3125 // 2 // 9 } func ExamplePtr() { gen := rapid.Ptr(rapid.Int(), true) for i := 0; i < 5; i++ { v := gen.Example(i) if v == nil { fmt.Println("") } else { fmt.Println("(*int)", *v) } } // Output: // (*int) 1 // (*int) -3 // // (*int) 590 // } rapid-1.3.0/combinators_external_test.go000066400000000000000000000074441516251644000204370ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "context" "errors" "fmt" "strconv" "testing" . "pgregory.net/rapid" ) type testStruct struct { x int y int } func genBool(t *T) bool { return Bool().Draw(t, "") } func genInterface(t *T) any { if Bool().Draw(t, "coinflip") { return Int8().Draw(t, "") } else { return Float64().Draw(t, "") } } func genSlice(t *T) []uint64 { return []uint64{ Uint64().Draw(t, ""), Uint64().Draw(t, ""), } } func genStruct(t *T) testStruct { return testStruct{ x: Int().Draw(t, "x"), y: Int().Draw(t, "y"), } } func TestCustom(t *testing.T) { t.Parallel() gens := []*Generator[any]{ Custom(genBool).AsAny(), Custom(genInterface).AsAny(), Custom(genSlice).AsAny(), Custom(genStruct).AsAny(), } for _, g := range gens { t.Run(g.String(), MakeCheck(func(t *T) { g.Draw(t, "") })) } } func TestCustomContext(t *testing.T) { t.Parallel() type key struct{} gen := Custom(func(t *T) context.Context { ctx := t.Context() // Inside the custom generator, the context must be valid. if err := ctx.Err(); err != nil { t.Fatalf("context must be valid: %v", err) } x := Int().Draw(t, "x") return context.WithValue(ctx, key{}, x) }) Check(t, func(t *T) { ctx := gen.Draw(t, "value") if _, ok := ctx.Value(key{}).(int); !ok { t.Fatalf("context must contain an int") } // Outside the custom generator, // the context from inside the generator // must no longer be valid. if err := ctx.Err(); err == nil || !errors.Is(err, context.Canceled) { t.Fatalf("context must be canceled: %v", err) } }) } func TestCustomCleanup(t *testing.T) { t.Parallel() var open bool gen := Custom(func(t *T) int { t.Cleanup(func() { open = false }) return Int().Draw(t, "") }) // Cleanup functions registered during a Custom generator // are run after generation, not after the Check. Check(t, func(t *T) { open = true _ = gen.Draw(t, "value") // Cleanup must run after each run of the custom generator. if open { t.Fatalf("cleanup must be run") } }) } func TestFilter(t *testing.T) { t.Parallel() g := Int().Filter(func(i int) bool { return i >= 0 }) Check(t, func(t *T) { v := g.Draw(t, "v") if v < 0 { t.Fatalf("got negative %v", v) } }) } func TestMap(t *testing.T) { t.Parallel() g := Map(Int(), strconv.Itoa) Check(t, func(t *T) { s := g.Draw(t, "s") _, err := strconv.Atoi(s) if err != nil { t.Fatalf("Atoi() error %v", err) } }) } func TestSampledFrom(t *testing.T) { t.Parallel() gens := []*Generator[int]{ Just(3), SampledFrom([]int{3, 5, 7}), } for _, g := range gens { t.Run(g.String(), MakeCheck(func(t *T) { n := g.Draw(t, "n") if n != 3 && n != 5 && n != 7 { t.Fatalf("got impossible %v", n) } })) } } func TestOneOf_SameType(t *testing.T) { t.Parallel() pos := Int().Filter(func(v int) bool { return v >= 10 }) neg := Int().Filter(func(v int) bool { return v <= -10 }) g := OneOf(pos, neg) Check(t, func(t *T) { n := g.Draw(t, "n") if n > -10 && n < 10 { t.Fatalf("got impossible %v", n) } }) } func TestOneOf_DifferentTypes(t *testing.T) { t.Parallel() g := OneOf(Int().AsAny(), Int8().AsAny(), Int16().AsAny(), Int32().AsAny(), Int64().AsAny()) Check(t, func(t *T) { n := g.Draw(t, "n") _ = rv(n).Int() }) } func TestPtr(t *testing.T) { t.Parallel() for _, allowNil := range []bool{false, true} { t.Run(fmt.Sprintf("allowNil=%v", allowNil), MakeCheck(func(t *T) { i := Ptr(Int(), allowNil).Draw(t, "i") if i == nil && !allowNil { t.Fatalf("got nil pointer") } })) } } rapid-1.3.0/combinators_test.go000066400000000000000000000012751516251644000165310ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import "testing" type intPair struct { x int y int } func BenchmarkHeavyChain3(b *testing.B) { t := newT(nil, newRandomBitStream(baseSeed(), false), false, nil) g1 := Custom(func(t *T) int { return Int().Draw(t, "") }) g2 := Map(g1, func(i int) intPair { return intPair{i, i << 13} }) g3 := Map(g2, func(p intPair) int { return p.x + p.y }) b.ResetTimer() for i := 0; i < b.N; i++ { g3.value(t) } } rapid-1.3.0/data.go000066400000000000000000000072521516251644000140640ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "hash/maphash" "math" "math/bits" ) type bitStream interface { drawBits(n int) uint64 beginGroup(label string, standalone bool) int endGroup(i int, discard bool) } func baseSeed() uint64 { if flags.seed != 0 { return flags.seed } return new(maphash.Hash).Sum64() } type randomBitStream struct { ctx jsf64ctx recordedBits } func newRandomBitStream(seed uint64, persist bool) *randomBitStream { s := &randomBitStream{} s.init(seed) s.persist = persist return s } func (s *randomBitStream) init(seed uint64) { s.ctx.init(seed) } func (s *randomBitStream) drawBits(n int) uint64 { assert(n >= 0) var u uint64 if n <= 64 { u = s.ctx.rand() & bitmask64(uint(n)) } else { u = math.MaxUint64 } s.record(u) return u } type bufBitStream struct { buf []uint64 recordedBits } func newBufBitStream(buf []uint64, persist bool) *bufBitStream { s := &bufBitStream{ buf: buf, } s.persist = persist return s } func (s *bufBitStream) drawBits(n int) uint64 { assert(n >= 0) if len(s.buf) == 0 { panic(invalidData("overrun")) } u := s.buf[0] & bitmask64(uint(n)) s.record(u) s.buf = s.buf[1:] return u } type groupInfo struct { begin int end int label string standalone bool discard bool } type recordedBits struct { data []uint64 groups []groupInfo dataLen int persist bool } func (rec *recordedBits) record(u uint64) { if rec.persist { rec.data = append(rec.data, u) } else { rec.dataLen++ } } func (rec *recordedBits) beginGroup(label string, standalone bool) int { if !rec.persist { return rec.dataLen } rec.groups = append(rec.groups, groupInfo{ begin: len(rec.data), end: -1, label: label, standalone: standalone, }) return len(rec.groups) - 1 } func (rec *recordedBits) endGroup(i int, discard bool) { assertf(discard || (!rec.persist && rec.dataLen > i) || (rec.persist && len(rec.data) > rec.groups[i].begin), "group did not use any data from bitstream; this is likely a result of Custom generator not calling any of the built-in generators") if !rec.persist { return } rec.groups[i].end = len(rec.data) rec.groups[i].discard = discard } func (rec *recordedBits) prune() { assert(rec.persist) for i := 0; i < len(rec.groups); { if rec.groups[i].discard { rec.removeGroup(i) // O(n^2) } else { i++ } } for _, g := range rec.groups { assert(g.begin != g.end) } } func (rec *recordedBits) removeGroup(i int) { g := rec.groups[i] assert(g.end >= 0) j := i + 1 for j < len(rec.groups) && rec.groups[j].end <= g.end { j++ } rec.data = append(rec.data[:g.begin], rec.data[g.end:]...) rec.groups = append(rec.groups[:i], rec.groups[j:]...) n := g.end - g.begin for j := range rec.groups { if rec.groups[j].begin >= g.end { rec.groups[j].begin -= n } if rec.groups[j].end >= g.end { rec.groups[j].end -= n } } } // "A Small Noncryptographic PRNG" by Bob Jenkins // See http://www.pcg-random.org/posts/bob-jenkins-small-prng-passes-practrand.html for some recent analysis. type jsf64ctx struct { a uint64 b uint64 c uint64 d uint64 } func (x *jsf64ctx) init(seed uint64) { x.a = 0xf1ea5eed x.b = seed x.c = seed x.d = seed for i := 0; i < 20; i++ { x.rand() } } func (x *jsf64ctx) rand() uint64 { e := x.a - bits.RotateLeft64(x.b, 7) x.a = x.b ^ bits.RotateLeft64(x.c, 13) x.b = x.c + bits.RotateLeft64(x.d, 37) x.c = x.d + e x.d = e + x.a return x.d } rapid-1.3.0/data_test.go000066400000000000000000000037411516251644000151220ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "math/bits" "math/rand" "testing" ) func TestJsfRand(t *testing.T) { t.Parallel() // using https://gist.github.com/imneme/85cff47d4bad8de6bdeb671f9c76c814 golden := [10]uint64{ 0xe7ac7348cb3c6182, 0xe20e62c321f18c3f, 0x592927f9846891ae, 0xda5c2b6e56ace47a, 0x3c5987be726a7740, 0x1463137b89c7292a, 0xd118e05a46bc8156, 0xeb72c3391969bc15, 0xe94f306afee04198, 0x0f57e93805e22a54, } ctx := &jsf64ctx{} ctx.init(0xcafe5eed00000001) for _, g := range golden { u := ctx.rand() if u != g { t.Errorf("0x%x instead of golden 0x%x", u, g) } } } func BenchmarkJsfRand(b *testing.B) { ctx := &jsf64ctx{} ctx.init(1) b.ResetTimer() for i := 0; i < b.N; i++ { ctx.rand() } } func BenchmarkMathRand(b *testing.B) { s := rand.NewSource(1).(rand.Source64) b.ResetTimer() for i := 0; i < b.N; i++ { s.Uint64() } } func TestRandomBitSteam_DrawBits(t *testing.T) { t.Parallel() s := createRandomBitStream(t) for n := 1; n <= 64; n++ { for i := 0; i < 100; i++ { t.Run(fmt.Sprintf("%v bits #%v", n, i), func(t *testing.T) { b := s.drawBits(n) if bits.Len64(b) > n { t.Errorf("%v: bitlen too big for %v bits", b, n) } if bits.OnesCount64(b) > n { t.Errorf("%v: too much ones for %v bits", b, n) } }) } } } func BenchmarkBaseSeed(b *testing.B) { for i := 0; i < b.N; i++ { baseSeed() } } func BenchmarkRandomBitStream_DrawBits1(b *testing.B) { s := newRandomBitStream(baseSeed(), false) b.ResetTimer() for i := 0; i < b.N; i++ { s.drawBits(1) } } func BenchmarkRandomBitStream_DrawBits64(b *testing.B) { s := newRandomBitStream(baseSeed(), false) b.ResetTimer() for i := 0; i < b.N; i++ { s.drawBits(64) } } rapid-1.3.0/doc.go000066400000000000000000000035061516251644000137160ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /* Package rapid implements utilities for property-based testing. [Check] verifies that properties you define hold for a large number of automatically generated test cases. If a failure is found, rapid fails the current test and presents an automatically minimized version of the failing test case. [T.Repeat] is used to construct state machine (sometimes called "stateful" or "model-based") tests. # Generators Primitives: - [Bool] - [Rune], [RuneFrom] - [Byte], [ByteMin], [ByteMax], [ByteRange] - [Int], [IntMin], [IntMax], [IntRange] - [Int8], [Int8Min], [Int8Max], [Int8Range] - [Int16], [Int16Min], [Int16Max], [Int16Range] - [Int32], [Int32Min], [Int32Max], [Int32Range] - [Int64], [Int64Min], [Int64Max], [Int64Range] - [Uint], [UintMin], [UintMax], [UintRange] - [Uint8], [Uint8Min], [Uint8Max], [Uint8Range] - [Uint16], [Uint16Min], [Uint16Max], [Uint16Range] - [Uint32], [Uint32Min], [Uint32Max], [Uint32Range] - [Uint64], [Uint64Min], [Uint64Max], [Uint64Range] - [Uintptr], [UintptrMin], [UintptrMax], [UintptrRange] - [Float32], [Float32Min], [Float32Max], [Float32Range] - [Float64], [Float64Min], [Float64Max], [Float64Range] Collections: - [String], [StringMatching], [StringOf], [StringOfN], [StringN] - [SliceOfBytesMatching] - [SliceOf], [SliceOfN], [SliceOfDistinct], [SliceOfNDistinct] - [Permutation] - [MapOf], [MapOfN], [MapOfValues], [MapOfNValues] User-defined types: - [Custom] - [Make] Other: - [Map], - [Generator.Filter] - [SampledFrom], [Just] - [OneOf] - [Deferred] - [Ptr] */ package rapid rapid-1.3.0/engine.go000066400000000000000000000563641516251644000144300ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "bytes" "context" "encoding/binary" "flag" "fmt" "io" "log" "os" "path/filepath" "regexp" "runtime" "strconv" "strings" "sync" "sync/atomic" "testing" "time" ) const ( small = 5 invalidChecksMult = 10 exampleMaxTries = 1000 maxTestTimeout = 24 * time.Hour shrinkStepBound = 10 * time.Second // can be improved by taking average checkOnce runtime into account tracebackLen = 32 tracebackStop = "pgregory.net/rapid.checkOnce" runtimePrefix = "runtime." ) var ( flags cmdline tracebackBlacklist = map[string]bool{ "pgregory.net/rapid.(*customGen[...]).maybeValue.func1": true, "pgregory.net/rapid.runAction.func1": true, } ) type cmdline struct { checks int steps int failfile string nofailfile bool seed uint64 log bool verbose bool debug bool debugvis bool shrinkTime time.Duration } func init() { defaults := loadCmdlineDefaults(os.LookupEnv) flag.IntVar(&flags.checks, "rapid.checks", defaults.checks, "rapid: number of checks to perform") flag.IntVar(&flags.steps, "rapid.steps", defaults.steps, "rapid: average number of Repeat actions to execute") flag.StringVar(&flags.failfile, "rapid.failfile", defaults.failfile, "rapid: fail file to use to reproduce test failure") flag.BoolVar(&flags.nofailfile, "rapid.nofailfile", defaults.nofailfile, "rapid: do not write fail files on test failures") flag.Uint64Var(&flags.seed, "rapid.seed", defaults.seed, "rapid: PRNG seed to start with (0 to use a random one)") flag.BoolVar(&flags.log, "rapid.log", defaults.log, "rapid: eager verbose output to stdout (to aid with unrecoverable test failures)") flag.BoolVar(&flags.verbose, "rapid.v", defaults.verbose, "rapid: verbose output") flag.BoolVar(&flags.debug, "rapid.debug", defaults.debug, "rapid: debugging output") flag.BoolVar(&flags.debugvis, "rapid.debugvis", defaults.debugvis, "rapid: debugging visualization") flag.DurationVar(&flags.shrinkTime, "rapid.shrinktime", defaults.shrinkTime, "rapid: maximum time to spend on test case minimization") } func defaultCmdline() cmdline { return cmdline{ checks: 100, steps: 30, shrinkTime: 30 * time.Second, } } func loadCmdlineDefaults(lookup func(string) (string, bool)) cmdline { defaults := defaultCmdline() defaults.checks = envInt(lookup, "RAPID_CHECKS", defaults.checks) defaults.steps = envInt(lookup, "RAPID_STEPS", defaults.steps) defaults.failfile = envString(lookup, "RAPID_FAILFILE", defaults.failfile) defaults.nofailfile = envBool(lookup, "RAPID_NOFAILFILE", defaults.nofailfile) defaults.seed = envUint64(lookup, "RAPID_SEED", defaults.seed) defaults.log = envBool(lookup, "RAPID_LOG", defaults.log) defaults.verbose = envBool(lookup, "RAPID_V", defaults.verbose) defaults.debug = envBool(lookup, "RAPID_DEBUG", defaults.debug) defaults.debugvis = envBool(lookup, "RAPID_DEBUGVIS", defaults.debugvis) defaults.shrinkTime = envDuration(lookup, "RAPID_SHRINKTIME", defaults.shrinkTime) return defaults } func envString(lookup func(string) (string, bool), key string, defaultValue string) string { if v, ok := lookup(key); ok { return v } return defaultValue } func envInt(lookup func(string) (string, bool), key string, defaultValue int) int { v, ok := lookup(key) if !ok { return defaultValue } n, err := strconv.ParseInt(v, 0, strconv.IntSize) if err != nil { panicInvalidEnv(key, v, err) } return int(n) } func envBool(lookup func(string) (string, bool), key string, defaultValue bool) bool { v, ok := lookup(key) if !ok { return defaultValue } b, err := strconv.ParseBool(v) if err != nil { panicInvalidEnv(key, v, err) } return b } func envUint64(lookup func(string) (string, bool), key string, defaultValue uint64) uint64 { v, ok := lookup(key) if !ok { return defaultValue } n, err := strconv.ParseUint(v, 0, 64) if err != nil { panicInvalidEnv(key, v, err) } return n } func envDuration(lookup func(string) (string, bool), key string, defaultValue time.Duration) time.Duration { v, ok := lookup(key) if !ok { return defaultValue } d, err := time.ParseDuration(v) if err != nil { panicInvalidEnv(key, v, err) } return d } func panicInvalidEnv(key, value string, err error) { panic(fmt.Sprintf("[rapid] invalid value for %s=%q: %v", key, value, err)) } func assert(ok bool) { if !ok { panic("assertion failed") } } func assertf(ok bool, format string, args ...any) { if !ok { panic(fmt.Sprintf(format, args...)) } } func assertValidRange(min int, max int) { if max >= 0 && min > max { panic(fmt.Sprintf("invalid range [%d, %d]", min, max)) } } func checkDeadline(tb tb) time.Time { t, ok := tb.(*testing.T) if !ok { return time.Now().Add(maxTestTimeout) } d, ok := t.Deadline() if !ok { return time.Now().Add(maxTestTimeout) } return d } func shrinkDeadline(deadline time.Time) time.Time { d := time.Now().Add(flags.shrinkTime) max := deadline.Add(-shrinkStepBound) // account for the fact that shrink deadline is checked before the step if d.After(max) { d = max } return d } // Check fails the current test if rapid can find a test case which falsifies prop. // // Property is falsified in case of a panic or a call to // [*T.Fatalf], [*T.Fatal], [*T.Errorf], [*T.Error], [*T.FailNow] or [*T.Fail]. func Check(t TB, prop func(*T)) { t.Helper() checkTB(t, checkDeadline(t), prop) } // MakeCheck is a convenience function for defining subtests suitable for // [*testing.T.Run]. It allows you to write this: // // t.Run("subtest name", rapid.MakeCheck(func(t *rapid.T) { // // test code // })) // // instead of this: // // t.Run("subtest name", func(t *testing.T) { // rapid.Check(t, func(t *rapid.T) { // // test code // }) // }) func MakeCheck(prop func(*T)) func(*testing.T) { return func(t *testing.T) { t.Helper() checkTB(t, checkDeadline(t), prop) } } // MakeFuzz creates a fuzz target for [*testing.F.Fuzz]: // // func FuzzFoo(f *testing.F) { // f.Fuzz(rapid.MakeFuzz(func(t *rapid.T) { // // test code // })) // } func MakeFuzz(prop func(*T)) func(*testing.T, []byte) { return func(t *testing.T, input []byte) { t.Helper() checkFuzz(t, prop, input) } } func checkFuzz(tb tb, prop func(*T), input []byte) { tb.Helper() var buf []uint64 for len(input) > 0 { var tmp [8]byte n := copy(tmp[:], input) buf = append(buf, binary.LittleEndian.Uint64(tmp[:])) input = input[n:] } t := newT(tb, newBufBitStream(buf, false), true, nil) err := checkOnce(t, prop) switch { case err == nil: // do nothing case err.isInvalidData(): tb.SkipNow() case err.isStopTest(): tb.Fatalf("[rapid] failed: %v", err) default: tb.Fatalf("[rapid] panic: %v\nTraceback:\n%v", err, traceback(err)) } } func checkTB(tb tb, deadline time.Time, prop func(*T)) { tb.Helper() checks := flags.checks if testing.Short() { checks /= 5 } start := time.Now() valid, invalid, earlyExit, seed, failfile, buf, err1, err2 := doCheck(tb, deadline, checks, baseSeed(), flags.failfile, true, prop) dt := time.Since(start) if err1 == nil && err2 == nil { if valid == checks || (earlyExit && valid > 0) { tb.Logf("[rapid] OK, passed %v tests (%v)", valid, dt) } else { tb.Errorf("[rapid] only generated %v valid tests from %v total (%v)", valid, valid+invalid, dt) } } else { if failfile == "" && !flags.nofailfile { _, failfile = failFileName(tb.Name()) out := captureTestOutput(tb, prop, buf) err := saveFailFile(failfile, rapidVersion, out, seed, buf) if err != nil { tb.Logf("[rapid] %v", err) failfile = "" } } var repr string switch { case failfile != "" && seed != 0: repr = fmt.Sprintf("-rapid.failfile=%q (or -rapid.seed=%d)", failfile, seed) case failfile != "": repr = fmt.Sprintf("-rapid.failfile=%q", failfile) case seed != 0: repr = fmt.Sprintf("-rapid.seed=%d", seed) } name := regexp.QuoteMeta(tb.Name()) if traceback(err1) == traceback(err2) { if err2.isStopTest() { tb.Errorf("[rapid] failed after %v tests: %v\nTo reproduce, specify -run=%q %v\nFailed test output:", valid, err2, name, repr) } else { tb.Errorf("[rapid] panic after %v tests: %v\nTo reproduce, specify -run=%q %v\nTraceback:\n%vFailed test output:", valid, err2, name, repr, traceback(err2)) } } else { tb.Errorf("[rapid] flaky test, can not reproduce a failure\nTo try to reproduce, specify -run=%q %v\nTraceback (%v):\n%vOriginal traceback (%v):\n%vFailed test output:", name, repr, err2, traceback(err2), err1, traceback(err1)) } _ = checkOnce(newT(tb, newBufBitStream(buf, false), true, nil), prop) // output using (*testing.T).Log for proper line numbers } if tb.Failed() { tb.FailNow() // do not try to run any checks after the first failed one } } func doCheck(tb tb, deadline time.Time, checks int, seed uint64, failfile string, globFailFiles bool, prop func(*T)) (int, int, bool, uint64, string, []uint64, *testError, *testError) { tb.Helper() assertf(!tb.Failed(), "check function called with *testing.T which has already failed") var failfiles []string if failfile != "" { failfiles = []string{failfile} } if globFailFiles { matches, _ := filepath.Glob(failFilePattern(tb.Name())) failfiles = append(failfiles, matches...) } for _, failfile := range failfiles { buf, err1, err2 := checkFailFile(tb, failfile, prop) if err1 != nil || err2 != nil { return 0, 0, false, 0, failfile, buf, err1, err2 } } valid, invalid, earlyExit, seed, err1 := findBug(tb, deadline, checks, seed, prop) if err1 == nil { return valid, invalid, earlyExit, 0, "", nil, nil, nil } s := newRandomBitStream(seed, true) t := newT(tb, s, flags.verbose, nil) t.Logf("[rapid] trying to reproduce the failure") err2 := checkOnce(t, prop) if !sameError(err1, err2) { return valid, invalid, false, seed, "", s.data, err1, err2 } t.Logf("[rapid] trying to minimize the failing test case") buf, err3 := shrink(tb, shrinkDeadline(deadline), s.recordedBits, err2, prop) return valid, invalid, false, seed, "", buf, err2, err3 } func checkFailFile(tb tb, failfile string, prop func(*T)) ([]uint64, *testError, *testError) { tb.Helper() version, _, buf, err := loadFailFile(failfile) if err != nil { tb.Logf("[rapid] ignoring fail file: %v", err) return nil, nil, nil } if version != rapidVersion { tb.Logf("[rapid] ignoring fail file: version %q differs from rapid version %q", version, rapidVersion) return nil, nil, nil } s1 := newBufBitStream(buf, false) t1 := newT(tb, s1, flags.verbose, nil) err1 := checkOnce(t1, prop) if err1 == nil { return nil, nil, nil } if err1.isInvalidData() { tb.Logf("[rapid] fail file %q is no longer valid", failfile) return nil, nil, nil } s2 := newBufBitStream(buf, false) t2 := newT(tb, s2, flags.verbose, nil) t2.Logf("[rapid] trying to reproduce the failure") err2 := checkOnce(t2, prop) return buf, err1, err2 } func findBug(tb tb, deadline time.Time, checks int, seed uint64, prop func(*T)) (int, int, bool, uint64, *testError) { tb.Helper() var ( r = newRandomBitStream(0, false) t = newT(tb, r, flags.verbose, nil) valid = 0 invalid = 0 ) var total time.Duration for valid < checks && invalid < checks*invalidChecksMult { iter := valid + invalid if iter > 0 && time.Until(deadline) < total/time.Duration(iter)*5 { if t.shouldLog() { t.Logf("[rapid] early exit after test #%v (%v)", iter, total) } return valid, invalid, true, 0, nil } seed += uint64(iter) r.init(seed) start := time.Now() if t.shouldLog() { t.Logf("[rapid] test #%v start (seed %v)", iter+1, seed) } err := checkOnce(t, prop) dt := time.Since(start) total += dt if err == nil { if t.shouldLog() { t.Logf("[rapid] test #%v OK (%v)", iter+1, dt) } valid++ } else if err.isInvalidData() { if t.shouldLog() { t.Logf("[rapid] test #%v invalid (%v)", iter+1, dt) } invalid++ } else { if t.shouldLog() { t.Logf("[rapid] test #%v failed: %v", iter+1, err) } return valid, invalid, false, seed, err } } return valid, invalid, false, 0, nil } func checkOnce(t *T, prop func(*T)) (err *testError) { if t.tbLog { t.tb.Helper() } defer func() { err = panicToError(recover(), 3) }() defer t.cleanup() prop(t) t.failOnError() return nil } func captureTestOutput(tb tb, prop func(*T), buf []uint64) []byte { var b bytes.Buffer l := log.New(&b, fmt.Sprintf("[%v] ", tb.Name()), log.Lmsgprefix|log.Ldate|log.Ltime|log.Lmicroseconds) _ = checkOnce(newT(tb, newBufBitStream(buf, false), false, l), prop) return b.Bytes() } type invalidData string type stopTest string type testError struct { data any traceback string } func panicToError(p any, skip int) *testError { if p == nil { return nil } if err, ok := p.(*testError); ok { return err } callers := make([]uintptr, tracebackLen) callers = callers[:runtime.Callers(skip, callers)] frames := runtime.CallersFrames(callers) b := &strings.Builder{} f, more, skipSpecial := runtime.Frame{}, true, true for more && !strings.HasSuffix(f.Function, tracebackStop) { f, more = frames.Next() if skipSpecial && (tracebackBlacklist[f.Function] || strings.HasPrefix(f.Function, runtimePrefix)) { continue } skipSpecial = false _, err := fmt.Fprintf(b, " %s:%d in %s\n", f.File, f.Line, f.Function) assert(err == nil) } return &testError{ data: p, traceback: b.String(), } } func (err *testError) Error() string { if msg, ok := err.data.(stopTest); ok { return string(msg) } if msg, ok := err.data.(invalidData); ok { return fmt.Sprintf("invalid data: %s", string(msg)) } return fmt.Sprintf("%v", err.data) } func (err *testError) isInvalidData() bool { _, ok := err.data.(invalidData) return ok } func (err *testError) isStopTest() bool { _, ok := err.data.(stopTest) return ok } func sameError(err1 *testError, err2 *testError) bool { return errorString(err1) == errorString(err2) && traceback(err1) == traceback(err2) } func errorString(err *testError) string { if err == nil { return "" } return err.Error() } func traceback(err *testError) string { if err == nil { return " \n" } return err.traceback } // TB is a common interface between [*testing.T], [*testing.B] and [*T]. type TB interface { Helper() Name() string Logf(format string, args ...any) Log(args ...any) Skipf(format string, args ...any) Skip(args ...any) SkipNow() Errorf(format string, args ...any) Error(args ...any) Fatalf(format string, args ...any) Fatal(args ...any) FailNow() Fail() Failed() bool } type tb TB // tb is a private copy of TB, made to avoid T having public fields type nilTB struct{} func (nilTB) Helper() {} func (nilTB) Name() string { return "" } func (nilTB) Logf(string, ...any) {} func (nilTB) Log(...any) {} func (nilTB) Skipf(string, ...any) { panic("call to TB.Skipf() outside a test") } func (nilTB) Skip(...any) { panic("call to TB.Skip() outside a test") } func (nilTB) SkipNow() { panic("call to TB.SkipNow() outside a test") } func (nilTB) Errorf(string, ...any) { panic("call to TB.Errorf() outside a test") } func (nilTB) Error(...any) { panic("call to TB.Error() outside a test") } func (nilTB) Fatalf(string, ...any) { panic("call to TB.Fatalf() outside a test") } func (nilTB) Fatal(...any) { panic("call to TB.Fatal() outside a test") } func (nilTB) FailNow() { panic("call to TB.FailNow() outside a test") } func (nilTB) Fail() { panic("call to TB.Fail() outside a test") } func (nilTB) Failed() bool { panic("call to TB.Failed() outside a test") } // T is similar to [testing.T], but with extra bookkeeping for property-based tests. // // For tests to be reproducible, they should generally run in a single goroutine. // If concurrency is unavoidable, methods on *T, such as [*testing.T.Helper] and [*T.Errorf], // are safe for concurrent calls, but *Generator.Draw from a given *T is not. type T struct { tb // unnamed to force re-export of (*T).Helper() ctx context.Context cancelCtx context.CancelFunc cleanups []func() cleaning atomic.Bool tbLog bool rawLog *log.Logger s bitStream draws int refDraws []any mu sync.RWMutex failed stopTest } func newT(tb tb, s bitStream, tbLog bool, rawLog *log.Logger, refDraws ...any) *T { if tb == nil { tb = nilTB{} } t := &T{ tb: tb, tbLog: tbLog, rawLog: rawLog, s: s, refDraws: refDraws, } if rawLog == nil && flags.log { testName := "rapid test" if tb != nil { testName = tb.Name() } t.rawLog = log.New(os.Stdout, fmt.Sprintf("[%v] ", testName), log.Lmsgprefix|log.Ldate|log.Ltime|log.Lmicroseconds) } return t } func (t *T) shouldLog() bool { return t.rawLog != nil || t.tbLog } // Context returns a context.Context that is canceled // after the property function exits, // before Cleanup-registered functions are run. // // For [Check], [MakeFuzz], and similar functions, // each call to the property function gets a unique context // that is canceled after that property function exits. // // For [Custom], each time a new value is generated, // the generator function gets a unique context // that is canceled after the generator function exits. func (t *T) Context() context.Context { // Fast path: no need to lock if the context is already set. t.mu.RLock() ctx := t.ctx t.mu.RUnlock() if ctx != nil { return ctx } // If we're in the middle of cleaning up // and the context has already been canceled and cleared, // don't create a new one. Return a canceled context instead. if t.cleaning.Load() { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx } // Slow path: lock and check again, create new context if needed. t.mu.Lock() defer t.mu.Unlock() if t.ctx != nil { // Another goroutine set the context // while we were waiting for the lock. return t.ctx } // Use the testing.TB's context as the starting point if available, // and the Background context if not. // // T.Context was added in Go 1.24. if tctx, ok := t.tb.(interface{ Context() context.Context }); ok { ctx = tctx.Context() } else { ctx = context.Background() } ctx, cancel := context.WithCancel(ctx) t.ctx = ctx t.cancelCtx = cancel return ctx } // Cleanup registers a function to be called // when a property function finishes running. // // For [Check], [MakeFuzz], and similar functions, // each call to the property function registers its cleanup functions, // which are called after the property function exits. // // For [Custom], each time a new value is generated, // the generator function registers its cleanup functions, // which are called after the generator function exits. // // Cleanup functions are called in last-in, first-out order. // // If [T.Context] is used, the context is canceled // before the Cleanup functions are executed. func (t *T) Cleanup(f func()) { t.mu.Lock() defer t.mu.Unlock() t.cleanups = append(t.cleanups, f) } // cleanup runs any cleanup tasks associated with the property check. // It is safe to call multiple times. func (t *T) cleanup() { t.cleaning.Store(true) defer t.cleaning.Store(false) // If a cleanup function panics, // we still want to run the remaining cleanup functions. defer func() { t.mu.Lock() recurse := len(t.cleanups) > 0 t.mu.Unlock() if recurse { t.cleanup() } }() // Context must be closed before t.Cleanup functions are run. t.mu.Lock() if t.cancelCtx != nil { t.cancelCtx() t.cancelCtx = nil t.ctx = nil } t.mu.Unlock() for { var cleanup func() t.mu.Lock() if len(t.cleanups) > 0 { last := len(t.cleanups) - 1 cleanup = t.cleanups[last] t.cleanups = t.cleanups[:last] } t.mu.Unlock() if cleanup == nil { break } cleanup() } } func (t *T) Logf(format string, args ...any) { if t.rawLog != nil { t.rawLog.Printf(format, args...) } else if t.tbLog { t.tb.Helper() t.tb.Logf(format, args...) } } func (t *T) Log(args ...any) { if t.rawLog != nil { t.rawLog.Print(args...) } else if t.tbLog { t.tb.Helper() t.tb.Log(args...) } } // Output returns a Writer that writes to the same test output stream as T.Log. // The output is indented like T.Log lines, but Output does not // add source locations or newlines. The output is internally line // buffered, and a call to T.Log or the end of the test will implicitly // flush the buffer, followed by a newline. After a test function and all its // parents return, neither Output nor the Write method may be called. // // Only available on Go >= 1.25 func (t *T) Output() io.Writer { t.Helper() if t.rawLog != nil { return t.rawLog.Writer() } else if t.tbLog { if tout, ok := t.tb.(interface{ Output() io.Writer }); ok { return tout.Output() } else { t.Fatal("[rapid] Output requires Go 1.25 or newer") return nil } } else { return io.Discard } } // Skipf is equivalent to [T.Logf] followed by [T.SkipNow]. func (t *T) Skipf(format string, args ...any) { if t.tbLog { t.tb.Helper() } t.Logf(format, args...) t.skip(fmt.Sprintf(format, args...)) } // Skip is equivalent to [T.Log] followed by [T.SkipNow]. func (t *T) Skip(args ...any) { if t.tbLog { t.tb.Helper() } t.Log(args...) t.skip(fmt.Sprint(args...)) } // SkipNow marks the current test case as invalid (except in [T.Repeat] // actions, where it marks current action as non-applicable instead). // If too many test cases are skipped, rapid will mark the test as failing // due to inability to generate enough valid test cases. // // The test case or action will be treated like it had never been drawn // and will not be shown in test logs. // Therefore, to avoid confusing test failures later on, // [SkipNow] must not be called after the action has already mutated shared state. // // Prefer *Generator.Filter to SkipNow, and prefer generators that always produce // valid test cases to Filter. func (t *T) SkipNow() { t.skip("(*T).SkipNow() called") } // Errorf is equivalent to [T.Logf] followed by [T.Fail]. func (t *T) Errorf(format string, args ...any) { if t.tbLog { t.tb.Helper() } t.Logf(format, args...) t.fail(false, fmt.Sprintf(format, args...)) } // Error is equivalent to [T.Log] followed by [T.Fail]. func (t *T) Error(args ...any) { if t.tbLog { t.tb.Helper() } t.Log(args...) t.fail(false, fmt.Sprint(args...)) } // Fatalf is equivalent to [T.Logf] followed by [T.FailNow]. func (t *T) Fatalf(format string, args ...any) { if t.tbLog { t.tb.Helper() } t.Logf(format, args...) t.fail(true, fmt.Sprintf(format, args...)) } // Fatal is equivalent to [T.Log] followed by [T.FailNow]. func (t *T) Fatal(args ...any) { if t.tbLog { t.tb.Helper() } t.Log(args...) t.fail(true, fmt.Sprint(args...)) } func (t *T) FailNow() { t.fail(true, "(*T).FailNow() called") } func (t *T) Fail() { t.fail(false, "(*T).Fail() called") } func (t *T) Failed() bool { t.mu.RLock() defer t.mu.RUnlock() return t.failed != "" } func (t *T) skip(msg string) { panic(invalidData(msg)) } func (t *T) fail(now bool, msg string) { t.mu.Lock() defer t.mu.Unlock() t.failed = stopTest(msg) if now { panic(t.failed) } } func (t *T) failOnError() { t.mu.RLock() defer t.mu.RUnlock() if t.failed != "" { panic(t.failed) } } rapid-1.3.0/engine_env_test.go000066400000000000000000000032741516251644000163270ustar00rootroot00000000000000package rapid import ( "testing" "time" ) func TestLoadCmdlineDefaultsUsesBaseValues(t *testing.T) { got := loadCmdlineDefaults(func(string) (string, bool) { return "", false }) want := defaultCmdline() if got != want { t.Fatalf("defaults mismatch: got %+v, want %+v", got, want) } } func TestLoadCmdlineDefaultsFromEnv(t *testing.T) { env := map[string]string{ "RAPID_CHECKS": "0xc8", "RAPID_STEPS": "40_000", "RAPID_FAILFILE": "/tmp/failfile", "RAPID_NOFAILFILE": "true", "RAPID_SEED": "0x1234", "RAPID_LOG": "true", "RAPID_V": "true", "RAPID_DEBUG": "true", "RAPID_DEBUGVIS": "true", "RAPID_SHRINKTIME": "45s", } got := loadCmdlineDefaults(func(key string) (string, bool) { value, ok := env[key] return value, ok }) if got.checks != 200 { t.Fatalf("checks: got %d, want %d", got.checks, 200) } if got.steps != 40000 { t.Fatalf("steps: got %d, want %d", got.steps, 40000) } if got.failfile != "/tmp/failfile" { t.Fatalf("failfile: got %q, want %q", got.failfile, "/tmp/failfile") } if !got.nofailfile { t.Fatalf("nofailfile: expected true") } if got.seed != 0x1234 { t.Fatalf("seed: got %d, want %d", got.seed, 0x1234) } if !got.log || !got.verbose || !got.debug || !got.debugvis { t.Fatalf("expected all bool flags true, got %+v", got) } if got.shrinkTime != 45*time.Second { t.Fatalf("shrinkTime: got %v, want %v", got.shrinkTime, 45*time.Second) } } func TestLoadCmdlineDefaultsInvalidEnvPanics(t *testing.T) { defer func() { if r := recover(); r == nil { t.Fatal("expected panic for invalid env value") } }() loadCmdlineDefaults(func(string) (string, bool) { return "not-an-int", true }) } rapid-1.3.0/engine_fuzz_test.go000066400000000000000000000046511516251644000165350ustar00rootroot00000000000000// Copyright 2022 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "context" "testing" . "pgregory.net/rapid" ) func checkInt(t *T) { answer := Int().Draw(t, "answer") if answer == 42 { t.Fatalf("fuzzing works") } } func checkSlice(t *T) { slice := SliceOfN(Int(), 5, 5).Draw(t, "slice") if slice[0] < slice[1] && slice[1] < slice[2] && slice[2] < slice[3] && slice[3] < slice[4] { t.Fatalf("fuzzing works") } } func checkString(t *T) { hello := String().Draw(t, "hello") if hello == "world" { t.Fatalf("fuzzing works") } } func checkStuckStateMachine(t *T) { die := 0 t.Repeat(map[string]func(*T){ "roll": func(t *T) { if die == 6 { t.Skip("game over") } die = IntRange(1, 6).Draw(t, "die") }, }) } func TestRapidInt(t *testing.T) { t.Skip() Check(t, checkInt) } func TestRapidSlice(t *testing.T) { t.Skip() Check(t, checkSlice) } func TestRapidString(t *testing.T) { t.Skip() Check(t, checkString) } func TestRapidStuckStateMachine(t *testing.T) { t.Skip() Check(t, checkStuckStateMachine) } func FuzzInt(f *testing.F) { f.Fuzz(MakeFuzz(checkInt)) } func FuzzSlice(f *testing.F) { f.Fuzz(MakeFuzz(checkSlice)) } func FuzzString(f *testing.F) { f.Fuzz(MakeFuzz(checkString)) } func FuzzStuckStateMachine(f *testing.F) { f.Fuzz(MakeFuzz(checkStuckStateMachine)) } func FuzzContext(f *testing.F) { type key struct{} var ctx context.Context f.Fuzz(MakeFuzz(func(t *T) { // Assign to outer variable // so we can check it after the fuzzing. ctx = context.WithValue(t.Context(), key{}, "value") if err := ctx.Err(); err != nil { t.Fatalf("context must be valid: %v", err) } })) // ctx is set only if the fuzzing function was called. if ctx != nil { if err := ctx.Err(); err == nil { f.Fatalf("context must be canceled") } if want, got := "value", ctx.Value(key{}); want != got { f.Fatalf("context must have value %q, got %q", want, got) } } } func FuzzCleanup(f *testing.F) { var state []bool f.Fuzz(MakeFuzz(func(t *T) { idx := len(state) state = append(state, false) t.Cleanup(func() { state[idx] = true }) })) for _, ok := range state { if !ok { f.Fatalf("cleanup must be called") } } } rapid-1.3.0/engine_test.go000066400000000000000000000136521516251644000154600ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "bytes" "context" "errors" "reflect" "strings" "testing" ) func brokenGen(*T) int { panic("this generator is not working") } type brokenMachine struct{} func (m *brokenMachine) DoNothing(_ *T) { panic("this state machine is not working") } func (m *brokenMachine) Check(_ *T) {} func TestPanicTraceback(t *testing.T) { t.Parallel() testData := []struct { name string suffix string canSucceed bool fail func(*T) *testError }{ { "impossible filter", "pgregory.net/rapid.find[...]", false, func(t *T) *testError { g := Bool().Filter(func(bool) bool { return false }) _, err := recoverValue(g, t) return err }, }, { "broken custom generator", "pgregory.net/rapid.brokenGen", false, func(t *T) *testError { g := Custom(brokenGen) _, err := recoverValue(g, t) return err }, }, { "broken state machine", "pgregory.net/rapid.(*brokenMachine).DoNothing", true, func(t *T) *testError { return checkOnce(t, func(t *T) { var sm brokenMachine t.Repeat(StateMachineActions(&sm)) }) }, }, } for _, td := range testData { t.Run(td.name, func(t *testing.T) { s := createRandomBitStream(t) nt := newT(t, s, false, nil) err := td.fail(nt) if err == nil { if td.canSucceed { t.SkipNow() } t.Fatalf("test case did not fail") } lines := strings.Split(err.traceback, "\n") if !strings.HasSuffix(lines[0], td.suffix) { t.Errorf("bad traceback:\n%v", err.traceback) } }) } } func BenchmarkCheckOverhead(b *testing.B) { g := Uint() f := func(t *T) { g.Draw(t, "") } deadline := checkDeadline(nil) b.ResetTimer() for i := 0; i < b.N; i++ { checkTB(b, deadline, f) } } func TestCheckContext(t *testing.T) { t.Parallel() type key struct{} var ctx context.Context Check(t, func(t *T) { ctx = context.WithValue(t.Context(), key{}, Int().Draw(t, "x")) if err := ctx.Err(); err != nil { t.Fatalf("unexpected error: %v", err) } }) if err := ctx.Err(); err == nil || !errors.Is(err, context.Canceled) { t.Fatalf("expected context to be canceled, got: %v", err) } if _, ok := ctx.Value(key{}).(int); !ok { t.Fatalf("context must have a value") } } func TestCheckCleanup(t *testing.T) { t.Parallel() // Each Check iteration will append a true to indicate "open", // and flip it to false on cleanup. // // After the check is done, we expect all values to be false. var state []bool Check(t, func(t *T) { idx := len(state) state = append(state, true) t.Cleanup(func() { state[idx] = false }) }) for _, v := range state { if v { t.Fatalf("expected all values to be false") } } } func TestCheckCleanupMultipleOrder(t *testing.T) { t.Parallel() // If multiple cleanups are appended during a Check, // they must run in reverse order. var state []int Check(t, func(t *T) { // We just want to capture the result of one iteration, // so we'll keep resetting the state. state = nil t.Cleanup(func() { state = append(state, 1) }) t.Cleanup(func() { state = append(state, 2) }) t.Cleanup(func() { state = append(state, 3) }) }) if !reflect.DeepEqual(state, []int{3, 2, 1}) { t.Fatalf("expected cleanups to run in reverse order, got: %v", state) } } func TestCheckCleanupPanic(t *testing.T) { t.Parallel() // A Cleanup function halfway through will panic. // Deferred assertions will check that all values are false. var state []bool defer func() { for _, v := range state { if v { t.Errorf("expected all values to be false") } } }() Check(ignoreErrorsTB{t}, func(t *T) { idx := len(state) state = append(state, true) t.Cleanup(func() { state[idx] = false if idx == len(state)/2 { panic("cleanup panic") } }) }) } func TestCheckCleanupNewCleanupsDuringCleanup(t *testing.T) { t.Parallel() // Cleanups can be added during cleanup. var state []bool Check(t, func(t *T) { idx := len(state) state = append(state, true) t.Cleanup(func() { // Odd numbered events will add a new cleanup. if idx%2 == 0 { state[idx] = false } else { t.Cleanup(func() { state[idx] = false }) } }) }) } func TestCheckCleanupContextIsCanceled(t *testing.T) { t.Parallel() // Context created during Check is canceled by the time Cleanup is run. Check(t, func(t *T) { ctx := t.Context() t.Cleanup(func() { if err := ctx.Err(); err == nil || !errors.Is(err, context.Canceled) { t.Fatalf("expected context to be canceled, got: %v", ctx) } }) }) } func TestCheckCleanupContextCreatedInCleanup(t *testing.T) { t.Parallel() // Context created during Cleanup is already canceled. Check(t, func(t *T) { ctx := t.Context() t.Cleanup(func() { // ctx is already cleared on rapid.T by now, // so this will request a new context. newCtx := t.Context() if ctx == newCtx { t.Fatalf("expected new context") } if err := newCtx.Err(); err == nil || !errors.Is(err, context.Canceled) { t.Fatalf("expected context to be canceled, got: %v", newCtx) } }) }) } func TestOutputRawLog(t *testing.T) { t.Parallel() msg := []byte("Hello World") out := captureTestOutput(t, func(t *T) { t.Output().Write(msg) }, nil) if !bytes.Contains(out, msg) { t.Errorf("expected output to contain %q, got: %q", msg, out) } } // ignoreErrorsTB is a TB that ignores all errors posted to it. type ignoreErrorsTB struct{ TB } func (ignoreErrorsTB) Error(...interface{}) {} func (ignoreErrorsTB) Errorf(string, ...interface{}) {} func (ignoreErrorsTB) Fatal(...interface{}) {} func (ignoreErrorsTB) Fatalf(string, ...interface{}) {} func (ignoreErrorsTB) Fail() {} rapid-1.3.0/example_function_test.go000066400000000000000000000031041516251644000175420ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "fmt" "strconv" "testing" "pgregory.net/rapid" ) // ParseDate parses dates in the YYYY-MM-DD format. func ParseDate(s string) (int, int, int, error) { if len(s) != 10 { return 0, 0, 0, fmt.Errorf("%q has wrong length: %v instead of 10", s, len(s)) } if s[4] != '-' || s[7] != '-' { return 0, 0, 0, fmt.Errorf("'-' separators expected in %q", s) } y, err := strconv.Atoi(s[0:4]) if err != nil { return 0, 0, 0, fmt.Errorf("failed to parse year: %v", err) } m, err := strconv.Atoi(s[6:7]) if err != nil { return 0, 0, 0, fmt.Errorf("failed to parse month: %v", err) } d, err := strconv.Atoi(s[8:10]) if err != nil { return 0, 0, 0, fmt.Errorf("failed to parse day: %v", err) } return y, m, d, nil } func testParseDate(t *rapid.T) { y := rapid.IntRange(0, 9999).Draw(t, "y") m := rapid.IntRange(1, 12).Draw(t, "m") d := rapid.IntRange(1, 31).Draw(t, "d") s := fmt.Sprintf("%04d-%02d-%02d", y, m, d) y_, m_, d_, err := ParseDate(s) if err != nil { t.Fatalf("failed to parse date %q: %v", s, err) } if y_ != y || m_ != m || d_ != d { t.Fatalf("got back wrong date: (%d, %d, %d)", y_, m_, d_) } } // Rename to TestParseDate(t *testing.T) to make an actual (failing) test. func ExampleCheck_parseDate() { var t *testing.T rapid.Check(t, testParseDate) } rapid-1.3.0/example_statemachine_test.go000066400000000000000000000033471516251644000203730ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "testing" "pgregory.net/rapid" ) // Queue implements integer queue with a fixed maximum size. type Queue struct { buf []int in int out int } func NewQueue(n int) *Queue { return &Queue{ buf: make([]int, n+1), } } // Precondition: Size() > 0. func (q *Queue) Get() int { i := q.buf[q.out] q.out = (q.out + 1) % len(q.buf) return i } // Precondition: Size() < n. func (q *Queue) Put(i int) { q.buf[q.in] = i q.in = (q.in + 1) % len(q.buf) } func (q *Queue) Size() int { return (q.in - q.out) % len(q.buf) } func testQueue(t *rapid.T) { n := rapid.IntRange(1, 1000).Draw(t, "n") // maximum queue size q := NewQueue(n) // queue being tested var state []int // model of the queue t.Repeat(map[string]func(*rapid.T){ "get": func(t *rapid.T) { if q.Size() == 0 { t.Skip("queue empty") } i := q.Get() if i != state[0] { t.Fatalf("got invalid value: %v vs expected %v", i, state[0]) } state = state[1:] }, "put": func(t *rapid.T) { if q.Size() == n { t.Skip("queue full") } i := rapid.Int().Draw(t, "i") q.Put(i) state = append(state, i) }, "": func(t *rapid.T) { if q.Size() != len(state) { t.Fatalf("queue size mismatch: %v vs expected %v", q.Size(), len(state)) } }, }) } // Rename to TestQueue(t *testing.T) to make an actual (failing) test. func ExampleT_Repeat_queue() { var t *testing.T rapid.Check(t, testQueue) } rapid-1.3.0/failure_external_test.go000066400000000000000000000072031516251644000175370ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "math" "testing" . "pgregory.net/rapid" ) // wrapper to test (*T).Helper() func fatalf(t *T, format string, args ...any) { t.Helper() t.Fatalf(format, args...) } func TestFailure_ImpossibleData(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { _ = Int().Filter(func(i int) bool { return false }).Draw(t, "i") }) } func TestFailure_Trivial(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { i := Int().Draw(t, "i") if i > 1000000000 { fatalf(t, "got a huge integer: %v", i) } }) } func TestFailure_SimpleCollection(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { s := SliceOf(Int().Filter(func(i int) bool { return i%2 == -1 })).Draw(t, "s") if len(s) > 3 { fatalf(t, "got a long sequence: %v", s) } }) } func TestFailure_CollectionElements(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { s := SliceOfN(Int(), 2, -1).Draw(t, "s") n := 0 for _, i := range s { if i > 1000000 { n++ } } if n > 1 { fatalf(t, "got %v huge elements", n) } }) } func TestFailure_TrivialString(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { s := String().Draw(t, "s") if len(s) > 7 { fatalf(t, "got bad string %v", s) } }) } func TestFailure_Make(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { n := IntMin(0).Draw(t, "n") _ = make([]int, n) }) } func TestFailure_Mean(t *testing.T) { t.Skip("expected failure") Check(t, func(t *T) { s := SliceOf(Float64()).Draw(t, "s") mean := 0.0 for _, f := range s { mean += f } mean /= float64(len(s)) min, max := math.Inf(0), math.Inf(-1) for _, f := range s { if f < min { min = f } if f > max { max = f } } if mean < min || mean > max { t.Fatalf("got mean %v for range [%v, %v]", mean, min, max) } }) } func TestFailure_ExampleParseDate(t *testing.T) { t.Skip("expected failure") Check(t, testParseDate) } func TestFailure_ExampleQueue(t *testing.T) { t.Skip("expected failure") Check(t, testQueue) } // LastIndex returns the index of the last instance of x in list, or // -1 if x is not present. The loop condition has a fault that // causes some tests to fail. Change it to i >= 0 to see them pass. func LastIndex(list []int, x int) int { for i := len(list) - 1; i > 0; i-- { if list[i] == x { return i } } return -1 } // This can be a good example of property-based test; however, it is unclear // what is the "best" way to generate input. Either it is concise (like in // the test below), but requires high quality data generation (and even then // is flaky), or it can be verbose, explicitly covering important input classes -- // however, how do we know them when writing a test? func TestFailure_LastIndex(t *testing.T) { t.Skip("expected failure (flaky)") Check(t, func(t *T) { s := SliceOf(Int()).Draw(t, "s") x := Int().Draw(t, "x") ix := LastIndex(s, x) // index is either -1 or in bounds if ix != -1 && (ix < 0 || ix >= len(s)) { t.Fatalf("%v is not a valid last index", ix) } // index is either -1 or a valid index of x if ix != -1 && s[ix] != x { t.Fatalf("%v is not a valid index of %v", ix, x) } // no valid index of x is bigger than ix for i := ix + 1; i < len(s); i++ { if s[i] == x { t.Fatalf("%v is not the last index of %v (%v is bigger)", ix, x, i) } } }) } rapid-1.3.0/floats.go000066400000000000000000000165741516251644000144520ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "math" "math/bits" ) const ( float32ExpBits = 8 float32SignifBits = 23 float64ExpBits = 11 float64SignifBits = 52 floatExpLabel = "floatexp" floatSignifLabel = "floatsignif" ) // Float32 is a shorthand for [Float32Range](-[math.MaxFloat32], [math.MaxFloat32]). func Float32() *Generator[float32] { return Float32Range(-math.MaxFloat32, math.MaxFloat32) } // Float32Min is a shorthand for [Float32Range](min, [math.MaxFloat32]). func Float32Min(min float32) *Generator[float32] { return Float32Range(min, math.MaxFloat32) } // Float32Max is a shorthand for [Float32Range](-[math.MaxFloat32], max). func Float32Max(max float32) *Generator[float32] { return Float32Range(-math.MaxFloat32, max) } // Float32Range creates a generator of 32-bit floating-point numbers in range [min, max]. // Both min and max can be infinite. func Float32Range(min float32, max float32) *Generator[float32] { assertf(min == min, "min should not be a NaN") assertf(max == max, "max should not be a NaN") assertf(min <= max, "invalid range [%v, %v]", min, max) return newGenerator[float32](&float32Gen{ floatGen{ min: float64(min), max: float64(max), minVal: -math.MaxFloat32, maxVal: math.MaxFloat32, }, }) } // Float64 is a shorthand for [Float64Range](-[math.MaxFloat64], [math.MaxFloat64]). func Float64() *Generator[float64] { return Float64Range(-math.MaxFloat64, math.MaxFloat64) } // Float64Min is a shorthand for [Float64Range](min, [math.MaxFloat64]). func Float64Min(min float64) *Generator[float64] { return Float64Range(min, math.MaxFloat64) } // Float64Max is a shorthand for [Float64Range](-[math.MaxFloat64], max). func Float64Max(max float64) *Generator[float64] { return Float64Range(-math.MaxFloat64, max) } // Float64Range creates a generator of 64-bit floating-point numbers in range [min, max]. // Both min and max can be infinite. func Float64Range(min float64, max float64) *Generator[float64] { assertf(min == min, "min should not be a NaN") assertf(max == max, "max should not be a NaN") assertf(min <= max, "invalid range [%v, %v]", min, max) return newGenerator[float64](&float64Gen{ floatGen{ min: min, max: max, minVal: -math.MaxFloat64, maxVal: math.MaxFloat64, }, }) } type floatGen struct { min float64 max float64 minVal float64 maxVal float64 } type float32Gen struct{ floatGen } type float64Gen struct{ floatGen } func (g *floatGen) stringImpl(kind string) string { if g.min != g.minVal && g.max != g.maxVal { return fmt.Sprintf("%sRange(%g, %g)", kind, g.min, g.max) } else if g.min != g.minVal { return fmt.Sprintf("%sMin(%g)", kind, g.min) } else if g.max != g.maxVal { return fmt.Sprintf("%sMax(%g)", kind, g.max) } return fmt.Sprintf("%s()", kind) } func (g *float32Gen) String() string { return g.stringImpl("Float32") } func (g *float64Gen) String() string { return g.stringImpl("Float64") } func (g *float32Gen) value(t *T) float32 { return float32FromParts(genFloatRange(t.s, g.min, g.max, float32SignifBits)) } func (g *float64Gen) value(t *T) float64 { return float64FromParts(genFloatRange(t.s, g.min, g.max, float64SignifBits)) } func ufloatFracBits(e int32, signifBits uint) uint { if e <= 0 { return signifBits } else if uint(e) < signifBits { return signifBits - uint(e) } else { return 0 } } func ufloat32Parts(f float32) (int32, uint64, uint64) { u := math.Float32bits(f) & math.MaxInt32 e := int32(u>>float32SignifBits) - int32(bitmask64(float32ExpBits-1)) s := uint64(u) & bitmask64(float32SignifBits) n := ufloatFracBits(e, float32SignifBits) return e, s >> n, s & bitmask64(n) } func ufloat64Parts(f float64) (int32, uint64, uint64) { u := math.Float64bits(f) & math.MaxInt64 e := int32(u>>float64SignifBits) - int32(bitmask64(float64ExpBits-1)) s := u & bitmask64(float64SignifBits) n := ufloatFracBits(e, float64SignifBits) return e, s >> n, s & bitmask64(n) } func ufloat32FromParts(e int32, si uint64, sf uint64) float32 { e_ := (uint32(e) + uint32(bitmask64(float32ExpBits-1))) << float32SignifBits s_ := (uint32(si) << ufloatFracBits(e, float32SignifBits)) | uint32(sf) return math.Float32frombits(e_ | s_) } func ufloat64FromParts(e int32, si uint64, sf uint64) float64 { e_ := (uint64(e) + bitmask64(float64ExpBits-1)) << float64SignifBits s_ := (si << ufloatFracBits(e, float64SignifBits)) | sf return math.Float64frombits(e_ | s_) } func float32FromParts(sign bool, e int32, si uint64, sf uint64) float32 { f := ufloat32FromParts(e, si, sf) if sign { return -f } else { return f } } func float64FromParts(sign bool, e int32, si uint64, sf uint64) float64 { f := ufloat64FromParts(e, si, sf) if sign { return -f } else { return f } } func genUfloatRange(s bitStream, min float64, max float64, signifBits uint) (int32, uint64, uint64) { assert(min >= 0 && min <= max) var ( minExp, maxExp int32 minSignifI, maxSignifI, minSignifF, maxSignifF uint64 ) if signifBits == float32SignifBits { minExp, minSignifI, minSignifF = ufloat32Parts(float32(min)) maxExp, maxSignifI, maxSignifF = ufloat32Parts(float32(max)) } else { minExp, minSignifI, minSignifF = ufloat64Parts(min) maxExp, maxSignifI, maxSignifF = ufloat64Parts(max) } i := s.beginGroup(floatExpLabel, false) e, lOverflow, rOverflow := genIntRange(s, int64(minExp), int64(maxExp), true) s.endGroup(i, false) fracBits := ufloatFracBits(int32(e), signifBits) j := s.beginGroup(floatSignifLabel, false) var siMin, siMax uint64 switch { case lOverflow: siMin, siMax = minSignifI, minSignifI case rOverflow: siMin, siMax = maxSignifI, maxSignifI case minExp == maxExp: siMin, siMax = minSignifI, maxSignifI case int32(e) == minExp: siMin, siMax = minSignifI, bitmask64(signifBits-fracBits) case int32(e) == maxExp: siMin, siMax = 0, maxSignifI default: siMin, siMax = 0, bitmask64(signifBits-fracBits) } si, _, _ := genUintRange(s, siMin, siMax, false) var sfMin, sfMax uint64 switch { case lOverflow: sfMin, sfMax = minSignifF, minSignifF case rOverflow: sfMin, sfMax = maxSignifF, maxSignifF case minExp == maxExp && minSignifI == maxSignifI: sfMin, sfMax = minSignifF, maxSignifF case int32(e) == minExp && si == minSignifI: sfMin, sfMax = minSignifF, bitmask64(fracBits) case int32(e) == maxExp && si == maxSignifI: sfMin, sfMax = 0, maxSignifF default: sfMin, sfMax = 0, bitmask64(fracBits) } maxR := bits.Len64(sfMax - sfMin) r := genUintNNoReject(s, uint64(maxR)) sf, _, _ := genUintRange(s, sfMin, sfMax, false) s.endGroup(j, false) for i := uint(0); i < uint(maxR)-uint(r); i++ { mask := ^(uint64(1) << i) if sf&mask < sfMin { break } sf &= mask } return int32(e), si, sf } func genFloatRange(s bitStream, min float64, max float64, signifBits uint) (bool, int32, uint64, uint64) { var posMin, negMin, pNeg float64 if min >= 0 { posMin = min pNeg = 0 } else if max <= 0 { negMin = -max pNeg = 1 } else { pNeg = 0.5 } if flipBiasedCoin(s, pNeg) { e, si, sf := genUfloatRange(s, negMin, -min, signifBits) return true, e, si, sf } else { e, si, sf := genUfloatRange(s, posMin, max, signifBits) return false, e, si, sf } } rapid-1.3.0/floats_external_test.go000066400000000000000000000065001516251644000173770ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "math" "sort" "testing" . "pgregory.net/rapid" ) func TestFloatNoInf(t *testing.T) { t.Parallel() gens := []*Generator[any]{ Float32().AsAny(), Float32Min(0).AsAny(), Float32Max(0).AsAny(), Float64().AsAny(), Float64Min(0).AsAny(), Float64Max(0).AsAny(), } for _, g := range gens { t.Run(g.String(), MakeCheck(func(t *T) { f := g.Draw(t, "f") if math.IsInf(rv(f).Float(), 0) { t.Fatalf("got infinity: %v", f) } })) } } func TestFloatExamples(t *testing.T) { gens := []*Generator[any]{ Float32().AsAny(), Float32Min(-0.1).AsAny(), Float32Min(1).AsAny(), Float32Max(0.1).AsAny(), Float32Max(2.5).AsAny(), Float32Range(0.3, 0.30001).AsAny(), Float32Range(0.3, 0.301).AsAny(), Float32Range(0.3, 0.7).AsAny(), Float32Range(math.E, math.Pi).AsAny(), Float32Range(0, 1).AsAny(), Float32Range(1, 2.5).AsAny(), Float32Range(0, 100).AsAny(), Float32Range(0, 10000).AsAny(), Float64().AsAny(), Float64Min(-0.1).AsAny(), Float64Min(1).AsAny(), Float64Max(0.1).AsAny(), Float64Max(2.5).AsAny(), Float64Range(0.3, 0.30000001).AsAny(), Float64Range(0.3, 0.301).AsAny(), Float64Range(0.3, 0.7).AsAny(), Float64Range(math.E, math.Pi).AsAny(), Float64Range(0, 1).AsAny(), Float64Range(1, 2.5).AsAny(), Float64Range(0, 100).AsAny(), Float64Range(0, 10000).AsAny(), } for _, g := range gens { t.Run(g.String(), func(t *testing.T) { var vals []float64 var vals32 bool for i := 0; i < 100; i++ { f := g.Example() _, vals32 = f.(float32) vals = append(vals, rv(f).Float()) } sort.Float64s(vals) for _, f := range vals { if vals32 { t.Logf("%30g %10.3g % 5d % 20d % 16x", f, f, int(math.Log10(math.Abs(f))), int64(f), math.Float32bits(float32(f))) } else { t.Logf("%30g %10.3g % 5d % 20d % 16x", f, f, int(math.Log10(math.Abs(f))), int64(f), math.Float64bits(f)) } } }) } } func TestFloat32BoundCoverage(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Float32().Draw(t, "min") max := Float32().Draw(t, "max") if min > max { min, max = max, min } g := Float32Range(min, max) var gotMin, gotMax, gotZero bool for i := 0; i < 400; i++ { f := g.Example(i) gotMin = gotMin || f == min gotMax = gotMax || f == max gotZero = gotZero || f == 0 if gotMin && gotMax && (min > 0 || max < 0 || gotZero) { return } } t.Fatalf("[%v, %v]: got min %v, got max %v, got zero %v", min, max, gotMin, gotMax, gotZero) }) } func TestFloat64BoundCoverage(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Float64().Draw(t, "min") max := Float64().Draw(t, "max") if min > max { min, max = max, min } g := Float64Range(min, max) var gotMin, gotMax, gotZero bool for i := 0; i < 400; i++ { f := g.Example(i) gotMin = gotMin || f == min gotMax = gotMax || f == max gotZero = gotZero || f == 0 if gotMin && gotMax && (min > 0 || max < 0 || gotZero) { return } } t.Fatalf("[%v, %v]: got min %v, got max %v, got zero %v", min, max, gotMin, gotMax, gotZero) }) } rapid-1.3.0/floats_test.go000066400000000000000000000060431516251644000154770ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "math" "testing" ) func TestFloatConversionRoundtrip(t *testing.T) { t.Parallel() Check(t, func(t *T) { u := uint32(t.s.drawBits(32)) f := math.Float32frombits(u) if math.IsNaN(float64(f)) { t.Skip("NaN") // we can get NaNs with different bit patterns back } g := float32(float64(f)) if g != f { t.Fatalf("got %v (0x%x) back from %v (0x%x)", g, math.Float32bits(g), f, math.Float32bits(f)) } }) } func TestUfloat32FromParts(t *testing.T) { t.Parallel() Check(t, func(t *T) { f := Float32Min(0).Draw(t, "f") g := ufloat32FromParts(ufloat32Parts(f)) if g != f { t.Fatalf("got %v (0x%x) back from %v (0x%x)", g, math.Float32bits(g), f, math.Float32bits(f)) } }) } func TestUfloat64FromParts(t *testing.T) { t.Parallel() Check(t, func(t *T) { f := Float64Min(0).Draw(t, "f") g := ufloat64FromParts(ufloat64Parts(f)) if g != f { t.Fatalf("got %v (0x%x) back from %v (0x%x)", g, math.Float64bits(g), f, math.Float64bits(f)) } }) } func TestGenUfloat32Range(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Float32Min(0).Draw(t, "min") max := Float32Min(0).Draw(t, "max") if min > max { min, max = max, min } f := ufloat32FromParts(genUfloatRange(t.s, float64(min), float64(max), float32SignifBits)) if f < min || f > max { t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float32bits(f), min, max, math.Float32bits(min), math.Float32bits(max)) } }) } func TestGenUfloat64Range(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Float64Min(0).Draw(t, "min") max := Float64Min(0).Draw(t, "max") if min > max { min, max = max, min } f := ufloat64FromParts(genUfloatRange(t.s, min, max, float64SignifBits)) if f < min || f > max { t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float64bits(f), min, max, math.Float64bits(min), math.Float64bits(max)) } }) } func TestGenFloat32Range(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Float32().Draw(t, "min") max := Float32().Draw(t, "max") if min > max { min, max = max, min } f := float32FromParts(genFloatRange(t.s, float64(min), float64(max), float32SignifBits)) if f < min || f > max { t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float32bits(f), min, max, math.Float32bits(min), math.Float32bits(max)) } }) } func TestGenFloat64Range(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Float64().Draw(t, "min") max := Float64().Draw(t, "max") if min > max { min, max = max, min } f := float64FromParts(genFloatRange(t.s, min, max, float64SignifBits)) if f < min || f > max { t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float64bits(f), min, max, math.Float64bits(min), math.Float64bits(max)) } }) } rapid-1.3.0/generator.go000066400000000000000000000051371516251644000151410ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "reflect" "sync" ) type generatorImpl[V any] interface { String() string value(t *T) V } // Generator describes a generator of values of type V. type Generator[V any] struct { impl generatorImpl[V] strOnce sync.Once str string } func newGenerator[V any](impl generatorImpl[V]) *Generator[V] { return &Generator[V]{ impl: impl, } } func (g *Generator[V]) String() string { g.strOnce.Do(func() { g.str = g.impl.String() }) return g.str } // Draw produces a value from the generator. func (g *Generator[V]) Draw(t *T, label string) V { if t.tbLog { t.tb.Helper() } v := g.value(t) if len(t.refDraws) > 0 { ref := t.refDraws[t.draws] if !reflect.DeepEqual(v, ref) { t.tb.Fatalf("draw %v differs: %#v vs expected %#v", t.draws, v, ref) } } if t.tbLog || t.rawLog != nil { if label == "" { label = fmt.Sprintf("#%v", t.draws) } if t.tbLog { t.tb.Helper() } t.Logf("[rapid] draw %v: %#v", label, v) } t.draws++ return v } func (g *Generator[V]) value(t *T) V { i := t.s.beginGroup(g.str, true) v := g.impl.value(t) t.s.endGroup(i, false) return v } // Example produces an example value from the generator. If seed is provided, value is produced deterministically // based on seed. Example should only be used for examples; always use *Generator.Draw in property-based tests. func (g *Generator[V]) Example(seed ...int) V { s := baseSeed() if len(seed) > 0 { s = uint64(seed[0]) } v, n, err := example(g, newT(nil, newRandomBitStream(s, false), false, nil)) assertf(err == nil, "%v failed to generate an example in %v tries: %v", g, n, err) return v } // Filter creates a generator producing only values from g for which fn returns true. func (g *Generator[V]) Filter(fn func(V) bool) *Generator[V] { return filter(g, fn) } // AsAny creates a generator producing values from g converted to any. func (g *Generator[V]) AsAny() *Generator[any] { return asAny(g) } func example[V any](g *Generator[V], t *T) (V, int, error) { defer t.cleanup() for i := 1; ; i++ { r, err := recoverValue(g, t) if err == nil { return r, i, nil } else if i == exampleMaxTries { var zero V return zero, i, err } } } func recoverValue[V any](g *Generator[V], t *T) (v V, err *testError) { defer func() { err = panicToError(recover(), 3) }() return g.value(t), nil } rapid-1.3.0/generator_test.go000066400000000000000000000034341516251644000161760ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "context" "errors" "testing" ) type trivialGenImpl struct{} func (trivialGenImpl) String() string { return "" } func (trivialGenImpl) value(t *T) uint64 { return t.s.drawBits(64) } func BenchmarkTrivialGenImplValue(b *testing.B) { t := newT(nil, newRandomBitStream(baseSeed(), false), false, nil) g := trivialGenImpl{} b.ResetTimer() for i := 0; i < b.N; i++ { g.value(t) } } func BenchmarkGenerator_Value(b *testing.B) { t := newT(nil, newRandomBitStream(baseSeed(), false), false, nil) g := newGenerator[uint64](trivialGenImpl{}) b.ResetTimer() for i := 0; i < b.N; i++ { g.value(t) } } func TestExampleHelper(t *testing.T) { g := Custom(func(t *T) int { t.Helper() return Int().Draw(t, t.Name()) }) g.Example(0) } func TestCustomExampleContext(t *testing.T) { type key struct{} g := Custom(func(t *T) context.Context { ctx := context.WithValue(t.Context(), key{}, Int().Draw(t, "x")) if err := ctx.Err(); err != nil { t.Fatalf("unexpected error: %v", err) } return ctx }) ctx := g.Example(0) if err := ctx.Err(); err == nil || !errors.Is(err, context.Canceled) { t.Fatalf("expected context to be canceled, got: %v", err) } if _, ok := ctx.Value(key{}).(int); !ok { t.Fatalf("context must have a value") } } func TestCustomExampleCleanup(t *testing.T) { var state bool g := Custom(func(t *T) int { t.Cleanup(func() { state = false }) return Int().Draw(t, "x") }) state = true _ = g.Example(0) if state { t.Fatalf("cleanup must be called") } } rapid-1.3.0/go.mod000066400000000000000000000000431516251644000137210ustar00rootroot00000000000000module pgregory.net/rapid go 1.23 rapid-1.3.0/integers.go000066400000000000000000000225641516251644000147760ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "math" ) const ( byteKind = "Byte" intKind = "Int" int8Kind = "Int8" int16Kind = "Int16" int32Kind = "Int32" int64Kind = "Int64" uintKind = "Uint" uint8Kind = "Uint8" uint16Kind = "Uint16" uint32Kind = "Uint32" uint64Kind = "Uint64" uintptrKind = "Uintptr" uintptrSize = 32 << (^uintptr(0) >> 32 & 1) uintSize = 32 << (^uint(0) >> 32 & 1) intSize = uintSize maxUintptr = 1<<(uint(uintptrSize)) - 1 ) var ( integerKindToInfo = map[string]integerKindInfo{ byteKind: {size: 1, umax: math.MaxUint8}, intKind: {signed: true, size: intSize / 8, smin: math.MinInt, smax: math.MaxInt}, int8Kind: {signed: true, size: 1, smin: math.MinInt8, smax: math.MaxInt8}, int16Kind: {signed: true, size: 2, smin: math.MinInt16, smax: math.MaxInt16}, int32Kind: {signed: true, size: 4, smin: math.MinInt32, smax: math.MaxInt32}, int64Kind: {signed: true, size: 8, smin: math.MinInt64, smax: math.MaxInt64}, uintKind: {size: uintSize / 8, umax: math.MaxUint}, uint8Kind: {size: 1, umax: math.MaxUint8}, uint16Kind: {size: 2, umax: math.MaxUint16}, uint32Kind: {size: 4, umax: math.MaxUint32}, uint64Kind: {size: 8, umax: math.MaxUint64}, uintptrKind: {size: uintptrSize / 8, umax: maxUintptr}, } ) type integer interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr } type integerKindInfo struct { signed bool size int smin int64 smax int64 umax uint64 } type boolGen struct{} func Bool() *Generator[bool] { return newGenerator[bool](&boolGen{}) } func (g *boolGen) String() string { return "Bool()" } func (g *boolGen) value(t *T) bool { return t.s.drawBits(1) == 1 } func Byte() *Generator[byte] { return newIntegerGen[byte](byteKind) } func Int() *Generator[int] { return newIntegerGen[int](intKind) } func Int8() *Generator[int8] { return newIntegerGen[int8](int8Kind) } func Int16() *Generator[int16] { return newIntegerGen[int16](int16Kind) } func Int32() *Generator[int32] { return newIntegerGen[int32](int32Kind) } func Int64() *Generator[int64] { return newIntegerGen[int64](int64Kind) } func Uint() *Generator[uint] { return newIntegerGen[uint](uintKind) } func Uint8() *Generator[uint8] { return newIntegerGen[uint8](uint8Kind) } func Uint16() *Generator[uint16] { return newIntegerGen[uint16](uint16Kind) } func Uint32() *Generator[uint32] { return newIntegerGen[uint32](uint32Kind) } func Uint64() *Generator[uint64] { return newIntegerGen[uint64](uint64Kind) } func Uintptr() *Generator[uintptr] { return newIntegerGen[uintptr](uintptrKind) } func ByteMin(min byte) *Generator[byte] { return newUintMinGen[byte](byteKind, uint64(min)) } func IntMin(min int) *Generator[int] { return newIntMinGen[int](intKind, int64(min)) } func Int8Min(min int8) *Generator[int8] { return newIntMinGen[int8](int8Kind, int64(min)) } func Int16Min(min int16) *Generator[int16] { return newIntMinGen[int16](int16Kind, int64(min)) } func Int32Min(min int32) *Generator[int32] { return newIntMinGen[int32](int32Kind, int64(min)) } func Int64Min(min int64) *Generator[int64] { return newIntMinGen[int64](int64Kind, min) } func UintMin(min uint) *Generator[uint] { return newUintMinGen[uint](uintKind, uint64(min)) } func Uint8Min(min uint8) *Generator[uint8] { return newUintMinGen[uint8](uint8Kind, uint64(min)) } func Uint16Min(min uint16) *Generator[uint16] { return newUintMinGen[uint16](uint16Kind, uint64(min)) } func Uint32Min(min uint32) *Generator[uint32] { return newUintMinGen[uint32](uint32Kind, uint64(min)) } func Uint64Min(min uint64) *Generator[uint64] { return newUintMinGen[uint64](uint64Kind, min) } func UintptrMin(min uintptr) *Generator[uintptr] { return newUintMinGen[uintptr](uintptrKind, uint64(min)) } func ByteMax(max byte) *Generator[byte] { return newUintMaxGen[byte](byteKind, uint64(max)) } func IntMax(max int) *Generator[int] { return newIntMaxGen[int](intKind, int64(max)) } func Int8Max(max int8) *Generator[int8] { return newIntMaxGen[int8](int8Kind, int64(max)) } func Int16Max(max int16) *Generator[int16] { return newIntMaxGen[int16](int16Kind, int64(max)) } func Int32Max(max int32) *Generator[int32] { return newIntMaxGen[int32](int32Kind, int64(max)) } func Int64Max(max int64) *Generator[int64] { return newIntMaxGen[int64](int64Kind, max) } func UintMax(max uint) *Generator[uint] { return newUintMaxGen[uint](uintKind, uint64(max)) } func Uint8Max(max uint8) *Generator[uint8] { return newUintMaxGen[uint8](uint8Kind, uint64(max)) } func Uint16Max(max uint16) *Generator[uint16] { return newUintMaxGen[uint16](uint16Kind, uint64(max)) } func Uint32Max(max uint32) *Generator[uint32] { return newUintMaxGen[uint32](uint32Kind, uint64(max)) } func Uint64Max(max uint64) *Generator[uint64] { return newUintMaxGen[uint64](uint64Kind, max) } func UintptrMax(max uintptr) *Generator[uintptr] { return newUintMaxGen[uintptr](uintptrKind, uint64(max)) } func ByteRange(min byte, max byte) *Generator[byte] { return newUintRangeGen[byte](byteKind, uint64(min), uint64(max)) } func IntRange(min int, max int) *Generator[int] { return newIntRangeGen[int](intKind, int64(min), int64(max)) } func Int8Range(min int8, max int8) *Generator[int8] { return newIntRangeGen[int8](int8Kind, int64(min), int64(max)) } func Int16Range(min int16, max int16) *Generator[int16] { return newIntRangeGen[int16](int16Kind, int64(min), int64(max)) } func Int32Range(min int32, max int32) *Generator[int32] { return newIntRangeGen[int32](int32Kind, int64(min), int64(max)) } func Int64Range(min int64, max int64) *Generator[int64] { return newIntRangeGen[int64](int64Kind, min, max) } func UintRange(min uint, max uint) *Generator[uint] { return newUintRangeGen[uint](uintKind, uint64(min), uint64(max)) } func Uint8Range(min uint8, max uint8) *Generator[uint8] { return newUintRangeGen[uint8](uint8Kind, uint64(min), uint64(max)) } func Uint16Range(min uint16, max uint16) *Generator[uint16] { return newUintRangeGen[uint16](uint16Kind, uint64(min), uint64(max)) } func Uint32Range(min uint32, max uint32) *Generator[uint32] { return newUintRangeGen[uint32](uint32Kind, uint64(min), uint64(max)) } func Uint64Range(min uint64, max uint64) *Generator[uint64] { return newUintRangeGen[uint64](uint64Kind, min, max) } func UintptrRange(min uintptr, max uintptr) *Generator[uintptr] { return newUintRangeGen[uintptr](uintptrKind, uint64(min), uint64(max)) } func newIntegerGen[I integer](kind string) *Generator[I] { return newGenerator[I](&integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, }) } func newIntRangeGen[I integer](kind string, min int64, max int64) *Generator[I] { assertf(min <= max, "invalid integer range [%v, %v]", min, max) g := &integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, hasMin: true, hasMax: true, } g.smin = min g.smax = max return newGenerator[I](g) } func newIntMinGen[I integer](kind string, min int64) *Generator[I] { g := &integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, hasMin: true, } g.smin = min return newGenerator[I](g) } func newIntMaxGen[I integer](kind string, max int64) *Generator[I] { g := &integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, hasMax: true, } g.smax = max return newGenerator[I](g) } func newUintRangeGen[I integer](kind string, min uint64, max uint64) *Generator[I] { assertf(min <= max, "invalid integer range [%v, %v]", min, max) g := &integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, hasMin: true, hasMax: true, } g.umin = min g.umax = max return newGenerator[I](g) } func newUintMinGen[I integer](kind string, min uint64) *Generator[I] { g := &integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, hasMin: true, } g.umin = min return newGenerator[I](g) } func newUintMaxGen[I integer](kind string, max uint64) *Generator[I] { g := &integerGen[I]{ integerKindInfo: integerKindToInfo[kind], kind: kind, hasMax: true, } g.umax = max return newGenerator[I](g) } type integerGen[I integer] struct { integerKindInfo kind string umin uint64 hasMin bool hasMax bool } func (g *integerGen[I]) String() string { if g.hasMin && g.hasMax { if g.signed { return fmt.Sprintf("%sRange(%d, %d)", g.kind, g.smin, g.smax) } else { return fmt.Sprintf("%sRange(%d, %d)", g.kind, g.umin, g.umax) } } else if g.hasMin { if g.signed { return fmt.Sprintf("%sMin(%d)", g.kind, g.smin) } else { return fmt.Sprintf("%sMin(%d)", g.kind, g.umin) } } else if g.hasMax { if g.signed { return fmt.Sprintf("%sMax(%d)", g.kind, g.smax) } else { return fmt.Sprintf("%sMax(%d)", g.kind, g.umax) } } return fmt.Sprintf("%s()", g.kind) } func (g *integerGen[I]) value(t *T) I { if g.signed { i, _, _ := genIntRange(t.s, g.smin, g.smax, true) return I(i) } else { u, _, _ := genUintRange(t.s, g.umin, g.umax, true) return I(u) } } rapid-1.3.0/integers_external_test.go000066400000000000000000000177361516251644000177440ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "flag" "math/bits" "reflect" "sort" "strconv" "testing" . "pgregory.net/rapid" ) var ( flaky = flag.Bool("flaky.ext", false, "run flaky external tests") rv = reflect.ValueOf ) func TestIntExamples(t *testing.T) { gens := []*Generator[any]{ Int().AsAny(), IntMin(-3).AsAny(), IntMax(3).AsAny(), IntRange(-3, 7).AsAny(), IntRange(-1000, 1000000).AsAny(), IntRange(0, 9).AsAny(), IntRange(0, 15).AsAny(), IntRange(10, 100).AsAny(), IntRange(100, 10000).AsAny(), IntRange(100, 1000000).AsAny(), IntRange(100, 1<<60-1).AsAny(), } for _, g := range gens { t.Run(g.String(), func(t *testing.T) { var vals []int for i := 0; i < 100; i++ { vals = append(vals, g.Example().(int)) } sort.Ints(vals) for _, i := range vals { t.Log(i) } }) } } func TestIntMinMaxRange(t *testing.T) { t.Parallel() data := []struct { g *Generator[any] min func(any) *Generator[any] max func(any) *Generator[any] range_ func(any, any) *Generator[any] }{ { Int().AsAny(), func(i any) *Generator[any] { return IntMin(i.(int)).AsAny() }, func(i any) *Generator[any] { return IntMax(i.(int)).AsAny() }, func(i, j any) *Generator[any] { return IntRange(i.(int), j.(int)).AsAny() }, }, { Int8().AsAny(), func(i any) *Generator[any] { return Int8Min(i.(int8)).AsAny() }, func(i any) *Generator[any] { return Int8Max(i.(int8)).AsAny() }, func(i, j any) *Generator[any] { return Int8Range(i.(int8), j.(int8)).AsAny() }, }, { Int16().AsAny(), func(i any) *Generator[any] { return Int16Min(i.(int16)).AsAny() }, func(i any) *Generator[any] { return Int16Max(i.(int16)).AsAny() }, func(i, j any) *Generator[any] { return Int16Range(i.(int16), j.(int16)).AsAny() }, }, { Int32().AsAny(), func(i any) *Generator[any] { return Int32Min(i.(int32)).AsAny() }, func(i any) *Generator[any] { return Int32Max(i.(int32)).AsAny() }, func(i, j any) *Generator[any] { return Int32Range(i.(int32), j.(int32)).AsAny() }, }, { Int64().AsAny(), func(i any) *Generator[any] { return Int64Min(i.(int64)).AsAny() }, func(i any) *Generator[any] { return Int64Max(i.(int64)).AsAny() }, func(i, j any) *Generator[any] { return Int64Range(i.(int64), j.(int64)).AsAny() }, }, } for _, d := range data { t.Run(d.g.String(), MakeCheck(func(t *T) { min := d.g.Draw(t, "min") max := d.g.Draw(t, "max") if rv(min).Int() > rv(max).Int() { t.Skip("min > max") } i := d.min(min).Draw(t, "i") if rv(i).Int() < rv(min).Int() { t.Fatalf("got %v which is less than min %v", i, min) } j := d.max(max).Draw(t, "j") if rv(j).Int() > rv(max).Int() { t.Fatalf("got %v which is more than max %v", j, max) } k := d.range_(min, max).Draw(t, "k") if rv(k).Int() < rv(min).Int() || rv(k).Int() > rv(max).Int() { t.Fatalf("got %v which is out of bounds [%v, %v]", k, min, max) } })) } } func TestUintMinMaxRange(t *testing.T) { t.Parallel() data := []struct { g *Generator[any] min func(any) *Generator[any] max func(any) *Generator[any] range_ func(any, any) *Generator[any] }{ { Byte().AsAny(), func(i any) *Generator[any] { return ByteMin(i.(byte)).AsAny() }, func(i any) *Generator[any] { return ByteMax(i.(byte)).AsAny() }, func(i, j any) *Generator[any] { return ByteRange(i.(byte), j.(byte)).AsAny() }, }, { Uint().AsAny(), func(i any) *Generator[any] { return UintMin(i.(uint)).AsAny() }, func(i any) *Generator[any] { return UintMax(i.(uint)).AsAny() }, func(i, j any) *Generator[any] { return UintRange(i.(uint), j.(uint)).AsAny() }, }, { Uint8().AsAny(), func(i any) *Generator[any] { return Uint8Min(i.(uint8)).AsAny() }, func(i any) *Generator[any] { return Uint8Max(i.(uint8)).AsAny() }, func(i, j any) *Generator[any] { return Uint8Range(i.(uint8), j.(uint8)).AsAny() }, }, { Uint16().AsAny(), func(i any) *Generator[any] { return Uint16Min(i.(uint16)).AsAny() }, func(i any) *Generator[any] { return Uint16Max(i.(uint16)).AsAny() }, func(i, j any) *Generator[any] { return Uint16Range(i.(uint16), j.(uint16)).AsAny() }, }, { Uint32().AsAny(), func(i any) *Generator[any] { return Uint32Min(i.(uint32)).AsAny() }, func(i any) *Generator[any] { return Uint32Max(i.(uint32)).AsAny() }, func(i, j any) *Generator[any] { return Uint32Range(i.(uint32), j.(uint32)).AsAny() }, }, { Uint64().AsAny(), func(i any) *Generator[any] { return Uint64Min(i.(uint64)).AsAny() }, func(i any) *Generator[any] { return Uint64Max(i.(uint64)).AsAny() }, func(i, j any) *Generator[any] { return Uint64Range(i.(uint64), j.(uint64)).AsAny() }, }, { Uintptr().AsAny(), func(i any) *Generator[any] { return UintptrMin(i.(uintptr)).AsAny() }, func(i any) *Generator[any] { return UintptrMax(i.(uintptr)).AsAny() }, func(i, j any) *Generator[any] { return UintptrRange(i.(uintptr), j.(uintptr)).AsAny() }, }, } for _, d := range data { t.Run(d.g.String(), MakeCheck(func(t *T) { min := d.g.Draw(t, "min") max := d.g.Draw(t, "max") if rv(min).Uint() > rv(max).Uint() { t.Skip("min > max") } i := d.min(min).Draw(t, "i") if rv(i).Uint() < rv(min).Uint() { t.Fatalf("got %v which is less than min %v", i, min) } j := d.max(max).Draw(t, "j") if rv(j).Uint() > rv(max).Uint() { t.Fatalf("got %v which is more than max %v", j, max) } k := d.range_(min, max).Draw(t, "k") if rv(k).Uint() < rv(min).Uint() || rv(k).Uint() > rv(max).Uint() { t.Fatalf("got %v which is out of bounds [%v, %v]", k, min, max) } })) } } func TestIntBoundCoverage(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Int().Draw(t, "min") max := Int().Draw(t, "max") if min > max { min, max = max, min } g := IntRange(min, max) var gotMin, gotMax, gotZero bool for i := 0; i < 250; i++ { n := g.Example(i) gotMin = gotMin || n == min gotMax = gotMax || n == max gotZero = gotZero || n == 0 if gotMin && gotMax && (min > 0 || max < 0 || gotZero) { return } } t.Fatalf("[%v, %v]: got min %v, got max %v, got zero %v", min, max, gotMin, gotMax, gotZero) }) } func TestByteCoverage(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } for b := 0; b < 256; b++ { t.Run(strconv.Itoa(b), func(t *testing.T) { _ = Byte().Filter(func(v byte) bool { return v == byte(b) }).Example() }) } } func TestIntCoverage(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } filters := []func(int) bool{ func(i int) bool { return i == 0 }, func(i int) bool { return i == 1 }, func(i int) bool { return i == -1 }, func(i int) bool { return i%2 == 0 }, func(i int) bool { return i%17 == 0 }, func(i int) bool { return i > 0 && i < 100 }, func(i int) bool { return i < 0 && i > -100 }, func(i int) bool { return i > 1<<30 }, func(i int) bool { return i < -(1 << 30) }, } if bits.UintSize == 64 { filters = append(filters, func(i int) bool { return i > 1<<62 }) filters = append(filters, func(i int) bool { return i < -(1 << 62) }) } for i, fn := range filters { t.Run(strconv.Itoa(i), func(t *testing.T) { _ = Int().Filter(fn).Example() }) } } func TestUintCoverage(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } filters := []func(uint) bool{ func(i uint) bool { return i == 0 }, func(i uint) bool { return i == 1 }, func(i uint) bool { return i%2 == 0 }, func(i uint) bool { return i%17 == 0 }, func(i uint) bool { return i > 0 && i < 100 }, func(i uint) bool { return i > 1<<31 }, } if bits.UintSize == 64 { filters = append(filters, func(i uint) bool { return i > 1<<63 }) } for i, fn := range filters { t.Run(strconv.Itoa(i), func(t *testing.T) { _ = Uint().Filter(fn).Example() }) } } rapid-1.3.0/make.go000066400000000000000000000147231516251644000140710ustar00rootroot00000000000000// Copyright 2022 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "reflect" ) // MakeConfig customizes reflection-based generators produced by MakeCustom. type MakeConfig struct { // Types, if specified, provides Generators for concrete types that override // the automatic reflection-based generation. Types map[reflect.Type]*Generator[any] // Kinds, if specified, provides Generators for the specified kind that // override the automatic reflection-based generation. Kinds map[reflect.Kind]*Generator[any] // Fields, if specified, provides Generators for fields on a given type that // override the automatic reflection-based generation. Fields map[reflect.Type]map[string]*Generator[any] } // Make creates a generator of values of type V, using reflection to infer the required structure. // Currently, Make may be unable to terminate generation of values of some recursive types, thus using // Make with recursive types requires extra care. func Make[V any]() *Generator[V] { return MakeCustom[V](MakeConfig{}) } // MakeCustom creates a generator of values of type V, using reflection and // overrides from MakeConfig to infer the required structure. // Currently, Make may be unable to terminate generation of values of some recursive types, thus using // Make with recursive types requires extra care. func MakeCustom[V any](cfg MakeConfig) *Generator[V] { var zero V gen := cfg.newMakeGen(reflect.TypeOf(zero)) return newGenerator[V](&makeGen[V]{ gen: gen, }) } type makeGen[V any] struct { gen *Generator[any] } func (g *makeGen[V]) String() string { var zero V return fmt.Sprintf("Make[%T]()", zero) } func (g *makeGen[V]) value(t *T) V { return g.gen.value(t).(V) } func (c *MakeConfig) newMakeGen(typ reflect.Type) *Generator[any] { gen, mayNeedCast := c.newMakeKindGen(typ) if !mayNeedCast || typ.String() == typ.Kind().String() { return gen // fast path with less reflect } return newGenerator[any](&castGen{gen, typ}) } type castGen struct { gen *Generator[any] typ reflect.Type } func (g *castGen) String() string { return fmt.Sprintf("cast(%v, %v)", g.gen, g.typ.Name()) } func (g *castGen) value(t *T) any { v := g.gen.value(t) if v == nil { return nil } return reflect.ValueOf(v).Convert(g.typ).Interface() } func (c *MakeConfig) newMakeKindGen(typ reflect.Type) (gen *Generator[any], mayNeedCast bool) { if c.Types != nil { if gen, ok := c.Types[typ]; ok { return gen, true } } if c.Kinds != nil { if gen, ok := c.Kinds[typ.Kind()]; ok { return gen, true } } switch typ.Kind() { case reflect.Bool: return Bool().AsAny(), true case reflect.Int: return Int().AsAny(), true case reflect.Int8: return Int8().AsAny(), true case reflect.Int16: return Int16().AsAny(), true case reflect.Int32: return Int32().AsAny(), true case reflect.Int64: return Int64().AsAny(), true case reflect.Uint: return Uint().AsAny(), true case reflect.Uint8: return Uint8().AsAny(), true case reflect.Uint16: return Uint16().AsAny(), true case reflect.Uint32: return Uint32().AsAny(), true case reflect.Uint64: return Uint64().AsAny(), true case reflect.Uintptr: return Uintptr().AsAny(), true case reflect.Float32: return Float32().AsAny(), true case reflect.Float64: return Float64().AsAny(), true case reflect.Array: return c.genAnyArray(typ), false case reflect.Map: return c.genAnyMap(typ), false case reflect.Pointer: return Deferred(func() *Generator[any] { return c.genAnyPointer(typ) }), false case reflect.Slice: return c.genAnySlice(typ), false case reflect.String: return String().AsAny(), true case reflect.Struct: return c.genAnyStruct(typ), false default: panic(fmt.Sprintf("unsupported type kind for Make: %v", typ.Kind())) } } func (c *MakeConfig) genAnyPointer(typ reflect.Type) *Generator[any] { elem := typ.Elem() elemGen := c.newMakeGen(elem) const pNonNil = 0.5 return Custom(func(t *T) any { if flipBiasedCoin(t.s, pNonNil) { val := elemGen.value(t) ptr := reflect.New(elem) ptr.Elem().Set(reflect.ValueOf(val)) return ptr.Interface() } else { return reflect.Zero(typ).Interface() } }) } func (c *MakeConfig) genAnyArray(typ reflect.Type) *Generator[any] { count := typ.Len() elemGen := c.newMakeGen(typ.Elem()) return Custom(func(t *T) any { a := reflect.Indirect(reflect.New(typ)) if count == 0 { t.s.drawBits(0) } else { for i := 0; i < count; i++ { e := reflect.ValueOf(elemGen.value(t)) a.Index(i).Set(e) } } return a.Interface() }) } func (c *MakeConfig) genAnySlice(typ reflect.Type) *Generator[any] { elemGen := c.newMakeGen(typ.Elem()) return Custom(func(t *T) any { repeat := newRepeat(-1, -1, -1, elemGen.String()) sl := reflect.MakeSlice(typ, 0, repeat.avg()) for repeat.more(t.s) { e := reflect.ValueOf(elemGen.value(t)) sl = reflect.Append(sl, e) } return sl.Interface() }) } func (c *MakeConfig) genAnyMap(typ reflect.Type) *Generator[any] { keyGen := c.newMakeGen(typ.Key()) valGen := c.newMakeGen(typ.Elem()) return Custom(func(t *T) any { label := keyGen.String() + "," + valGen.String() repeat := newRepeat(-1, -1, -1, label) m := reflect.MakeMapWithSize(typ, repeat.avg()) for repeat.more(t.s) { k := reflect.ValueOf(keyGen.value(t)) v := reflect.ValueOf(valGen.value(t)) if m.MapIndex(k).IsValid() { repeat.reject() } else { m.SetMapIndex(k, v) } } return m.Interface() }) } func (c *MakeConfig) genAnyStruct(typ reflect.Type) *Generator[any] { customFields := map[string]*Generator[any]{} if c.Fields != nil { if custom, ok := c.Fields[typ]; ok { customFields = custom } } numFields := typ.NumField() fieldGens := make([]*Generator[any], numFields) for i := 0; i < numFields; i++ { field := typ.Field(i) if !field.IsExported() { continue } if gen, ok := customFields[field.Name]; ok { fieldGens[i] = gen } else { fieldGens[i] = c.newMakeGen(field.Type) } } return Custom(func(t *T) any { s := reflect.Indirect(reflect.New(typ)) fieldsSet := 0 for i := 0; i < numFields; i++ { if fieldGens[i] == nil { continue } value := fieldGens[i].value(t) if value == nil { continue } s.Field(i).Set(reflect.ValueOf(value)) fieldsSet++ } if fieldsSet == 0 { t.s.drawBits(0) } return s.Interface() }) } rapid-1.3.0/make_example_test.go000066400000000000000000000031401516251644000166320ustar00rootroot00000000000000// Copyright 2022 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "fmt" "pgregory.net/rapid" ) func ExampleMake() { gen := rapid.Make[map[int]bool]() for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // map[-433:true -261:false -53:false -23:false 1:true 184:false] // map[-3:true 0:true] // map[4:true] // map[-359:true -154:true -71:true -17:false -1:false 590:false 22973756520:true] // map[] } type nodeValue int type tree struct { Value nodeValue Left, Right *tree } func (t *tree) String() string { if t == nil { return "nil" } return fmt.Sprintf("(%s %v %s)", t.Left.String(), t.Value, t.Right.String()) } func ExampleMake_tree() { gen := rapid.Make[*tree]() for i := 0; i < 5; i++ { fmt.Println(gen.Example(i)) } // Output: // (nil 1 (nil 184 nil)) // (((nil -1 (((((nil -485 ((nil -2 ((((nil -5 nil) -9898554875447 nil) -34709387 ((nil 50440 nil) 113 (((((nil -442 nil) -66090341586 nil) 179745 nil) 494 (((nil -2 nil) 543360606020 nil) 15261837 nil)) -1778 nil))) -21034573818 nil)) -5 nil)) 15606609 nil) 882666 (nil 3 nil)) -12 (nil -2 ((nil 1 nil) -2 (((nil 11 nil) -187307 ((nil -198 (nil -6895 nil)) 12027 (nil -539313 nil))) 1532 (nil 6 nil))))) 1745354 nil)) -2 nil) -3 nil) // nil // (((nil -15 (nil 6598 nil)) -131 (nil 317121006373596 ((nil 14 ((nil -9223372036854775808 nil) 1 nil)) 14668 nil))) 590 nil) // nil } rapid-1.3.0/make_test.go000066400000000000000000000036151516251644000151260ustar00rootroot00000000000000// Copyright 2022 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "reflect" "testing" "pgregory.net/rapid" ) type privateFields struct { private bool } type fieldOverride struct { Name string Count int } type stringAlias string type kindOverride struct { Name string Alias stringAlias } func TestMakeIgnoresPrivateFields(t *testing.T) { // Private fields are ignored (and don't panic). rapid.Make[privateFields]().Example() } func TestMakeCustomTypeOverride(t *testing.T) { ex := rapid.MakeCustom[privateFields](rapid.MakeConfig{ Types: map[reflect.Type]*rapid.Generator[any]{ reflect.TypeOf(privateFields{}): rapid.Just(privateFields{private: true}).AsAny(), }, }).Example() if !ex.private { t.Errorf(".private should be true. got: %#v", ex) } } func TestMakeCustomFieldOverride(t *testing.T) { const customName = "custom-name" v := rapid.MakeCustom[fieldOverride](rapid.MakeConfig{ Fields: map[reflect.Type]map[string]*rapid.Generator[any]{ reflect.TypeOf(fieldOverride{}): { "Name": rapid.Just(customName).AsAny(), }, }, }).Example() if v.Name != customName { t.Fatalf("expected Name to be %q, got %q", customName, v.Name) } } func TestMakeCustomKindOverride(t *testing.T) { const customValue = "kind-override" v := rapid.MakeCustom[kindOverride](rapid.MakeConfig{ Kinds: map[reflect.Kind]*rapid.Generator[any]{ reflect.String: rapid.Just(customValue).AsAny(), }, }).Example() if v.Name != customValue { t.Fatalf("expected Name to be %q, got %q", customValue, v.Name) } expectedAlias := stringAlias(customValue) if v.Alias != expectedAlias { t.Fatalf("expected Alias to be %q, got %q", expectedAlias, v.Alias) } } rapid-1.3.0/persist.go000066400000000000000000000102021516251644000146310ustar00rootroot00000000000000// Copyright 2020 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "bufio" "fmt" "os" "path/filepath" "strconv" "strings" "time" "unicode" ) const ( rapidVersion = "v0.4.8" persistDirMode = 0775 failfileTmpPattern = ".rapid-failfile-tmp-*" ) var ( // https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file windowsReservedNames = []string{ "CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "COM¹", "COM²", "COM³", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "LPT¹", "LPT²", "LPT³", } ) func kindaSafeFilename(f string) string { var s strings.Builder for _, r := range f { if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_' { s.WriteRune(r) } else { s.WriteRune('_') } } name := s.String() nameUpper := strings.ToUpper(name) for _, reserved := range windowsReservedNames { if nameUpper == reserved { return name + "_" } } return name } func failFileName(testName string) (string, string) { ts := time.Now().Format("20060102150405") fileName := fmt.Sprintf("%s-%s-%d.fail", kindaSafeFilename(testName), ts, os.Getpid()) dirName := filepath.Join("testdata", "rapid", kindaSafeFilename(testName)) return dirName, filepath.Join(dirName, fileName) } func failFilePattern(testName string) string { fileName := fmt.Sprintf("%s-*.fail", kindaSafeFilename(testName)) dirName := filepath.Join("testdata", "rapid", kindaSafeFilename(testName)) return filepath.Join(dirName, fileName) } func saveFailFile(filename string, version string, output []byte, seed uint64, buf []uint64) error { dir := filepath.Dir(filename) err := os.MkdirAll(dir, persistDirMode) if err != nil { return fmt.Errorf("failed to create directory for fail file %q: %w", filename, err) } f, err := os.CreateTemp(dir, failfileTmpPattern) if err != nil { return fmt.Errorf("failed to create temporary file for fail file %q: %w", filename, err) } defer func() { _ = os.Remove(f.Name()) }() defer func() { _ = f.Close() }() out := strings.Split(string(output), "\n") for _, s := range out { _, err := f.WriteString("# " + s + "\n") if err != nil { return fmt.Errorf("failed to write data to fail file %q: %w", filename, err) } } bs := []string{fmt.Sprintf("%v#%v", version, seed)} for _, u := range buf { bs = append(bs, fmt.Sprintf("0x%x", u)) } _, err = f.WriteString(strings.Join(bs, "\n")) if err != nil { return fmt.Errorf("failed to write data to fail file %q: %w", filename, err) } _ = f.Close() // early close, otherwise os.Rename will fail on Windows err = os.Rename(f.Name(), filename) if err != nil { return fmt.Errorf("failed to save fail file %q: %w", filename, err) } return nil } func loadFailFile(filename string) (string, uint64, []uint64, error) { f, err := os.Open(filename) if err != nil { return "", 0, nil, fmt.Errorf("failed to open fail file: %w", err) } defer func() { _ = f.Close() }() var data []string scanner := bufio.NewScanner(f) for scanner.Scan() { s := strings.TrimSpace(scanner.Text()) if strings.HasPrefix(s, "#") || s == "" { continue } data = append(data, s) } if err := scanner.Err(); err != nil { return "", 0, nil, fmt.Errorf("failed to load fail file %q: %w", filename, err) } if len(data) == 0 { return "", 0, nil, fmt.Errorf("no data in fail file %q", filename) } split := strings.Split(data[0], "#") if len(split) != 2 { return "", 0, nil, fmt.Errorf("invalid version/seed field %q in %q", data[0], filename) } seed, err := strconv.ParseUint(split[1], 10, 64) if err != nil { return "", 0, nil, fmt.Errorf("invalid seed %q in %q", split[1], filename) } var buf []uint64 for _, b := range data[1:] { u, err := strconv.ParseUint(b, 0, 64) if err != nil { return "", 0, nil, fmt.Errorf("failed to load fail file %q: %w", filename, err) } buf = append(buf, u) } return split[0], seed, buf, nil } rapid-1.3.0/persist_test.go000066400000000000000000000035161516251644000157020ustar00rootroot00000000000000// Copyright 2020 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "os" "path/filepath" "testing" ) func TestFailFilePattern(t *testing.T) { t.Parallel() Check(t, func(t *T) { testName := String().Draw(t, "testName") _, fileName := failFileName(testName) pattern := failFilePattern(testName) match, err := filepath.Match(pattern, fileName) if !match || err != nil { t.Fatalf("pattern %q does not match %q; err %v", pattern, fileName, err) } }) } func TestFailFileRoundtrip(t *testing.T) { t.Parallel() Check(t, func(t *T) { var ( // OS X seems to have issues with Go 1.16 and String(), reporting "illegal byte sequence" when trying to rename the file testName = StringMatching(`[a-zA-Z0-9._-]+`).Draw(t, "testName") version = StringMatching(`[a-zA-Z0-9._-]+`).Draw(t, "version") seed = Uint64().Draw(t, "seed") output = SliceOf(Byte()).Draw(t, "output") buf = SliceOf(Uint64()).Draw(t, "buf") ) dirName, fileName := failFileName(testName) err := saveFailFile(fileName, version, output, seed, buf) if err != nil { t.Fatal(err) } defer func() { _ = os.RemoveAll(dirName) }() version2, seed2, buf2, err := loadFailFile(fileName) if err != nil { t.Fatal(err) } if version2 != version { t.Fatalf("got version %q instead of %q", version2, version) } if seed2 != seed { t.Fatalf("got seed %v instead of %v", seed2, seed) } if len(buf2) != len(buf) { t.Fatalf("got buf of length %v instead of %v", len(buf2), len(buf)) } for i, u := range buf { if buf2[i] != u { t.Fatalf("got %v instead of %v at %v", buf2[i], u, i) } } }) } rapid-1.3.0/regexp_external_test.go000066400000000000000000003551421516251644000174120ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "regexp" "testing" . "pgregory.net/rapid" ) // Based on https://github.com/rust-lang/regex/blob/master/tests/crates_regex.rs var crateRegexps = []string{ `\s*(\d+)(\w)\s*`, // autoshutdown-0.1.0: r"\s*(\d+)(\w)\s*" `/`, // epub-1.1.1: r"/" "^Revision\t+: ([0-9a-fA-F]+)", // rpi-info-0.2.0: "^Revision\t+: ([0-9a-fA-F]+)" "Serial\t+: ([0-9a-fA-F]+)", // rpi-info-0.2.0: "Serial\t+: ([0-9a-fA-F]+)" `^u([0-9]+)(be|le|he)?$`, // pnet_macros-0.21.0: r"^u([0-9]+)(be|le|he)?$" `^[A-Z]{2}\d{2}[A-Z\d]{1,30}$`, // iban_validate-1.0.3: r"^[A-Z]{2}\d{2}[A-Z\d]{1,30}$" `.*\[(?P.+)%.*\].*`, // markifier-0.1.0: r".*\[(?P.+)%.*\].*" `(#include) (\S*)(.*)`, // mallumo-0.3.0: r"(#include) (\S*)(.*)" `(ERROR: \d+:)(\d+)(: )(.+)`, // mallumo-0.3.0: r"(ERROR: \d+:)(\d+)(: )(.+)" `(\d+\()(\d+)(?:\) : )(.+)`, // mallumo-0.3.0: r"(\d+\()(\d+)(?:\) : )(.+)" `(.+?)(\[.*?\])?`, // magnet_more-0.0.1: r"(.+?)(\[.*?\])?" `:(?P[a-zA-Z_]+)`, // magnet_app-0.0.1: r":(?P[a-zA-Z_]+)" `^\d{6}(?:\s*,\s*\d{6})*$`, // yubibomb-0.2.0: r"^\d{6}(?:\s*,\s*\d{6})*$" `[\\/]([^\\/?]+)(\?.*)?$`, // multirust-rs-0.0.4: r"[\\/]([^\\/?]+)(\?.*)?$" "\"[a-z]*\":null", // hueclient-0.3.2: "\"[a-z]*\":null" ",+", // hueclient-0.3.2: ",+" ",\\}", // hueclient-0.3.2: ",\\}" "\\{,", // hueclient-0.3.2: "\\{," `[a-zA-Z_\$][a-zA-Z_0-9]*`, // aerial-0.1.0: r"[a-zA-Z_\$][a-zA-Z_0-9]*" `thi[sng]+`, // aerial-0.1.0: r"thi[sng]+" `(.+)\s+\((.+?)\)`, // rvue-0.1.0: r"(.+)\s+\((.+?)\)" `([\d\.]+)\s*out\s*of\s*([\d\.]+)`, // rvue-0.1.0: r"([\d\.]+)\s*out\s*of\s*([\d\.]+)" `^([\d\.]+)\s*(?:\(\))?$`, // rvue-0.1.0: r"^([\d\.]+)\s*(?:\(\))?$" `([\d\.]+)\s*Points\s*Possible`, // rvue-0.1.0: r"([\d\.]+)\s*Points\s*Possible" `([\d\.]+)\s*/\s*([\d\.]+)`, // rvue-0.1.0: r"([\d\.]+)\s*/\s*([\d\.]+)" `_?([_a-z0-9]+)\s*:\s*([_a-z0-9]+)\s*[,)]`, // rvsim-0.1.0: r"_?([_a-z0-9]+)\s*:\s*([_a-z0-9]+)\s*[,)]" "(.*[^\\\\])\\{\\}(.*)", // nereon-0.1.4: "(.*[^\\\\])\\{\\}(.*)" `((?i)^(.+).s(\d+)e(\d+).*)$`, // next_episode-0.3.0: r"((?i)^(.+).s(\d+)e(\d+).*)$" `[^a-z0-9-]+`, // migrant_lib-0.19.2: r"[^a-z0-9-]+" `[0-9]{14}_[a-z0-9-]+`, // migrant_lib-0.19.2: r"[0-9]{14}_[a-z0-9-]+" `([0-9]{14}_)?[a-z0-9-]+`, // migrant_lib-0.19.2: r"([0-9]{14}_)?[a-z0-9-]+" // minipre-0.2.0: "$_" `>\s+<`, // minifier-0.0.13: r">\s+<" `\s{2,}|[\r\n]`, // minifier-0.0.13: r"\s{2,}|[\r\n]" `<(style|script)[\w|\s].*?>`, // minifier-0.0.13: r"<(style|script)[\w|\s].*?>" "", // minifier-0.0.13: "" `<\w.*?>`, // minifier-0.0.13: r"<\w.*?>" ` \s+|\s +`, // minifier-0.0.13: r" \s+|\s +" `\w\s+\w`, // minifier-0.0.13: r"\w\s+\w" `'\s+>`, // minifier-0.0.13: r"'\s+>" `\d\s+>`, // minifier-0.0.13: r"\d\s+>" `(?P\([^)]+\))|(?P[a-zA-Z0-9_]+)`, // ggp-rs-0.1.2: r"(?P\([^)]+\))|(?P[a-zA-Z0-9_]+)" `\((.*)\).`, // ggp-rs-0.1.2: r"\((.*)\)." "[A-Za-z0-9_]", // poe-superfilter-0.2.0: "[A-Za-z0-9_]" `(\d+)x(\d+)`, // poke-a-mango-0.5.0: r"(\d+)x(\d+)" `(?P\d+) (?P\d+)`, // pop3-rs-0.1.0: r"(?P\d+) (?P\d+)" `(?P\d+) (?P[\x21-\x7E]{1,70})`, // pop3-rs-0.1.0: r"(?P\d+) (?P[\x21-\x7E]{1,70})" `(<.*>)\r\n$`, // pop3-rs-0.1.0: r"(<.*>)\r\n$" `^(?P\+OK|-ERR) (?P.*)`, // pop3-rs-0.1.0: r"^(?P\+OK|-ERR) (?P.*)" `^\.\r\n$`, // pop3-1.0.6: r"^\.\r\n$" `\+OK(.*)`, // pop3-1.0.6: r"\+OK(.*)" `-ERR(.*)`, // pop3-1.0.6: r"-ERR(.*)" `\+OK (\d+) (\d+)\r\n`, // pop3-1.0.6: r"\+OK (\d+) (\d+)\r\n" `(\d+) ([\x21-\x7e]+)\r\n`, // pop3-1.0.6: r"(\d+) ([\x21-\x7e]+)\r\n" `\+OK (\d+) ([\x21-\x7e]+)\r\n`, // pop3-1.0.6: r"\+OK (\d+) ([\x21-\x7e]+)\r\n" `(\d+) (\d+)\r\n`, // pop3-1.0.6: r"(\d+) (\d+)\r\n" `\+OK (\d+) (\d+)\r\n`, // pop3-1.0.6: r"\+OK (\d+) (\d+)\r\n" "github:(\\w+)/?(\\w+)?", // polk-1.1.3: "github:(\\w+)/?(\\w+)?" "^[0-9]{5}", // geochunk-0.1.5: "^[0-9]{5}" `((?:(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))`, // generic-dns-update-1.1.4: r"((?:(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))" `((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(([0-9A-Fa-f]{1,4}:){0,5}:((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(::([0-9A-Fa-f]{1,4}:){0,5}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))`, // generic-dns-update-1.1.4: r"((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(([0-9A-Fa-f]{1,4}:){0,5}:((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(::([0-9A-Fa-f]{1,4}:){0,5}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))" `([0-9.]*)`, // generic-dns-update-1.1.4: r"([0-9.]*)" `([0-9]+)`, // generic-dns-update-1.1.4: r"([0-9]+)" `([0-9]+)`, // generic-dns-update-1.1.4: r"([0-9]+)" `([0-1]*)`, // generic-dns-update-1.1.4: r"([0-1]*)" `(\d*)\.(\d*)\.(\d*)(-(\S*))?`, // generate-nix-pkg-0.3.0: r"(\d*)\.(\d*)\.(\d*)(-(\S*))?" `^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?`, // generate-nix-pkg-0.3.0: r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?" `arch/([a-z0-9_])+/`, // genact-0.6.0: r"arch/([a-z0-9_])+/" `arch/([a-z0-9_])+/`, // genact-0.6.0: r"arch/([a-z0-9_])+/" `^\s*((\*(/\d+)?)|[0-9-,/]+)(\s+((\*(/\d+)?)|[0-9-,/]+)){4,5}\s*$`, // cron_rs-0.1.6: r"^\s*((\*(/\d+)?)|[0-9-,/]+)(\s+((\*(/\d+)?)|[0-9-,/]+)){4,5}\s*$" `^([a-zA-Z]+)::(.+)$`, // systemfd-0.3.0: r"^([a-zA-Z]+)::(.+)$" "__?hidden#\\d+_", // symbolic-debuginfo-5.0.2: "__?hidden#\\d+_" `^Linux ([^ ]+) (.*) \w+(?: GNU/Linux)?$`, // symbolic-minidump-5.0.2: r"^Linux ([^ ]+) (.*) \w+(?: GNU/Linux)?$" // graphql-idl-parser-0.1.1: "^(?u:\\#)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+" // graphql-idl-parser-0.1.1: "^(?u:=)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+" "^([A-Z_-_a-z])([0-9A-Z_-_a-z])*", // graphql-idl-parser-0.1.1: "^(?u:[A-Z_-_a-z])(?u:[0-9A-Z_-_a-z])*" "^(!)", // graphql-idl-parser-0.1.1: "^(?u:!)" "^(\\()", // graphql-idl-parser-0.1.1: "^(?u:\\()" "^(\\))", // graphql-idl-parser-0.1.1: "^(?u:\\))" "^(,)", // graphql-idl-parser-0.1.1: "^(?u:,)" "^(:)", // graphql-idl-parser-0.1.1: "^(?u::)" "^(@)", // graphql-idl-parser-0.1.1: "^(?u:@)" "^(\\[)", // graphql-idl-parser-0.1.1: "^(?u:\\[)" "^(\\])", // graphql-idl-parser-0.1.1: "^(?u:\\])" "^(enum)", // graphql-idl-parser-0.1.1: "^(?u:enum)" "^(implements)", // graphql-idl-parser-0.1.1: "^(?u:implements)" "^(input)", // graphql-idl-parser-0.1.1: "^(?u:input)" "^(interface)", // graphql-idl-parser-0.1.1: "^(?u:interface)" "^(scalar)", // graphql-idl-parser-0.1.1: "^(?u:scalar)" "^(type)", // graphql-idl-parser-0.1.1: "^(?u:type)" "^(union)", // graphql-idl-parser-0.1.1: "^(?u:union)" "^(\\{)", // graphql-idl-parser-0.1.1: "^(?u:\\{)" "^(\\})", // graphql-idl-parser-0.1.1: "^(?u:\\})" `(?s)/\*(?P.*?)\*/`, // grimoire-0.1.0: r"(?s)/\*(?P.*?)\*/" `[\d]+(?:[~\x{2053}\x{223C}\x{FF5E}][\d]+)?`, // phonenumber-0.2.0+8.9.0: r"[\d]+(?:[~\x{2053}\x{223C}\x{FF5E}][\d]+)?" `[, \[\]]`, // phonenumber-0.2.0+8.9.0: r"[, \[\]]" `[\\/] *x`, // phonenumber-0.2.0+8.9.0: r"[\\/] *x" `[[\P{N}&&\P{L}]&&[^#]]+$`, // phonenumber-0.2.0+8.9.0: r"[[\P{N}&&\P{L}]&&[^#]]+$" `(?:.*?[A-Za-z]){3}.*`, // phonenumber-0.2.0+8.9.0: r"(?:.*?[A-Za-z]){3}.*" `(\D+)`, // phonenumber-0.2.0+8.9.0: r"(\D+)" `(\$\d)`, // phonenumber-0.2.0+8.9.0: r"(\$\d)" `\(?\$1\)?`, // phonenumber-0.2.0+8.9.0: r"\(?\$1\)?" `\D`, // phone_number-0.1.0: r"\D" `^0+`, // phone_number-0.1.0: r"^0+" `^89`, // phone_number-0.1.0: r"^89" `^8+`, // phone_number-0.1.0: r"^8+" `^ *(\^_*\^) *$`, // phile-0.1.4: r"^ *(\^_*\^) *$" // phile-0.1.4: r"^[_\p{XID_Start}]$" // phile-0.1.4: r"^\p{XID_Continue}$" "%25(?P[0-9a-fA-F][0-9a-fA-F])", // uritemplate-0.1.2: "%25(?P[0-9a-fA-F][0-9a-fA-F])" "^package://(\\w+)/", // urdf-rs-0.4.2: "^package://(\\w+)/" `(?P[?&.])`, // url-match-0.1.7: r"(?P[?&.])" `:(?P[a-zA-Z0-9_-]+)`, // url-match-0.1.7: r":(?P[a-zA-Z0-9_-]+)" `hello world`, // tsm-sys-0.1.0: r"hello world" "^(?:(?:(?:\\d+:).+)|(?:[^:]+))$", // deb-version-0.1.0: "^(?:(?:(?:\\d+:).+)|(?:[^:]+))$" `^(?i)(a|an|the)\s+`, // debcargo-2.1.0: r"^(?i)(a|an|the)\s+" `^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+`, // debcargo-2.1.0: r"^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+" `^.*\.h$`, // feaders-0.2.0: r"^.*\.h$" `^.*\.c$`, // feaders-0.2.0: r"^.*\.c$" `^.*\.hpp$`, // feaders-0.2.0: r"^.*\.hpp$" `^.*\.cc$`, // feaders-0.2.0: r"^.*\.cc$" `^.*\.cpp$`, // feaders-0.2.0: r"^.*\.cpp$" `CPtr\(\w+\)`, // hyperscan-0.1.6: r"CPtr\(\w+\)" `^Version:\s(\d\.\d\.\d)\sFeatures:\s+(\w+)?\sMode:\s(\w+)$`, // hyperscan-0.1.6: r"^Version:\s(\d\.\d\.\d)\sFeatures:\s+(\w+)?\sMode:\s(\w+)$" `RawDatabase\{db: \w+\}`, // hyperscan-0.1.6: r"RawDatabase\{db: \w+\}" `RawSerializedDatabase\{p: \w+, len: \d+\}`, // hyperscan-0.1.6: r"RawSerializedDatabase\{p: \w+, len: \d+\}" `[0-9A-F]+`, // ucd-parse-0.1.1: r"[0-9A-F]+" `.*`, // afsort-0.2.0: r".*" `.*`, // afsort-0.2.0: r".*" `.*`, // afsort-0.2.0: r".*" `.*`, // afsort-0.2.0: r".*" `.*`, // afsort-0.2.0: r".*" `.*`, // afsort-0.2.0: r".*" `^[a-z]+$`, // afsort-0.2.0: r"^[a-z]+$" `^[a-z]+$`, // afsort-0.2.0: r"^[a-z]+$" `(\.git|\.pijul|_darcs|\.hg)$`, // tin-summer-1.21.4: r"(\.git|\.pijul|_darcs|\.hg)$" `.*?\.(a|la|lo|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // tin-drummer-1.0.1: r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" `.*?\.(stats|conf|h|out|cache.*|dat|pc|info|\.js)$`, // tin-drummer-1.0.1: r".*?\.(stats|conf|h|out|cache.*|dat|pc|info|\.js)$" `.*?\.(exe|a|la|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // tin-drummer-1.0.1: r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" `.*?\.(stats|conf|h|out|cache.*|\.js)$`, // tin-drummer-1.0.1: r".*?\.(stats|conf|h|out|cache.*|\.js)$" `(\.git|\.pijul|_darcs|\.hg)$`, // tin-drummer-1.0.1: r"(\.git|\.pijul|_darcs|\.hg)$" `.*?\.(dyn_o|out|d|hi|dyn_hi|dump-.*|p_hi|p_o|prof|tix)$`, // tin-drummer-1.0.1: r".*?\.(dyn_o|out|d|hi|dyn_hi|dump-.*|p_hi|p_o|prof|tix)$" `.*?\.(ibc)$`, // tin-drummer-1.0.1: r".*?\.(ibc)$" `\.stack-work|dist-newstyle`, // tin-drummer-1.0.1: r"\.stack-work|dist-newstyle" `_NET_WM_PID\(CARDINAL\) = (\d+)`, // timmy-0.3.0: r"_NET_WM_PID\(CARDINAL\) = (\d+)" `today|yesterday|now`, // timmy-0.3.0: r"today|yesterday|now" `(?P\d{1,2})/(?P\d{1,2})(/(?P\d{4}|\d{2}))?`, // timmy-0.3.0: r"(?P\d{1,2})/(?P\d{1,2})(/(?P\d{4}|\d{2}))?" `(?P\d+) (days?|ds?)(?P( ago)?)`, // timmy-0.3.0: r"(?P\d+) (days?|ds?)(?P( ago)?)" `(?P
\d{2}):(?P\d{2})`, // timmy-0.3.0: r"(?P
\d{2}):(?P\d{2})" `^(\d+): \d+ windows \(.*\) \[\d+x\d+\]( \(attached\))?`, // tinfo-0.5.0: r"^(\d+): \d+ windows \(.*\) \[\d+x\d+\]( \(attached\))?" `^(\d+):(\d+): (.*) \((\d+) panes\) \[(\d+)x(\d+)\]`, // tinfo-0.5.0: r"^(\d+):(\d+): (.*) \((\d+) panes\) \[(\d+)x(\d+)\]" `(?:\\\{start\\\}|\\\{end\\\})`, // timespan-0.0.4: r"(?:\\\{start\\\}|\\\{end\\\})" `(.*)\s+-\s+(.*)`, // timespan-0.0.4: r"(.*)\s+-\s+(.*)" `(.*)\s+(\w+)$`, // timespan-0.0.4: r"(.*)\s+(\w+)$" `(.*)\s+(\w+)$`, // timespan-0.0.4: r"(.*)\s+(\w+)$" `(.*)\s+-\s+(.*)`, // timespan-0.0.4: r"(.*)\s+-\s+(.*)" `[[:lower:]]`, // titlecase-0.10.0: r"[[:lower:]]" `^\d+ (day|week|month|year)s?$`, // tight-0.1.3: r"^\d+ (day|week|month|year)s?$" `^\d+ (day|week|month|year)s?$`, // tight-0.1.3: r"^\d+ (day|week|month|year)s?$" `^[-+]?(0|[1-9][0-9_]*)$`, // yaml-0.2.1: r"^[-+]?(0|[1-9][0-9_]*)$" `^([-+]?)0o?([0-7_]+)$`, // yaml-0.2.1: r"^([-+]?)0o?([0-7_]+)$" `^([-+]?)0x([0-9a-fA-F_]+)$`, // yaml-0.2.1: r"^([-+]?)0x([0-9a-fA-F_]+)$" `^([-+]?)0b([0-1_]+)$`, // yaml-0.2.1: r"^([-+]?)0b([0-1_]+)$" `^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$`, // yaml-0.2.1: r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$" `^[+]?(\.inf|\.Inf|\.INF)$`, // yaml-0.2.1: r"^[+]?(\.inf|\.Inf|\.INF)$" `^-(\.inf|\.Inf|\.INF)$`, // yaml-0.2.1: r"^-(\.inf|\.Inf|\.INF)$" `^(\.nan|\.NaN|\.NAN)$`, // yaml-0.2.1: r"^(\.nan|\.NaN|\.NAN)$" `^(null|Null|NULL|~)$`, // yaml-0.2.1: r"^(null|Null|NULL|~)$" `^(true|True|TRUE|yes|Yes|YES)$`, // yaml-0.2.1: r"^(true|True|TRUE|yes|Yes|YES)$" `^(false|False|FALSE|no|No|NO)$`, // yaml-0.2.1: r"^(false|False|FALSE|no|No|NO)$" `(?m)^(\S+)/(\S+) (\S+)(?: \((.*)\))?$`, // kefia-0.1.0: r"(?m)^(\S+)/(\S+) (\S+)(?: \((.*)\))?$" "^(\\s+|;.*?(\n|$))+", // risp-0.7.0: "^(\\s+|;.*?(\n|$))+" "^\".*?\"", // risp-0.7.0: "^\".*?\"" `^[^\s\{\}()\[\]]+`, // risp-0.7.0: r"^[^\s\{\}()\[\]]+" `^-?\d+`, // risp-0.7.0: r"^-?\d+" "^([0-9]+)([KMG])?$", // ripgrep-0.8.1: "^([0-9]+)([KMG])?$" `^\w+`, // riquid-0.0.1: r"^\w+" `^\d+`, // riquid-0.0.1: r"^\d+" `\A(0x)?([a-fA-F0-9]+)\z`, // recursive_disassembler-2.1.2: r"\A(0x)?([a-fA-F0-9]+)\z" `^[a-zA-Z_][a-zA-Z0-9_]*`, // remake-0.1.0: r"^[a-zA-Z_][a-zA-Z0-9_]*" `'(?P[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" "[0-9]{3}-[0-9]{3}-[0-9]{4}", // regex-cache-0.2.0: "[0-9]{3}-[0-9]{3}-[0-9]{4}" `^\d+$`, // regex-cache-0.2.0: r"^\d+$" `^[a-z]+$`, // regex-cache-0.2.0: r"^[a-z]+$" `^\d+$`, // regex-cache-0.2.0: r"^\d+$" `^\d+$`, // regex-cache-0.2.0: r"^\d+$" `\d{4}-\d{2}-\d{2}`, // regex_dfa-0.5.0: r"\d{4}-\d{2}-\d{2}" `^[0-9\p{L} _\\.]{3,16}$`, // reaper-2.0.0: r"^[0-9\p{L} _\\.]{3,16}$" `^attachment; filename=(.+)$`, // retdec-0.1.0: r"^attachment; filename=(.+)$" `(\\)(?P<head>\$[0-9A-Za-z_{])`, // renvsubst-0.1.2: r"(\\)(?P<head>\$[0-9A-Za-z_{])" `\$([[:word:]]+)`, // renvsubst-0.1.2: r"\$([[:word:]]+)" `\$\{([[:word:]]+)\}`, // renvsubst-0.1.2: r"\$\{([[:word:]]+)\}" `'[a-z]+'`, // rexpect-0.3.0: r"'[a-z]+'" `^\d{4}-\d{2}-\d{2}$`, // rexpect-0.3.0: r"^\d{4}-\d{2}-\d{2}$" `-\d{2}-`, // rexpect-0.3.0: r"-\d{2}-" "^a(b|c)c*$", // luther-0.1.0: "^a(b|c)c*$" `(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]`, // little_boxes-1.6.0: r"(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]" "^[a-zA-Z]([a-zA-Z0-9_-]*)$", // libimagentrytag-0.8.0: "^[a-zA-Z]([a-zA-Z0-9_-]*)$" `^[Yy](\n?)$`, // libimaginteraction-0.8.0: r"^[Yy](\n?)$" `^[Nn](\n?)$`, // libimaginteraction-0.8.0: r"^[Nn](\n?)$" "^(?P<KEY>([^=]*))=(.*)$", // libimagutil-0.8.0: "^(?P<KEY>([^=]*))=(.*)$" "(.*)=(\"(?P<QVALUE>([^\"]*))\"|(?P<VALUE>(.*)))$", // libimagutil-0.8.0: "(.*)=(\"(?P<QVALUE>([^\"]*))\"|(?P<VALUE>(.*)))$" `\s+`, // linux_ip-0.1.0: r"\s+" `\s*[\n\r]+\s*`, // linux_ip-0.1.0: r"\s*[\n\r]+\s*" `^([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$`, // linux_ip-0.1.0: r"^([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$" `^([0-9a-fA-F\.:/]+|default)\s+via\s+([a-z0-9\.:]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$`, // linux_ip-0.1.0: r"^([0-9a-fA-F\.:/]+|default)\s+via\s+([a-z0-9\.:]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$" `^(blackhole)\s+([0-9a-fA-F\.:/]+)$`, // linux_ip-0.1.0: r"^(blackhole)\s+([0-9a-fA-F\.:/]+)$" `^(unreachable)\s+([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s+(.*)$`, // linux_ip-0.1.0: r"^(unreachable)\s+([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s+(.*)$" `\s*[\n\r]+\s*`, // linux_ip-0.1.0: r"\s*[\n\r]+\s*" `^\d+:\s+([a-zA-Z0-9\.-]+)(@\S+)*:\s+(.*)$`, // linux_ip-0.1.0: r"^\d+:\s+([a-zA-Z0-9\.-]+)(@\S+)*:\s+(.*)$" `\s*link/ether\s+([a-f0-9:]+)\s+.*`, // linux_ip-0.1.0: r"\s*link/ether\s+([a-f0-9:]+)\s+.*" `\s*inet[6]*\s+([0-9a-f:\./]+)\s+.*`, // linux_ip-0.1.0: r"\s*inet[6]*\s+([0-9a-f:\./]+)\s+.*" `[^\w -]`, // linky-0.1.4: r"[^\w -]" `^(.*):(\d+): [^ ]* ([^ ]*)$`, // linky-0.1.4: r"^(.*):(\d+): [^ ]* ([^ ]*)$" `^(\d{4}-\d{2}-\d{2})-(\d{3})-(.+)$`, // limonite-0.2.1: r"^(\d{4}-\d{2}-\d{2})-(\d{3})-(.+)$" `^[a-zA-Z]+$`, // process-queue-0.1.1: r"^[a-zA-Z]+$" `^\{([a-zA-Z_]+)\}$`, // pronghorn-0.1.2: r"^\{([a-zA-Z_]+)\}$" "(?m:^(\\d{3}) (.+)\r$)", // protocol-ftp-client-0.1.1: "(?m:^(\\d{3}) (.+)\r$)" "\"(.+)\"", // protocol-ftp-client-0.1.1: "\"(.+)\"" "(\\w+) [Tt]ype: (\\w+)", // protocol-ftp-client-0.1.1: "(\\w+) [Tt]ype: (\\w+)" "(?m:^(\\d{3})-.+\r$)", // protocol-ftp-client-0.1.1: "(?m:^(\\d{3})-.+\r$)" "Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)", // protocol-ftp-client-0.1.1: "Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)" "(?m:^(.+)\r$)", // protocol-ftp-client-0.1.1: "(?m:^(.+)\r$)" "^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$", // protocol-ftp-client-0.1.1: "^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$" `([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})`, // article-date-extractor-0.1.1: r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})" `(?i)publishdate|pubdate|timestamp|article_date|articledate|date`, // article-date-extractor-0.1.1: r"(?i)publishdate|pubdate|timestamp|article_date|articledate|date" `type\((.*)\)`, // arthas_plugin-0.1.1: r"type\((.*)\)" `Vec<(.*)>`, // arthas_plugin-0.1.1: r"Vec<(.*)>" `Option<(.*)>`, // arthas_plugin-0.1.1: r"Option<(.*)>" `HashMap<[a-z0-9A-Z]+, *(.*)>`, // arthas_plugin-0.1.1: r"HashMap<[a-z0-9A-Z]+, *(.*)>" "Vec *< *(.*) *>", // arthas_derive-0.1.0: "Vec *< *(.*) *>" `Option *< *(.*) *>`, // arthas_derive-0.1.0: r"Option *< *(.*) *>" `HashMap *< *[a-z0-9A-Z]+ *, *(.*) *>`, // arthas_derive-0.1.0: r"HashMap *< *[a-z0-9A-Z]+ *, *(.*) *>" `^([\w\-\(\)\.']+)\s+([^\s].*)\s*$`, // arpabet-0.2.0: r"^([\w\-\(\)\.']+)\s+([^\s].*)\s*$" `^;;;\s+`, // arpabet-0.2.0: r"^;;;\s+" `/\*.*?\*/|//.*`, // glossy_codegen-0.2.0: r"/\*.*?\*/|//.*" "^\\s*#\\s*include\\s+<([:print:]+)>\\s*$", // glossy_codegen-0.2.0: "^\\s*#\\s*include\\s+<([:print:]+)>\\s*$" "^\\s*#\\s*include\\s+\"([:print:]+)\"\\s*$", // glossy_codegen-0.2.0: "^\\s*#\\s*include\\s+\"([:print:]+)\"\\s*$" `^\s*#\s*version\s+(\d+)`, // glossy_codegen-0.2.0: r"^\s*#\s*version\s+(\d+)" `^\s*$`, // glossy_codegen-0.2.0: r"^\s*$" `(?P<addr>via \S+)`, // gluster-1.0.1: r"(?P<addr>via \S+)" `(?P<src>src \S+)`, // gluster-1.0.1: r"(?P<src>src \S+)" `(.*)\[\d+\]`, // gl_helpers-0.1.7: r"(.*)\[\d+\]" `(\d+).(\d+)`, // gl_helpers-0.1.7: r"(\d+).(\d+)" `(?P<c>[\\\.\+\*\?\(\)\|\[\]\{\}\^\$])`, // glr-parser-0.0.1: r"(?P<c>[\\\.\+\*\?\(\)\|\[\]\{\}\^\$])" `^\w+$`, // glr-parser-0.0.1: r"^\w+$" "'[^']+'", // glr-parser-0.0.1: "'[^']+'" `(?m)//.*`, // hoodlum-0.5.0: r"(?m)//.*" `^1\d{10}$`, // form-checker-0.2.2: r"^1\d{10}$" `(?i)^[\w.%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$`, // form-checker-0.2.2: r"(?i)^[\w.%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$" `(?P<user_agent>[a-zA-Z0-9-_]+/[0-9\.]+)`, // wikibase-0.2.0: r"(?P<user_agent>[a-zA-Z0-9-_]+/[0-9\.]+)" `Cell [0-9]{2,} - Address:`, // wifiscanner-0.3.6: r"Cell [0-9]{2,} - Address:" `([0-9a-zA-Z]{1}[0-9a-zA-Z]{1}[:]{1}){5}[0-9a-zA-Z]{1}[0-9a-zA-Z]{1}`, // wifiscanner-0.3.6: r"([0-9a-zA-Z]{1}[0-9a-zA-Z]{1}[:]{1}){5}[0-9a-zA-Z]{1}[0-9a-zA-Z]{1}" `Signal level=(\d+)/100`, // wifiscanner-0.3.6: r"Signal level=(\d+)/100" `(?s)\[b\](.*?)\[/b\]`, // bbcode-1.0.2: r"(?s)\[b\](.*?)\[/b\]" `(?s)\[i\](.*?)\[/i\]`, // bbcode-1.0.2: r"(?s)\[i\](.*?)\[/i\]" `(?s)\[u\](.*?)\[/u\]`, // bbcode-1.0.2: r"(?s)\[u\](.*?)\[/u\]" `(?s)\[s\](.*?)\[/s\]`, // bbcode-1.0.2: r"(?s)\[s\](.*?)\[/s\]" `(?s)\[size=(\d+)](.*?)\[/size\]`, // bbcode-1.0.2: r"(?s)\[size=(\d+)](.*?)\[/size\]" `(?s)\[color=(.+)](.*?)\[/color\]`, // bbcode-1.0.2: r"(?s)\[color=(.+)](.*?)\[/color\]" `(?s)\[center\](.*?)\[/center\]`, // bbcode-1.0.2: r"(?s)\[center\](.*?)\[/center\]" `(?s)\[left\](.*?)\[/left\]`, // bbcode-1.0.2: r"(?s)\[left\](.*?)\[/left\]" `(?s)\[right\](.*?)\[/right\]`, // bbcode-1.0.2: r"(?s)\[right\](.*?)\[/right\]" `(?s)\[table\](.*?)\[/table\]`, // bbcode-1.0.2: r"(?s)\[table\](.*?)\[/table\]" `(?s)\[td\](.*?)\[/td\]`, // bbcode-1.0.2: r"(?s)\[td\](.*?)\[/td\]" `(?s)\[tr\](.*?)\[/tr\]`, // bbcode-1.0.2: r"(?s)\[tr\](.*?)\[/tr\]" `(?s)\[th\](.*?)\[/th\]`, // bbcode-1.0.2: r"(?s)\[th\](.*?)\[/th\]" `(?s)\[url\](.*?)\[/url\]`, // bbcode-1.0.2: r"(?s)\[url\](.*?)\[/url\]" `(?s)\[url=(.+)\](.*?)\[/url\]`, // bbcode-1.0.2: r"(?s)\[url=(.+)\](.*?)\[/url\]" `(?s)\[quote\](.*?)\[/quote\]`, // bbcode-1.0.2: r"(?s)\[quote\](.*?)\[/quote\]" `(?s)\[quote=(.+)\](.*?)\[/quote\]`, // bbcode-1.0.2: r"(?s)\[quote=(.+)\](.*?)\[/quote\]" `(?s)\[img=(\d+)x(\d+)(\b.*)?\](.*?)\[/img\]`, // bbcode-1.0.2: r"(?s)\[img=(\d+)x(\d+)(\b.*)?\](.*?)\[/img\]" `(?s)\[img=(.+)(\b.*)?\](.*?)\[/img\]`, // bbcode-1.0.2: r"(?s)\[img=(.+)(\b.*)?\](.*?)\[/img\]" `(?s)\[img(\b.*)?\](.*?)\[/img\]`, // bbcode-1.0.2: r"(?s)\[img(\b.*)?\](.*?)\[/img\]" `(?s)\[ol\](.*?)\[/ol\]`, // bbcode-1.0.2: r"(?s)\[ol\](.*?)\[/ol\]" `(?s)\[ul\](.*?)\[/ul\]`, // bbcode-1.0.2: r"(?s)\[ul\](.*?)\[/ul\]" `(?s)\[list\](.*?)\[/list\]`, // bbcode-1.0.2: r"(?s)\[list\](.*?)\[/list\]" `(?s)\[youtube\](.*?)\[/youtube\]`, // bbcode-1.0.2: r"(?s)\[youtube\](.*?)\[/youtube\]" `(?s)\[youtube=(\d+)x(\d+)\](.*?)\[/youtube\]`, // bbcode-1.0.2: r"(?s)\[youtube=(\d+)x(\d+)\](.*?)\[/youtube\]" `(?s)\[li\](.*?)\[/li\]`, // bbcode-1.0.2: r"(?s)\[li\](.*?)\[/li\]" `loop\d+`, // block-utils-0.5.0: r"loop\d+" `ram\d+`, // block-utils-0.5.0: r"ram\d+" `md\d+`, // block-utils-0.5.0: r"md\d+" `^([1-9]) min$`, // kvvliveapi-0.1.0: r"^([1-9]) min$" `(\d{2}):(\d{2}):(\d{2})`, // rfc822_sanitizer-0.3.3: r"(\d{2}):(\d{2}):(\d{2})" `(\d{1,2}):(\d{1,2}):(\d{1,2})`, // rfc822_sanitizer-0.3.3: r"(\d{1,2}):(\d{1,2}):(\d{1,2})" `[2-9]`, // faker-0.0.4: r"[2-9]" `[1-9]`, // faker-0.0.4: r"[1-9]" `[0-9]`, // faker-0.0.4: r"[0-9]" `\d{10}`, // faker-0.0.4: r"\d{10}" `\d{1}`, // faker-0.0.4: r"\d{1}" `^\w+`, // faker-0.0.4: r"^\w+" `^\w+`, // faker-0.0.4: r"^\w+" `^(\w+\.? ?){2,3}$`, // faker-0.0.4: r"^(\w+\.? ?){2,3}$" `^[A-Z][a-z]+\.?$`, // faker-0.0.4: r"^[A-Z][a-z]+\.?$" `^[A-Z][A-Za-z]*\.?$`, // faker-0.0.4: r"^[A-Z][A-Za-z]*\.?$" `http://lorempixel.com/100/100/\w+`, // faker-0.0.4: r"http://lorempixel.com/100/100/\w+" `http://lorempixel.com/100/100/cats`, // faker-0.0.4: r"http://lorempixel.com/100/100/cats" "(?i:ß)", // fancy-regex-0.1.0: "(?i:ß)" "(?i:\\x{0587})", // fancy-regex-0.1.0: "(?i:\\x{0587})" "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})", // fancy-regex-0.1.0: "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})" `/([^/])[^/]+/`, // fancy-prompt-0.1.5: r"/([^/])[^/]+/" `^([^:]+):.*?(?::([^:]+))?$`, // fancy-prompt-0.1.5: r"^([^:]+):.*?(?::([^:]+))?$" `^(/?__\w+__)/(.*)`, // fanta-0.2.0: r"^(/?__\w+__)/(.*)" `(.)([A-Z])`, // fanta-cli-0.1.1: r"(.)([A-Z])" "\\{:[^\\s]+\\}", // fanta-cli-0.1.1: "\\{:[^\\s]+\\}" "(?P<last>[^\r])\n", // amethyst_tools-0.7.1: "(?P<last>[^\r])\n" `^-?\d+(\.\d)?`, // amigo-0.3.1: r"^-?\d+(\.\d)?" `^[a-zA-Z_]+[\w-]*[!?_]?`, // amigo-0.3.1: r"^[a-zA-Z_]+[\w-]*[!?_]?" `^\(`, // amigo-0.3.1: r"^\(" `^\)`, // amigo-0.3.1: r"^\)" `^\s+`, // amigo-0.3.1: r"^\s+" "\x1b\\[[^m]+m", // ethcore-logger-1.12.0: "\x1b\\[[^m]+m" `__.*?__`, // dash2html-1.0.1: r"__.*?__" `(?i)@(?:time|clipboard|cursor|date)`, // dash2html-1.0.1: r"(?i)@(?:time|clipboard|cursor|date)" `^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$`, // os_type-2.0.0: r"^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$" `ProductName:\s([\w\s]+)\n`, // os_type-2.0.0: r"ProductName:\s([\w\s]+)\n" `ProductVersion:\s(\w+\.\w+\.\w+)`, // os_type-2.0.0: r"ProductVersion:\s(\w+\.\w+\.\w+)" `BuildVersion:\s(\w+)`, // os_type-2.0.0: r"BuildVersion:\s(\w+)" `(\w+) Linux release`, // os_type-2.0.0: r"(\w+) Linux release" `release\s([\w\.]+)`, // os_type-2.0.0: r"release\s([\w\.]+)" `Distributor ID:\s(\w+)`, // os_type-2.0.0: r"Distributor ID:\s(\w+)" `Release:\s([\w\.]+)`, // os_type-2.0.0: r"Release:\s([\w\.]+)" `typename type\-parameter\-\d+\-\d+::.+`, // bindgen-0.37.0: r"typename type\-parameter\-\d+\-\d+::.+" "^+(.*)\r\n", // imap-0.8.1: "^+(.*)\r\n" `^ffd8ffe0`, // image-base64-0.1.0: r"^ffd8ffe0" `^89504e47`, // image-base64-0.1.0: r"^89504e47" `^47494638`, // image-base64-0.1.0: r"^47494638" "^(/([^/~]|~[01])*)*$", // json-pointer-0.3.2: "^(/([^/~]|~[01])*)*$" "^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$", // json-pointer-0.3.2: "^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$" `^5.5.5-(\d{1,2})\.(\d{1,2})\.(\d{1,3})-MariaDB`, // mysql_common-0.7.0: r"^5.5.5-(\d{1,2})\.(\d{1,2})\.(\d{1,3})-MariaDB" `^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)`, // mysql_common-0.7.0: r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)" `^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$`, // government_id-0.1.0: r"^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$" `UniqueIndexViolation: (\w+)`, // ohmers-0.1.1: r"UniqueIndexViolation: (\w+)" `(.*) you are (.*)`, // eliza-1.0.0: r"(.*) you are (.*)" `(.*) you are (.*)`, // eliza-1.0.0: r"(.*) you are (.*)" `(.*) you are (.*)`, // eliza-1.0.0: r"(.*) you are (.*)" "^\\s*\\*", // chema-0.0.5: "^\\s*\\*" "^\\s*@(\\w+)\\s+(.*)", // chema-0.0.5: "^\\s*@(\\w+)\\s+(.*)" `^\s*#`, // chord3-0.3.0: r"^\s*#" `\{(?P<cmd>\w+)(?::?\s*(?P<arg>.*))?\}`, // chord3-0.3.0: r"\{(?P<cmd>\w+)(?::?\s*(?P<arg>.*))?\}" `\{(eot|end_of_tab):?\s*`, // chord3-0.3.0: r"\{(eot|end_of_tab):?\s*" `([^\[]*)(?:\[([^\]]*)\])?`, // chord3-0.3.0: r"([^\[]*)(?:\[([^\]]*)\])?" "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", // checkmail-0.1.1: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" `\b\w\w+\b`, // cntk-0.2.1: r"\b\w\w+\b" `\b\w\w+\b`, // cntk-0.2.1: r"\b\w\w+\b" `\(id: (\d+)\)`, // cniguru-0.1.0: r"\(id: (\d+)\)" `^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$`, // upm_lib-0.3.0: r"^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$" `^\s*(\*+(\s+))?`, // avro-0.2.1: r"^\s*(\*+(\s+))?" `^\s*(\*+)?`, // avro-0.2.1: r"^\s*(\*+)?" "[0-9]+", // nomi-0.0.2: "[0-9]+" "([0-9]+)@(?:nodes|n)?:([^@]+)?", // nodes-0.1.0: "([0-9]+)@(?:nodes|n)?:([^@]+)?" `(?i)in (\d+) (second|minute|hour|day|week)s?`, // not-stakkr-1.0.0: r"(?i)in (\d+) (second|minute|hour|day|week)s?" "^([A-Za-z0-9 -_:]+)\n-+\n", // notetxt-0.0.1: "^([A-Za-z0-9 -_:]+)\n-+\n" `^-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?$`, // nail-0.1.0-pre.0: r"^-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?$" `^-?[0-9]+$`, // nail-0.1.0-pre.0: r"^-?[0-9]+$" `[^\w\s\pP]+`, // askalono-0.2.0: r"[^\w\s\pP]+" `[ \t\p{Zs} \\ / \| \x2044 ]+`, // askalono-0.2.0: r"(?x)[ \t\p{Zs} \\ / \| \x2044 ]+" `\p{Pd}+`, // askalono-0.2.0: r"\p{Pd}+" `\p{Ps}+`, // askalono-0.2.0: r"\p{Ps}+" `\p{Pe}+`, // askalono-0.2.0: r"\p{Pe}+" `\p{Pc}+`, // askalono-0.2.0: r"\p{Pc}+" `[©Ⓒⓒ]`, // askalono-0.2.0: r"[©Ⓒⓒ]" `[\r\n\v\f]`, // askalono-0.2.0: r"[\r\n\v\f]" `\n{3,}`, // askalono-0.2.0: r"\n{3,}" `[^\w\s]+`, // askalono-0.2.0: r"[^\w\s]+" `\s+`, // askalono-0.2.0: r"\s+" `[^0-9a-zA-Z_]`, // assembunny_plus-0.0.3: r"[^0-9a-zA-Z_]" `[0-9]`, // assembunny_plus-0.0.3: r"[0-9]" `(?m)^Minion (\S*) did not respond\. No job will be sent\.$`, // salt-compressor-0.4.0: r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$" `</?[^>]+?>`, // sabisabi-0.4.1: r"</?[^>]+?>" `\([^)]*\)`, // sabisabi-0.4.1: r"\([^)]*\)" "@import \"([^\"]*)\";", // sassers-0.13.5-h28: "@import \"([^\"]*)\";" `[A-Za-z\d-]{1,63}$`, // shadowsocks-0.6.2: r"[A-Za-z\d-]{1,63}$" "[abc]+", // shkeleton-0.1.5: "[abc]+" `([^A-Za-z0-9_\-.,:/@\n])`, // shellwords-0.1.0: r"([^A-Za-z0-9_\-.,:/@\n])" `\n`, // shellwords-0.1.0: r"\n" "(?P<num>[0-9]+)(?P<units>[dhms])", // shush-0.1.5: "(?P<num>[0-9]+)(?P<units>[dhms])" `(?:Chrome|CrMo|CriOS)/([.0-9]+)`, // woothee-0.8.0: r"(?:Chrome|CrMo|CriOS)/([.0-9]+)" `Vivaldi/([.0-9]+)`, // woothee-0.8.0: r"Vivaldi/([.0-9]+)" `Firefox/([.0-9]+)`, // woothee-0.8.0: r"Firefox/([.0-9]+)" `^Mozilla/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko/[.0-9]+ Firefox/[.0-9]+$`, // woothee-0.8.0: r"^Mozilla/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko/[.0-9]+ Firefox/[.0-9]+$" `FxiOS/([.0-9]+)`, // woothee-0.8.0: r"FxiOS/([.0-9]+)" `\(([^;)]+);FOMA;`, // woothee-0.8.0: r"\(([^;)]+);FOMA;" `jig browser[^;]+; ([^);]+)`, // woothee-0.8.0: r"jig browser[^;]+; ([^);]+)" `(?i)rss(?:reader|bar|[-_ /;()]|[ +]*/)`, // woothee-0.8.0: r"(?i)rss(?:reader|bar|[-_ /;()]|[ +]*/)" `(?i)(?:bot|crawler|spider)(?:[-_ ./;@()]|$)`, // woothee-0.8.0: r"(?i)(?:bot|crawler|spider)(?:[-_ ./;@()]|$)" `(?i)(?:feed|web) ?parser`, // woothee-0.8.0: r"(?i)(?:feed|web) ?parser" `(?i)watch ?dog`, // woothee-0.8.0: r"(?i)watch ?dog" `Edge/([.0-9]+)`, // woothee-0.8.0: r"Edge/([.0-9]+)" `MSIE ([.0-9]+);`, // woothee-0.8.0: r"MSIE ([.0-9]+);" `Version/([.0-9]+)`, // woothee-0.8.0: r"Version/([.0-9]+)" `Opera[/ ]([.0-9]+)`, // woothee-0.8.0: r"Opera[/ ]([.0-9]+)" `OPR/([.0-9]+)`, // woothee-0.8.0: r"OPR/([.0-9]+)" `Version/([.0-9]+)`, // woothee-0.8.0: r"Version/([.0-9]+)" `(?:SoftBank|Vodafone|J-PHONE)/[.0-9]+/([^ /;()]+)`, // woothee-0.8.0: r"(?:SoftBank|Vodafone|J-PHONE)/[.0-9]+/([^ /;()]+)" `Trident/([.0-9]+);`, // woothee-0.8.0: r"Trident/([.0-9]+);" ` rv:([.0-9]+)`, // woothee-0.8.0: r" rv:([.0-9]+)" `IEMobile/([.0-9]+);`, // woothee-0.8.0: r"IEMobile/([.0-9]+);" `(?:WILLCOM|DDIPOCKET);[^/]+/([^ /;()]+)`, // woothee-0.8.0: r"(?:WILLCOM|DDIPOCKET);[^/]+/([^ /;()]+)" `Windows ([ .a-zA-Z0-9]+)[;\\)]`, // woothee-0.8.0: r"Windows ([ .a-zA-Z0-9]+)[;\\)]" `^Phone(?: OS)? ([.0-9]+)`, // woothee-0.8.0: r"^Phone(?: OS)? ([.0-9]+)" `iP(hone;|ad;|od) .*like Mac OS X`, // woothee-0.8.0: r"iP(hone;|ad;|od) .*like Mac OS X" `Version/([.0-9]+)`, // woothee-0.8.0: r"Version/([.0-9]+)" `rv:(\d+\.\d+\.\d+)`, // woothee-0.8.0: r"rv:(\d+\.\d+\.\d+)" `FreeBSD ([^;\)]+);`, // woothee-0.8.0: r"FreeBSD ([^;\)]+);" `CrOS ([^\)]+)\)`, // woothee-0.8.0: r"CrOS ([^\)]+)\)" `Android[- ](\d+\.\d+(?:\.\d+)?)`, // woothee-0.8.0: r"Android[- ](\d+\.\d+(?:\.\d+)?)" `PSP \(PlayStation Portable\); ([.0-9]+)\)`, // woothee-0.8.0: r"PSP \(PlayStation Portable\); ([.0-9]+)\)" `PLAYSTATION 3;? ([.0-9]+)\)`, // woothee-0.8.0: r"PLAYSTATION 3;? ([.0-9]+)\)" `PlayStation Vita ([.0-9]+)\)`, // woothee-0.8.0: r"PlayStation Vita ([.0-9]+)\)" `PlayStation 4 ([.0-9]+)\)`, // woothee-0.8.0: r"PlayStation 4 ([.0-9]+)\)" `BB10(?:.+)Version/([.0-9]+) `, // woothee-0.8.0: r"BB10(?:.+)Version/([.0-9]+) " `BlackBerry(?:\d+)/([.0-9]+) `, // woothee-0.8.0: r"BlackBerry(?:\d+)/([.0-9]+) " `; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X`, // woothee-0.8.0: r"; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X" `Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)`, // woothee-0.8.0: r"Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)" `^(?:Apache-HttpClient/|Jakarta Commons-HttpClient/|Java/)`, // woothee-0.8.0: r"^(?:Apache-HttpClient/|Jakarta Commons-HttpClient/|Java/)" `[- ]HttpClient(/|$)`, // woothee-0.8.0: r"[- ]HttpClient(/|$)" `^(?:PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:/| |$)`, // woothee-0.8.0: r"^(?:PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:/| |$)" `(?:PEAR HTTP_Request|HTTP_Request)(?: class|2)`, // woothee-0.8.0: r"(?:PEAR HTTP_Request|HTTP_Request)(?: class|2)" `(?:Rome Client |UnwindFetchor/|ia_archiver |Summify |PostRank/)`, // woothee-0.8.0: r"(?:Rome Client |UnwindFetchor/|ia_archiver |Summify |PostRank/)" `Sleipnir/([.0-9]+)`, // woothee-0.8.0: r"Sleipnir/([.0-9]+)" `@@[a-z|A-Z|\d]+@@`, // word_replace-0.0.3: r"@@[a-z|A-Z|\d]+@@" `\w+`, // wordcount-0.1.0: r"\w+" "^([^=]+)=(.*)$", // just-0.3.12: "^([^=]+)=(.*)$" `:[a-zA-Z_]+?:`, // emote-0.1.0: r":[a-zA-Z_]+?:" `:([a-zA-Z0-9_+-]+):`, // emojicons-1.0.1: r":([a-zA-Z0-9_+-]+):" `git-codecommit\.([a-z0-9-]+)\.amazonaws\.com`, // git2_codecommit-0.1.2: r"git-codecommit\.([a-z0-9-]+)\.amazonaws\.com" `^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$`, // git-workarea-3.1.2: r"^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$" `^(?P<command>git-(?:receive|upload)-pack) '(?P<path>.+)'$`, // git-shell-enforce-directory-1.0.0: r"^(?P<command>git-(?:receive|upload)-pack) '(?P<path>.+)'$" `[ \n]:(.*?):`, // git-journal-1.6.3: r"[ \n]:(.*?):" `^git@(?P<host>[[:alnum:]\._-]+):(?P<path>[[:alnum:]\._\-/]+).git$`, // git-find-0.3.2: r"^git@(?P<host>[[:alnum:]\._-]+):(?P<path>[[:alnum:]\._\-/]+).git$" `private_token=\w{20}`, // gitlab-api-0.6.0: r"private_token=\w{20}" "^(http://|https://)", // td-client-0.7.0: "^(http://|https://)" `--(?P<type>[a-zA-Z]+)-- (?P<contents>.*)`, // karaconv-0.3.0: r"--(?P<type>[a-zA-Z]+)-- (?P<contents>.*)" `(?P<comp>et al\.)(?:\.)`, // katana-1.0.2: r"(?P<comp>et al\.)(?:\.)" `\.{3}`, // katana-1.0.2: r"\.{3}" `(?P<number>[0-9]+)\.(?P<decimal>[0-9]+)`, // katana-1.0.2: r"(?P<number>[0-9]+)\.(?P<decimal>[0-9]+)" `\s\.(?P<nums>[0-9]+)`, // katana-1.0.2: r"\s\.(?P<nums>[0-9]+)" `(?:[A-Za-z]\.){2,}`, // katana-1.0.2: r"(?:[A-Za-z]\.){2,}" `(?P<init>[A-Z])(?P<point>\.)`, // katana-1.0.2: r"(?P<init>[A-Z])(?P<point>\.)" `(?P<title>[A-Z][a-z]{1,3})(\.)`, // katana-1.0.2: r"(?P<title>[A-Z][a-z]{1,3})(\.)" `&==&(?P<p>[.!?])`, // katana-1.0.2: r"&==&(?P<p>[.!?])" `&\^&(?P<p>[.!?])`, // katana-1.0.2: r"&\^&(?P<p>[.!?])" `&\*\*&(?P<p>[.!?])`, // katana-1.0.2: r"&\*\*&(?P<p>[.!?])" `&=&(?P<p>[.!?])`, // katana-1.0.2: r"&=&(?P<p>[.!?])" `&##&(?P<p>[.!?])`, // katana-1.0.2: r"&##&(?P<p>[.!?])" `&\$&(?P<p>[.!?])`, // katana-1.0.2: r"&\$&(?P<p>[.!?])" `@(?:_|\d+(?:/\d+(?:-\d+)?)?)`, // kailua_syntax-1.1.0: r"@(?:_|\d+(?:/\d+(?:-\d+)?)?)" `<(\d+)>`, // kailua_syntax-1.1.0: r"<(\d+)>" `\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)`, // ftp-3.0.1: r"\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)" `\b(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\b`, // ftp-3.0.1: r"\b(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\b" `\s+(\d+)\s*$`, // ftp-3.0.1: r"\s+(\d+)\s*$" `<countryCode>(.*?)</countryCode>`, // vat-0.1.0: r"<countryCode>(.*?)</countryCode>" `<vatNumber>(.*?)</vatNumber>`, // vat-0.1.0: r"<vatNumber>(.*?)</vatNumber>" `<name>(.*?)</name>`, // vat-0.1.0: r"<name>(.*?)</name>" `<address>(?s)(.*?)(?-s)</address>`, // vat-0.1.0: r"<address>(?s)(.*?)(?-s)</address>" `<valid>(true|false)</valid>`, // vat-0.1.0: r"<valid>(true|false)</valid>" `^ATU\d{8}$`, // vat-0.1.0: r"^ATU\d{8}$" `^BE0?\d{9, 10}$`, // vat-0.1.0: r"^BE0?\d{9, 10}$" `^BG\d{9,10}$`, // vat-0.1.0: r"^BG\d{9,10}$" `^HR\d{11}$`, // vat-0.1.0: r"^HR\d{11}$" `^CY\d{8}[A-Z]$`, // vat-0.1.0: r"^CY\d{8}[A-Z]$" `^CZ\d{8,10}$`, // vat-0.1.0: r"^CZ\d{8,10}$" `^DK\d{8}$`, // vat-0.1.0: r"^DK\d{8}$" `^EE\d{9}$`, // vat-0.1.0: r"^EE\d{9}$" `^FI\d{8}$`, // vat-0.1.0: r"^FI\d{8}$" `^FR[A-HJ-NP-Z0-9][A-HJ-NP-Z0-9]\d{9}$`, // vat-0.1.0: r"^FR[A-HJ-NP-Z0-9][A-HJ-NP-Z0-9]\d{9}$" `^DE\d{9}$`, // vat-0.1.0: r"^DE\d{9}$" `^EL\d{9}$`, // vat-0.1.0: r"^EL\d{9}$" `^HU\d{8}$`, // vat-0.1.0: r"^HU\d{8}$" `^IE\d[A-Z0-9\+\*]\d{5}[A-Z]{1,2}$`, // vat-0.1.0: r"^IE\d[A-Z0-9\+\*]\d{5}[A-Z]{1,2}$" `^IT\d{11}$`, // vat-0.1.0: r"^IT\d{11}$" `^LV\d{11}$`, // vat-0.1.0: r"^LV\d{11}$" `^LT(\d{9}|\d{12})$`, // vat-0.1.0: r"^LT(\d{9}|\d{12})$" `^LU\d{8}$`, // vat-0.1.0: r"^LU\d{8}$" `^MT\d{8}$`, // vat-0.1.0: r"^MT\d{8}$" `^NL\d{9}B\d{2}$`, // vat-0.1.0: r"^NL\d{9}B\d{2}$" `^PL\d{10}$`, // vat-0.1.0: r"^PL\d{10}$" `^PT\d{9}$`, // vat-0.1.0: r"^PT\d{9}$" `^RO\d{2,10}$`, // vat-0.1.0: r"^RO\d{2,10}$" `^SK\d{10}$`, // vat-0.1.0: r"^SK\d{10}$" `^SI\d{8}$`, // vat-0.1.0: r"^SI\d{8}$" `^ES[A-Z0-9]\d{7}[A-Z0-9]$`, // vat-0.1.0: r"^ES[A-Z0-9]\d{7}[A-Z0-9]$" `^SE\d{10}01$`, // vat-0.1.0: r"^SE\d{10}01$" `^(GB(GD|HA)\d{3}|GB\d{9}|GB\d{12})$`, // vat-0.1.0: r"^(GB(GD|HA)\d{3}|GB\d{9}|GB\d{12})$" `\{\{(.*)\}\}`, // eve-0.1.1: r"\{\{(.*)\}\}" "^mio", // egc-0.1.2: "^mio" "", // pew-0.2.3: "" "", // pew-0.2.3: "" "y", // mob-0.4.3: "y" "@([a-z]+)", // lit-0.2.8: "@([a-z]+)" "([A-Z-]+):(.*)", // lit-0.2.8: "([A-Z-]+):(.*)" "^[a-zA-Z_][a-zA-Z0-9_]*$", // lit-0.2.8: "^[a-zA-Z_][a-zA-Z0-9_]*$" `\d+\.\d+\.\d+`, // avm-1.0.1: r"\d+\.\d+\.\d+" `\d+\.\d+\.\d+`, // avm-1.0.1: r"\d+\.\d+\.\d+" `^Vec<(.+)>$`, // orm-0.2.0: r"^Vec<(.+)>$" `\\(\r\n|\n\r|\n|\r)`, // sgf-0.1.5: r"\\(\r\n|\n\r|\n|\r)" `\\(.)`, // sgf-0.1.5: r"\\(.)" `\r\n|\n\r|\n|\r`, // sgf-0.1.5: r"\r\n|\n\r|\n|\r" `([\]\\:])`, // sgf-0.1.5: r"([\]\\:])" "^Bearer realm=\"(.+?)\",service=\"(.+?)\",scope=\"(.+?)\"$", // dok-0.2.0: "^Bearer realm=\"(.+?)\",service=\"(.+?)\",scope=\"(.+?)\"$" `([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)`, // d20-0.1.0: r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)" "E", // dvb-0.3.0: "E" "^F", // dvb-0.3.0: "^F" "^S", // dvb-0.3.0: "^S" `Change-Id: (I[a-f0-9]{40})$`, // ger-0.2.0: r"Change-Id: (I[a-f0-9]{40})$" `(refs|ref|fix|fixes|close|closes)\s+([A-Z]{2,5}-[0-9]{1,5})$`, // ger-0.2.0: r"(refs|ref|fix|fixes|close|closes)\s+([A-Z]{2,5}-[0-9]{1,5})$" `(\d+)(\.(\d+))?(\.(\d+))?(.*)`, // n5-0.2.1: r"(\d+)(\.(\d+))?(\.(\d+))?(.*)" `[A-Za-z0-9]`, // po-0.1.4: r"[A-Za-z0-9]" "path is (‘|')?([^’'\n]*)(’|')?", // carnix-0.8.5: "path is (‘|')?([^’'\n]*)(’|')?" `^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?(.*)?`, // carnix-0.8.5: r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?(.*)?" `(\d*)\.(\d*)\.(\d*)(-(\S*))?`, // carnix-0.8.5: r"(\d*)\.(\d*)\.(\d*)(-(\S*))?" `(\S*)-(\d*)\.(\d*)\.(\d*)(-(\S*))?`, // carnix-0.8.5: r"(\S*)-(\d*)\.(\d*)\.(\d*)(-(\S*))?" `^# CaseFolding-(\d+)\.(\d+)\.(\d+).txt$`, // caseless-0.2.1: r"^# CaseFolding-(\d+)\.(\d+)\.(\d+).txt$" `^([0-9A-F]+); [CF]; ([0-9A-F ]+);`, // caseless-0.2.1: r"^([0-9A-F]+); [CF]; ([0-9A-F ]+);" "\r?\n\r?\n", // cabot-0.2.0: "\r?\n\r?\n" "\r?\n", // cabot-0.2.0: "\r?\n" `^600`, // card-validate-2.2.1: r"^600" `^5019`, // card-validate-2.2.1: r"^5019" `^4`, // card-validate-2.2.1: r"^4" `^(5[1-5]|2[2-7])`, // card-validate-2.2.1: r"^(5[1-5]|2[2-7])" `^3[47]`, // card-validate-2.2.1: r"^3[47]" `^3[0689]`, // card-validate-2.2.1: r"^3[0689]" `^6([045]|22)`, // card-validate-2.2.1: r"^6([045]|22)" `^(62|88)`, // card-validate-2.2.1: r"^(62|88)" `^35`, // card-validate-2.2.1: r"^35" `^[0-9]+$`, // card-validate-2.2.1: r"^[0-9]+$" `\d{1,} passed.*filtered out`, // cargo-testify-0.3.0: r"\d{1,} passed.*filtered out" `error(:|\[).*`, // cargo-testify-0.3.0: r"error(:|\[).*" `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" `(?m)^incremental: re-using (\d+) out of (\d+) modules$`, // cargo-incremental-0.1.23: r"(?m)^incremental: re-using (\d+) out of (\d+) modules$" "(?m)(warning|error): (.*)\n --> ([^:]:\\d+:\\d+)$", // cargo-incremental-0.1.23: "(?m)(warning|error): (.*)\n --> ([^:]:\\d+:\\d+)$" `(?m)^test (.*) \.\.\. (\w+)`, // cargo-incremental-0.1.23: r"(?m)^test (.*) \.\.\. (\w+)" `(?m)(\d+) passed; (\d+) failed; (\d+) ignored; \d+ measured`, // cargo-incremental-0.1.23: r"(?m)(\d+) passed; (\d+) failed; (\d+) ignored; \d+ measured" `^[^-]+-[0-9a-f]+\.js$`, // cargo-testjs-0.1.2: r"^[^-]+-[0-9a-f]+\.js$" `\s*//`, // cargo-tarpaulin-0.6.2: r"\s*//" `/\*`, // cargo-tarpaulin-0.6.2: r"/\*" `\*/`, // cargo-tarpaulin-0.6.2: r"\*/" `^fo`, // cargo-culture-kit-0.1.0: r"^fo" "\\s+", // cargo-screeps-0.1.3: "\\s+" "`(\\S+) v([0-9.]+)", // cargo-brew-0.1.4: r"`(\S+) v([0-9.]+)" "^\\[.+\\]", // cargo-release-0.10.2: "^\\[.+\\]" "^\\[\\[.+\\]\\]", // cargo-release-0.10.2: "^\\[\\[.+\\]\\]" `^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$`, // cargo-edit-0.3.0-beta.1: r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$" `^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$`, // cargo-edit-0.3.0-beta.1: r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$" ".*", // cargo-disassemble-0.1.1: ".*" `(?m)(?P<symbol>_ZN[0-9]+.*E)`, // cargo-demangle-0.1.2: r"(?m)(?P<symbol>_ZN[0-9]+.*E)" `^\s*\}(?:\)*;?|\s*else\s*\{)$`, // cargo-coverage-annotations-0.1.5: r"^\s*\}(?:\)*;?|\s*else\s*\{)$" "[\u001b\u009b][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]", // cargo-urlcrate-1.0.1: "[\u{001b}\u{009b}][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]" `^\s*\*( |$)`, // cargo-script-0.2.8: r"^\s*\*( |$)" `^(\s+)`, // cargo-script-0.2.8: r"^(\s+)" `/\*|\*/`, // cargo-script-0.2.8: r"/\*|\*/" `^\s*//!`, // cargo-script-0.2.8: r"^\s*//!" `^#![^\[].*?(\r\n|\n)`, // cargo-script-0.2.8: r"^#![^\[].*?(\r\n|\n)" `cargo-install-update\.exe-v.+`, // cargo-update-1.5.2: r"cargo-install-update\.exe-v.+" `^<(?:(int|uint|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$`, // canteen-0.4.1: r"^<(?:(int|uint|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$" `(.)([A-Z])`, // thruster-cli-0.1.3: r"(.)([A-Z])" "([Z]+)$", // thieves-cant-0.1.0: "([Z]+)$" `^@\S+/\S+`, // codeowners-0.1.3: r"^@\S+/\S+" `^@\S+`, // codeowners-0.1.3: r"^@\S+" `^\S+@\S+`, // codeowners-0.1.3: r"^\S+@\S+" `^b0000 {21} complete 20[-0-9T:+]+\s +\d+s\n$`, // conserve-0.4.2: r"^b0000 {21} complete 20[-0-9T:+]+\s +\d+s\n$" `(?P<greeting>\S+?) (?P<name>\S+?)$`, // commodore-0.3.0: r"(?P<greeting>\S+?) (?P<name>\S+?)$" "([ \\t]*)```haskell([\\s\\S]*?)```", // corollary-0.3.0: r"([ \t]*)```haskell([\s\S]*?)```" `\b((?:a|b|t)\d*)\b`, // corollary-0.3.0: r"\b((?:a|b|t)\d*)\b" "NB", // colorizex-0.1.3: "NB" `(?i)\[[a-z0-9_-]+\]`, // colorstring-0.0.1: r"(?i)\[[a-z0-9_-]+\]" `^(?i)(\[[a-z0-9_-]+\])+`, // colorstring-0.0.1: r"^(?i)(\[[a-z0-9_-]+\])+" "name:(.+)", // cosmogony-0.3.0: "name:(.+)" `(?m:^ {0,3}\[[^\]]+\]:.+$)`, // cobalt-bin-0.12.1: r"(?m:^ {0,3}\[[^\]]+\]:.+$)" `[^\p{L}\p{M}\p{N}\p{Pc} -]`, // comrak-0.2.12: r"[^\p{L}\p{M}\p{N}\p{Pc} -]" "", // content-blocker-0.2.3: "" "(?i)hi", // content-blocker-0.2.3: "(?i)hi" "http[s]?://domain.org", // content-blocker-0.2.3: "http[s]?://domain.org" "(?i)http[s]?://domain.org", // content-blocker-0.2.3: "(?i)http[s]?://domain.org" "http://domain.org", // content-blocker-0.2.3: "http://domain.org" "http://domain.org", // content-blocker-0.2.3: "http://domain.org" "ad.html", // content-blocker-0.2.3: "ad.html" "ad.html", // content-blocker-0.2.3: "ad.html" "http://domain.org", // content-blocker-0.2.3: "http://domain.org" "http://domain.org/nocookies.sjs", // content-blocker-0.2.3: "http://domain.org/nocookies.sjs" "http://domain.org/nocookies.sjs", // content-blocker-0.2.3: "http://domain.org/nocookies.sjs" "http://domain.org/hideme.jpg", // content-blocker-0.2.3: "http://domain.org/hideme.jpg" "http://domain.org/ok.html", // content-blocker-0.2.3: "http://domain.org/ok.html" "http://domain.org/ok.html\\?except_this=1", // content-blocker-0.2.3: "http://domain.org/ok.html\\?except_this=1" "[A-Za-z0-9=]", // victoria-dom-0.1.2: "[A-Za-z0-9=]" `^nsq://`, // numbat-1.0.0: r"^nsq://" `[\s\t\r\n]`, // airkorea-0.1.2: r"[\s\t\r\n]" `([\{\[,])|([\}\]])`, // airkorea-0.1.2: r"([\{\[,])|([\}\]])" `[^.\d]+$`, // airkorea-0.1.2: r"[^.\d]+$" // rofl-0.0.1: r"\b" `--------- beginning of.*`, // rogcat-0.2.15: r"--------- beginning of.*" `a|e|i|o|u`, // rogcat-0.2.15: r"a|e|i|o|u" `^(\d+)([kMG])$`, // rogcat-0.2.15: r"^(\d+)([kMG])$" "\\.([A-Za-z0-9]{2,4})$", // media_filename-0.1.4: "\\.([A-Za-z0-9]{2,4})$" "([0-9]{3,4}p|[0-9]{3,4}x[0-9]{3,4})", // media_filename-0.1.4: "([0-9]{3,4}p|[0-9]{3,4}x[0-9]{3,4})" "(?:^\\[([^]]+)\\]|- ?([^-]+)$)", // media_filename-0.1.4: "(?:^\\[([^]]+)\\]|- ?([^-]+)$)" "(?:[eE]([0-9]{2,3})|[^0-9A-Za-z]([0-9]{2,3})(?:v[0-9])?[^0-9A-Za-z])", // media_filename-0.1.4: "(?:[eE]([0-9]{2,3})|[^0-9A-Za-z]([0-9]{2,3})(?:v[0-9])?[^0-9A-Za-z])" "[sS]([0-9]{1,2})", // media_filename-0.1.4: "[sS]([0-9]{1,2})" "((?i)(?:PPV.)?[HP]DTV|(?:HD)?CAM|BRRIP|[^a-z]TS[^a-z]|(?:PPV )?WEB.?DL(?: DVDRip)?|HDRip|DVDRip|CamRip|W[EB]BRip|BluRay|BD|DVD|DvDScr|hdtv)", // media_filename-0.1.4: "((?i)(?:PPV.)?[HP]DTV|(?:HD)?CAM|BRRIP|[^a-z]TS[^a-z]|(?:PPV )?WEB.?DL(?: DVDRip)?|HDRip|DVDRip|CamRip|W[EB]BRip|BluRay|BD|DVD|DvDScr|hdtv)" "((19[0-9]|20[01])[0-9])", // media_filename-0.1.4: "((19[0-9]|20[01])[0-9])" "((?i)xvid|x264|h\\.?264)", // media_filename-0.1.4: "((?i)xvid|x264|h\\.?264)" "((?i)MP3|DD5\\.?1|Dual[- ]Audio|LiNE|DTS|AAC(?:\\.?2\\.0)?|AC3(?:\\.5\\.1)?)", // media_filename-0.1.4: "((?i)MP3|DD5\\.?1|Dual[- ]Audio|LiNE|DTS|AAC(?:\\.?2\\.0)?|AC3(?:\\.5\\.1)?)" "\\[([0-9A-F]{8})\\]", // media_filename-0.1.4: "\\[([0-9A-F]{8})\\]" `(\d+)[xX](\d+)`, // termimage-0.3.2: r"(\d+)[xX](\d+)" `.*(\d{4}-\d{2}-\d{2}).*`, // teensy-0.1.0: r".*(\d{4}-\d{2}-\d{2}).*" `<@(.+)>`, // telescreen-0.1.3: r"<@(.+)>" `^(\d+)`, // tempus_fugit-0.4.4: r"^(\d+)" "(\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)", // fselect-0.4.1: "(\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)" "(%|_|\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)", // fselect-0.4.1: "(%|_|\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)" `^([A-Z]+)(?:\s(.+))?\s*`, // fs_eventbridge-0.1.0: r"^([A-Z]+)(?:\s(.+))?\s*" `(\w{1,2})\[(.+?)\]`, // joseki-0.0.1: r"(\w{1,2})\[(.+?)\]" `(?i)in (\d+) (second|minute|hour|day|week)s?`, // tweetr-0.2.1: r"(?i)in (\d+) (second|minute|hour|day|week)s?" "^([0-9])+", // bullet_core-0.1.1: "^(?u:[0-9])+" "^([0-9])+(\\.)([0-9])+", // bullet_core-0.1.1: "^(?u:[0-9])+(?u:\\.)(?u:[0-9])+" "^([A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+", // bullet_core-0.1.1: "^(?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+" "^(d/d)(([A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+)", // bullet_core-0.1.1: "^(?u:d/d)((?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+)" "^(\\()", // bullet_core-0.1.1: "^(?u:\\()" "^(\\))", // bullet_core-0.1.1: "^(?u:\\))" "^(\\*)", // bullet_core-0.1.1: "^(?u:\\*)" "^(\\+)", // bullet_core-0.1.1: "^(?u:\\+)" "^(,)", // bullet_core-0.1.1: "^(?u:,)" "^(\\-)", // bullet_core-0.1.1: "^(?u:\\-)" "^(/)", // bullet_core-0.1.1: "^(?u:/)" "^(\\[)", // bullet_core-0.1.1: "^(?u:\\[)" "^(\\])", // bullet_core-0.1.1: "^(?u:\\])" "^(\\^)", // bullet_core-0.1.1: "^(?u:\\^)" "^(·)", // bullet_core-0.1.1: "^(?u:·)" "//+", // actix-web-0.6.13: "//+" "//+", // actix-web-0.6.13: "//+" `(\S*) .* (\S*) (REACHABLE|STALE|DELAY)`, // althea_kernel_interface-0.1.0: r"(\S*) .* (\S*) (REACHABLE|STALE|DELAY)" `-s (.*) --ip6-dst (.*)/.* bcnt = (.*)`, // althea_kernel_interface-0.1.0: r"-s (.*) --ip6-dst (.*)/.* bcnt = (.*)" `\buci(?:\s|$)`, // alcibiades-0.3.0: r"\buci(?:\s|$)" `\A[a-z0-9._=-]+\z`, // ruma-identifiers-0.11.0: r"\A[a-z0-9._=-]+\z" `/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})$`, // rusqbin-0.2.3: r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})$" `/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})/requests/?$`, // rusqbin-0.2.3: r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})/requests/?$" `^(nightly|beta|stable)(?:-(\d{4}-\d{2}-\d{2}))?$`, // rust-install-0.0.4: r"^(nightly|beta|stable)(?:-(\d{4}-\d{2}-\d{2}))?$" "^+(.*)\r\n", // rust_inbox-0.0.5: "^+(.*)\r\n" `^\* CAPABILITY (.*)\r\n`, // rust_inbox-0.0.5: r"^\* CAPABILITY (.*)\r\n" `^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)`, // rust_inbox-0.0.5: r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)" `^\* (\d+) EXISTS\r\n`, // rust_inbox-0.0.5: r"^\* (\d+) EXISTS\r\n" `^\* (\d+) RECENT\r\n`, // rust_inbox-0.0.5: r"^\* (\d+) RECENT\r\n" `^\* FLAGS (.+)\r\n`, // rust_inbox-0.0.5: r"^\* FLAGS (.+)\r\n" `^\* OK \[UNSEEN (\d+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[UNSEEN (\d+)\](.*)\r\n" `^\* OK \[UIDVALIDITY (\d+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[UIDVALIDITY (\d+)\](.*)\r\n" `^\* OK \[UIDNEXT (\d+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[UIDNEXT (\d+)\](.*)\r\n" `^\* OK \[PERMANENTFLAGS (.+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[PERMANENTFLAGS (.+)\](.*)\r\n" `^[a-z]+ (\d+)$`, // rustml-0.0.7: r"^[a-z]+ (\d+)$" `^[a-z]+ (\d+)$`, // rustml-0.0.7: r"^[a-z]+ (\d+)$" `^[a-z]+ (\d+)$`, // rustml-0.0.7: r"^[a-z]+ (\d+)$" `([^\\](\\\\)*)\\[\n\r][[:space:]]*`, // rustfmt-0.10.0: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*" `(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)`, // rustfmt-core-0.4.0: r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)" "^## `([^`]+)`", // rustfmt-core-0.4.0: r"^## `([^`]+)`" `([^\\](\\\\)*)\\[\n\r][[:space:]]*`, // rustfmt-core-0.4.0: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*" `\s;`, // rustfmt-core-0.4.0: r"\s;" `^(0x)?([:digit:]+)$`, // rust-enum-derive-0.4.0: r"^(0x)?([:digit:]+)$" `^([:digit:]+)[:space:]*<<[:space:]*([:digit:]+)$`, // rust-enum-derive-0.4.0: r"^([:digit:]+)[:space:]*<<[:space:]*([:digit:]+)$" `^[:space:]*([[:alnum:]_]+)([:space:]*=[:space:]*([:graph:]+))?[:space:]*,`, // rust-enum-derive-0.4.0: r"^[:space:]*([[:alnum:]_]+)([:space:]*=[:space:]*([:graph:]+))?[:space:]*," `^#define[:space:]+([:graph:]+)[:space:]+([:graph:]+)`, // rust-enum-derive-0.4.0: r"^#define[:space:]+([:graph:]+)[:space:]+([:graph:]+)" `^\s*pub mod (.+);$`, // rustsourcebundler-0.2.0: r"^\s*pub mod (.+);$" `^\s*pub mod (.+);$`, // rustsourcebundler-0.2.0: r"^\s*pub mod (.+);$" `([^\\](\\\\)*)\\[\n\r][[:space:]]*`, // rustfmt-nightly-0.8.2: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*" `\s;`, // rustfmt-nightly-0.8.2: r"\s;" `(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)`, // rustache-0.1.0: r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)" `_ZN[\$\._[:alnum:]]*`, // rustfilt-0.2.0: r"_ZN[\$\._[:alnum:]]*" `(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)`, // rustache-lists-0.1.2: r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)" "(.+)=(.+)", // rural-0.7.3: "(.+)=(.+)" "(.*):(.+)", // rural-0.7.3: "(.*):(.+)" "(.+):=(.+)", // rural-0.7.3: "(.+):=(.+)" "(.*)==(.+)", // rural-0.7.3: "(.*)==(.+)" `^\[([^\]]+)\]$`, // rusoto_credential-0.11.0: r"^\[([^\]]+)\]$" "([:blank:]*)$", // rumblebars-0.3.0: "([:blank:]*)$" "(\r?\n)[:blank:]*(\\{\\{~?[#!/](?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z", // rumblebars-0.3.0: "(\r?\n)[:blank:]*(\\{\\{~?[#!/](?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z" "(\r?\n[:blank:]*)(\\{\\{~?>(?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z", // rumblebars-0.3.0: "(\r?\n[:blank:]*)(\\{\\{~?>(?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z" "((?:[:blank:]|\r?\n)*)(\r?\n)[:blank:]*$", // rumblebars-0.3.0: "((?:[:blank:]|\r?\n)*)(\r?\n)[:blank:]*$" "^([:blank:]*\r?\n)(.*)", // rumblebars-0.3.0: "^([:blank:]*\r?\n)(.*)" `(?P<stamp>[\d-]*)_hello`, // diesel_cli-1.3.1: r"(?P<stamp>[\d-]*)_hello" `(\d+)s`, // dishub-0.1.1: r"(\d+)s" `\n`, // spreadsheet_textconv-0.1.0: r"\n" `\r`, // spreadsheet_textconv-0.1.0: r"\r" `\t`, // spreadsheet_textconv-0.1.0: r"\t" `DELAY (-?\d+)ms`, // split_aud-0.1.0: r"DELAY (-?\d+)ms" `Trim\((\d+), ?(\d+)\)`, // split_aud-0.1.0: r"Trim\((\d+), ?(\d+)\)" `spotify:[a-z]+:[a-zA-Z0-9]+`, // spotrust-0.0.5: r"spotify:[a-z]+:[a-zA-Z0-9]+" `[^\x00-\x7F]`, // spaceslugs-0.1.0: r"[^\x00-\x7F]" `[']+`, // spaceslugs-0.1.0: r"[']+" `\W+`, // spaceslugs-0.1.0: r"\W+" `[ ]+`, // spaceslugs-0.1.0: r"[ ]+" "PHPSESSID=([0-9a-f]+)", // space_email_api-0.1.1: "PHPSESSID=([0-9a-f]+)" "[^0-9.,]", // lorikeet-0.7.0: "[^0-9.,]" `^(?:\b|(-)?)(\p{L})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$`, // claude-0.3.0: r"^(?:\b|(-)?)(\p{Currency_Symbol})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$" `<%=\s*(.+?)\s*%>`, // clam-0.1.6: r"<%=\s*(.+?)\s*%>" `(\s)`, // classifier-0.0.3: r"(\s)" `(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)`, // click-0.3.2: r"(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)" `-----BEGIN PRIVATE KEY-----`, // click-0.3.2: r"-----BEGIN PRIVATE KEY-----" `#([A-Z3a-z]*):(.*)`, // ultrastar-txt-0.1.2: r"#([A-Z3a-z]*):(.*)" "^-\\s?(-?[0-9]+)\\s*$", // ultrastar-txt-0.1.2: "^-\\s?(-?[0-9]+)\\s*$" "^-\\s?(-?[0-9]+)\\s+(-?[0-9]+)", // ultrastar-txt-0.1.2: "^-\\s?(-?[0-9]+)\\s+(-?[0-9]+)" "^(.)\\s*(-?[0-9]+)\\s+(-?[0-9]+)\\s+(-?[0-9]+)\\s?(.*)", // ultrastar-txt-0.1.2: "^(.)\\s*(-?[0-9]+)\\s+(-?[0-9]+)\\s+(-?[0-9]+)\\s?(.*)" "^P\\s?(-?[0-9]+)", // ultrastar-txt-0.1.2: "^P\\s?(-?[0-9]+)" `^template\.add($|\..+$)`, // db-accelerate-2.0.0: r"^template\.add($|\..+$)" `^template\.sub($|\..+$)`, // db-accelerate-2.0.0: r"^template\.sub($|\..+$)" `(\d+)([cegps])`, // sterling-0.3.0: r"(\d+)([cegps])" `[^\w]`, // stache-0.2.0: r"[^\w]" "\"([<>]?)([xcbB\\?hHiIlLqQfdspP]*)\"", // strukt-0.1.0: "\"([<>]?)([xcbB\\?hHiIlLqQfdspP]*)\"" `^STEAM_([0-4]):([0-1]):([0-9]{1,10})$`, // steamid-ng-0.3.1: r"^STEAM_([0-4]):([0-1]):([0-9]{1,10})$" `^\[([AGMPCgcLTIUai]):([0-4]):([0-9]{1,10})(:([0-9]+))?\]$`, // steamid-ng-0.3.1: r"^\[([AGMPCgcLTIUai]):([0-4]):([0-9]{1,10})(:([0-9]+))?\]$" `^\w+`, // strscan-0.1.1: r"^\w+" `^\s+`, // strscan-0.1.1: r"^\s+" `^\w+`, // strscan-0.1.1: r"^\w+" `^\s+`, // strscan-0.1.1: r"^\s+" `^(\w+)\s+`, // strscan-0.1.1: r"^(\w+)\s+" `^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$`, // tk-carbon-0.2.0: r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$" `^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$`, // tk-carbon-0.2.0: r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$" `extern\s+crate\s+([a-z0-9_]+)\s*;(\s*//(.+))?`, // evalrs-0.0.10: r"extern\s+crate\s+([a-z0-9_]+)\s*;(\s*//(.+))?" `(?m)^# `, // evalrs-0.0.10: r"(?m)^# " `(?m)^\s*fn +main *\( *\)`, // evalrs-0.0.10: r"(?m)^\s*fn +main *\( *\)" `(extern\s+crate\s+[a-z0-9_]+\s*;)`, // evalrs-0.0.10: r"(extern\s+crate\s+[a-z0-9_]+\s*;)" "(.*)_t([0-9]+)", // gate_build-0.5.0: "(.*)_t([0-9]+)" `[^\P{P}-]|\s+-\s+`, // rake-0.1.1: r"[^\P{P}-]|\s+-\s+" `^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*`, // rafy-0.2.1: r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*" `^(?P<protocol>.*?)://(?P<public_key>.*?):(?P<secret_key>.*?)@(?P<host>.*?)/(?P<path>.*/)?(?P<project_id>.*)$`, // raven-0.2.1: r"^(?P<protocol>.*?)://(?P<public_key>.*?):(?P<secret_key>.*?)@(?P<host>.*?)/(?P<path>.*/)?(?P<project_id>.*)$" `\{[[:space:]]*[^{}]*[[:space:]]*\}`, // rargs-0.2.0: r"\{[[:space:]]*[^{}]*[[:space:]]*\}" `^\{[[:space:]]*(?P<name>[[:word:]]*)[[:space:]]*\}$`, // rargs-0.2.0: r"^\{[[:space:]]*(?P<name>[[:word:]]*)[[:space:]]*\}$" `^\{[[:space:]]*(?P<num>-?\d+)[[:space:]]*\}$`, // rargs-0.2.0: r"^\{[[:space:]]*(?P<num>-?\d+)[[:space:]]*\}$" `^\{(?P<left>-?\d*)?\.\.(?P<right>-?\d*)?(?::(?P<sep>.*))?\}$`, // rargs-0.2.0: r"^\{(?P<left>-?\d*)?\.\.(?P<right>-?\d*)?(?::(?P<sep>.*))?\}$" `(.*?)[[:space:]]+|(.*?)$`, // rargs-0.2.0: r"(.*?)[[:space:]]+|(.*?)$" `[a-zA-Z0-9]{8}`, // indradb-lib-0.15.0: r"[a-zA-Z0-9]{8}" `::`, // fungi-lang-0.1.50: r"::" "/hello/(?P<name>[a-zA-Z]+)", // nickel-0.10.1: "/hello/(?P<name>[a-zA-Z]+)" "/hello/(?P<name>[a-zA-Z]+)", // nickel-0.10.1: "/hello/(?P<name>[a-zA-Z]+)" `\{(\w+)\}`, // pact_verifier-0.4.0: r"\{(\w+)\}" "application/.*json", // pact_matching-0.4.1: "application/.*json" "application/json.*", // pact_matching-0.4.1: "application/json.*" "application/.*xml", // pact_matching-0.4.1: "application/.*xml" "([\"'\\(\\[\\{{<\u201c])(\\s*)(.+?)(\\s*)([\"'\\)\\]\\}}>\u201d])", // pangu-0.2.0: "([\"'\\(\\[\\{{<\u{201c}])(\\s*)(.+?)(\\s*)([\"'\\)\\]\\}}>\u{201d}])" "([\\(\\[\\{{<\u201c]+)(\\s*)(.+?)(\\s*)([\\)\\]\\}}>\u201d]+)", // pangu-0.2.0: "([\\(\\[\\{{<\u{201c}]+)(\\s*)(.+?)(\\s*)([\\)\\]\\}}>\u{201d}]+)" `\{-[\s\S]*?-\}`, // parser-haskell-0.2.0: r"\{-[\s\S]*?-\}" `(?m);+\s*$`, // parser-haskell-0.2.0: r"(?m);+\s*$" `(?m)^#(if|ifn?def|endif|else|include|elif).*`, // parser-haskell-0.2.0: r"(?m)^#(if|ifn?def|endif|else|include|elif).*" `'([^'\\]|\\[A-Z]{1,3}|\\.)'`, // parser-haskell-0.2.0: r"'([^'\\]|\\[A-Z]{1,3}|\\.)'" `forall\s+(.*?)\.`, // parser-haskell-0.2.0: r"forall\s+(.*?)\." "\\s{2,}", // html2md-0.2.1: "\\s{2,}" "\\n{2,}", // html2md-0.2.1: "\\n{2,}" "(?m)(\\S) $", // html2md-0.2.1: "(?m)(\\S) $" "(?m)^[-*] ", // html2md-0.2.1: "(?m)^[-*] " `#.*$`, // ovpnfile-0.1.2: r"#.*$" `^<(\S+)>`, // ovpnfile-0.1.2: r"^<(\S+)>" `^</(\S+)>`, // ovpnfile-0.1.2: r"^</(\S+)>" `#([:xdigit:]{2})([:xdigit:]{2})([:xdigit:]{2})`, // screenruster-saver-fractal-0.1.1: r"#([:xdigit:]{2})([:xdigit:]{2})([:xdigit:]{2})" `rgb\((?: *(\d{1,3}),)(?: *(\d{1,3}),)(?: *(\d{1,3}))\)`, // scarlet-0.2.2: r"rgb\((?: *(\d{1,3}),)(?: *(\d{1,3}),)(?: *(\d{1,3}))\)" `^([\w:]+)<(.+)>$`, // cpp_to_rust_generator-0.2.0: r"^([\w:]+)<(.+)>$" `^type-parameter-(\d+)-(\d+)$`, // cpp_to_rust_generator-0.2.0: r"^type-parameter-(\d+)-(\d+)$" `^([\w~]+)<[^<>]+>$`, // cpp_to_rust_generator-0.2.0: r"^([\w~]+)<[^<>]+>$" `(signals|Q_SIGNALS)\s*:`, // cpp_to_rust_generator-0.2.0: r"(signals|Q_SIGNALS)\s*:" `(slots|Q_SLOTS)\s*:`, // cpp_to_rust_generator-0.2.0: r"(slots|Q_SLOTS)\s*:" `(public|protected|private)\s*:`, // cpp_to_rust_generator-0.2.0: r"(public|protected|private)\s*:" `^([\w:]+)<(.+)>$`, // cpp_to_rust-0.5.3: r"^([\w:]+)<(.+)>$" `^type-parameter-(\d+)-(\d+)$`, // cpp_to_rust-0.5.3: r"^type-parameter-(\d+)-(\d+)$" `^([\w~]+)<[^<>]+>$`, // cpp_to_rust-0.5.3: r"^([\w~]+)<[^<>]+>$" `(signals|Q_SIGNALS)\s*:`, // cpp_to_rust-0.5.3: r"(signals|Q_SIGNALS)\s*:" `(slots|Q_SLOTS)\s*:`, // cpp_to_rust-0.5.3: r"(slots|Q_SLOTS)\s*:" `(public|protected|private)\s*:`, // cpp_to_rust-0.5.3: r"(public|protected|private)\s*:" "(\\d{2}\\.\\d{2}\\.\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (.*)", // fritzbox_logs-0.2.0: "(\\d{2}\\.\\d{2}\\.\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (.*)" `mxc://(?P<server>[^/]+)/(?P<media>.+)`, // fractal-matrix-api-3.29.0: r"mxc://(?P<server>[^/]+)/(?P<media>.+)" `^api-[a-zA-Z0-9]{32}$`, // smtp2go-0.1.4: r"^api-[a-zA-Z0-9]{32}$" `^[-a-zA-Z0-9_=@,.;]+$`, // pusher-0.3.1: r"^[-a-zA-Z0-9_=@,.;]+$" `\A\d+\.\d+\z`, // pusher-0.3.1: r"\A\d+\.\d+\z" `^\.(.+?) +?(.+)$`, // bakervm-0.9.0: r"^\.(.+?) +?(.+)$" `^\.([^\s]+)$`, // bakervm-0.9.0: r"^\.([^\s]+)$" `^include! +([^\s]+)$`, // bakervm-0.9.0: r"^include! +([^\s]+)$" `^@(\d+)$`, // bakervm-0.9.0: r"^@(\d+)$" `^true|false$`, // bakervm-0.9.0: r"^true|false$" `^(-?\d+)?\.[0-9]+$`, // bakervm-0.9.0: r"^(-?\d+)?\.[0-9]+$" `^(-?\d+)?$`, // bakervm-0.9.0: r"^(-?\d+)?$" `^#([0-9abcdefABCDEF]{6})$`, // bakervm-0.9.0: r"^#([0-9abcdefABCDEF]{6})$" `^'(.)'$`, // bakervm-0.9.0: r"^'(.)'$" `^\$vi\((\d+)\)$`, // bakervm-0.9.0: r"^\$vi\((\d+)\)$" `^\$key\((\d+)\)$`, // bakervm-0.9.0: r"^\$key\((\d+)\)$" "(?P<type>[A-Z^']+) (?P<route>[^']+) HTTP/(?P<http>[^']+)", // banana-0.0.2: "(?P<type>[A-Z^']+) (?P<route>[^']+) HTTP/(?P<http>[^']+)" `[A-F0-9]{8}`, // serial-key-2.0.0: r"[A-F0-9]{8}" // serde-hjson-0.8.1: "[\\\\\"\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]" // serde-hjson-0.8.1: "[\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]" // serde-hjson-0.8.1: "'''|[\x00-\x09\x0b\x0c\x0e-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]" `/todos/(?P<id>\d+)`, // serde-odbc-0.1.0: r"/todos/(?P<id>\d+)" `^(?:_<)?([a-zA-Z0-9_]+?)(?:\.\.|::)`, // sentry-0.6.0: r"^(?:_<)?([a-zA-Z0-9_]+?)(?:\.\.|::)" `[^a-zA-Z0 -]+`, // sentiment-0.1.1: r"[^a-zA-Z0 -]+" ` {2,}`, // sentiment-0.1.1: r" {2,}" `(?m)//.*`, // verilog-0.0.1: r"(?m)//.*" "(?P<robot>C3PO)", // verex-0.2.2: "(?P<robot>C3PO)" ">|<|\"|&", // handlebars-0.32.4: ">|<|\"|&" `^\w+-\w+-[0123456789]{4}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789]{4}$" `^\w+@\w+@[0123456789]{4}$`, // haikunator-0.1.2: r"^\w+@\w+@[0123456789]{4}$" `^\w+-\w+-[0123456789abcdef]{4}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789abcdef]{4}$" `^\w+-\w+-[0123456789忠犬ハチ公]{10}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789忠犬ハチ公]{10}$" `^\w+-\w+$`, // haikunator-0.1.2: r"^\w+-\w+$" `^\w+-\w+-[foo]{4}$`, // haikunator-0.1.2: r"^\w+-\w+-[foo]{4}$" `^\w+-\w+-[0123456789忠犬ハチ公]{5}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789忠犬ハチ公]{5}$" `(.*)`, // bobbin-cli-0.8.3: r"(.*)" `rustc (.*)`, // bobbin-cli-0.8.3: r"rustc (.*)" `cargo (.*)`, // bobbin-cli-0.8.3: r"cargo (.*)" `xargo (.*)\n`, // bobbin-cli-0.8.3: r"xargo (.*)\n" `Open On-Chip Debugger (.*)`, // bobbin-cli-0.8.3: r"Open On-Chip Debugger (.*)" `arm-none-eabi-gcc \(GNU Tools for ARM Embedded Processors[^\)]*\) (.*)`, // bobbin-cli-0.8.3: r"arm-none-eabi-gcc \(GNU Tools for ARM Embedded Processors[^\)]*\) (.*)" `(?m).*\nBasic Open Source SAM-BA Application \(BOSSA\) Version (.*)\n`, // bobbin-cli-0.8.3: r"(?m).*\nBasic Open Source SAM-BA Application \(BOSSA\) Version (.*)\n" `(?m)SEGGER J-Link Commander (.*)\n`, // bobbin-cli-0.8.3: r"(?m)SEGGER J-Link Commander (.*)\n" `(?m)Teensy Loader, Command Line, Version (.*)\n`, // bobbin-cli-0.8.3: r"(?m)Teensy Loader, Command Line, Version (.*)\n" `dfu-util (.*)\n`, // bobbin-cli-0.8.3: r"dfu-util (.*)\n" `^/static/[\w.]+$`, // borsholder-0.9.1: r"^/static/[\w.]+$" `^/timeline/([0-9]+)$`, // borsholder-0.9.1: r"^/timeline/([0-9]+)$" "\u001B\\[[\\d;]*[^\\d;]", // fblog-1.0.1: "\u{001B}\\[[\\d;]*[^\\d;]" "\u001B\\[[\\d;]*[^\\d;]", // fblog-1.0.1: "\u{001B}\\[[\\d;]*[^\\d;]" `^\[\d+\]$`, // toml-query-0.6.0: r"^\[\d+\]$" ` (?P<key>[^\s]+):(?P<value>[^\s^/]+)`, // todo-txt-1.1.0: r" (?P<key>[^\s]+):(?P<value>[^\s^/]+)" `\band\b`, // findr-0.1.5: r"\band\b" `\bor\b`, // findr-0.1.5: r"\bor\b" `\bnot\b`, // findr-0.1.5: r"\bnot\b" `.*?\.(a|la|lo|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // file-sniffer-3.0.1: r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" `.*?\.(stats|conf|h|cache.*|dat|pc|info)$`, // file-sniffer-3.0.1: r".*?\.(stats|conf|h|cache.*|dat|pc|info)$" `.*?\.(exe|a|la|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // file-sniffer-3.0.1: r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" `.*?\.(stats|conf|h|cache.*)$`, // file-sniffer-3.0.1: r".*?\.(stats|conf|h|cache.*)$" `(\.git|\.pijul|_darcs|\.hg)$`, // file-sniffer-3.0.1: r"(\.git|\.pijul|_darcs|\.hg)$" "test", // file_logger-0.1.0: "test" `foo`, // file_scanner-0.2.0: r"foo" `a+b`, // file_scanner-0.2.0: r"a+b" `a[ab]*b`, // file_scanner-0.2.0: r"a[ab]*b" `\s+`, // file_scanner-0.2.0: r"\s+" `\s+`, // file_scanner-0.2.0: r"\s+" `^\s*([^\s]+) %cellsplit<\d+>$`, // cellsplit-0.2.1: r"^\s*([^\s]+) %cellsplit<\d+>$" `^\s*([^\s]+) %cellsplit<\d+>$`, // cellsplit-0.2.1: r"^\s*([^\s]+) %cellsplit<\d+>$" `^[+\-]?[0-9]+`, // aterm-0.20.0: r"^[+\-]?[0-9]+" `^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?`, // aterm-0.20.0: r"^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?" `^[*] OK`, // atarashii_imap-0.3.0: r"^[*] OK" `FLAGS\s\((.+)\)`, // atarashii_imap-0.3.0: r"FLAGS\s\((.+)\)" `\[PERMANENTFLAGS\s\((.+)\)\]`, // atarashii_imap-0.3.0: r"\[PERMANENTFLAGS\s\((.+)\)\]" `\[UIDVALIDITY\s(\d+)\]`, // atarashii_imap-0.3.0: r"\[UIDVALIDITY\s(\d+)\]" `(\d+)\sEXISTS`, // atarashii_imap-0.3.0: r"(\d+)\sEXISTS" `(\d+)\sRECENT`, // atarashii_imap-0.3.0: r"(\d+)\sRECENT" `\[UNSEEN\s(\d+)\]`, // atarashii_imap-0.3.0: r"\[UNSEEN\s(\d+)\]" `\[UIDNEXT\s(\d+)\]`, // atarashii_imap-0.3.0: r"\[UIDNEXT\s(\d+)\]" `\\(\{|\})`, // editorconfig-1.0.0: r"\\(\{|\})" `(^|[^\\])\\\|`, // editorconfig-1.0.0: r"(^|[^\\])\\\|" `\[([^\]]*)$`, // editorconfig-1.0.0: r"\[([^\]]*)$" `\[(.*/.*)\]`, // editorconfig-1.0.0: r"\[(.*/.*)\]" `\{(-?\d+\\\.\\\.-?\d+)\}`, // editorconfig-1.0.0: r"\{(-?\d+\\\.\\\.-?\d+)\}" `\{([^,]+)\}`, // editorconfig-1.0.0: r"\{([^,]+)\}" `\{(([^\}].*)?(,|\|)(.*[^\\])?)\}`, // editorconfig-1.0.0: r"\{(([^\}].*)?(,|\|)(.*[^\\])?)\}" `^/`, // editorconfig-1.0.0: r"^/" `(^|[^\\])(\{|\})`, // editorconfig-1.0.0: r"(^|[^\\])(\{|\})" "^#!.*\n", // edmunge-1.0.0: "^#!.*\n" `\\N\{(.*?)(?:\}|$)`, // unicode_names2_macros-0.2.0: r"\\N\{(.*?)(?:\}|$)" `^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?`, // unidiff-0.2.1: r"^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?" `^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?`, // unidiff-0.2.1: r"^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?" `^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)`, // unidiff-0.2.1: r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)" `^(?P<line_type>[- \n\+\\]?)(?P<value>.*)`, // unidiff-0.2.1: r"^(?P<line_type>[- \n\+\\]?)(?P<value>.*)" "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$", // slippy-map-tiles-0.13.1: "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$" `^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$`, // slippy-map-tiles-0.13.1: r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$" `^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$`, // slippy-map-tiles-0.13.1: r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$" `^https?://(.+?):1400/xml`, // sonos-0.1.2: r"^https?://(.+?):1400/xml" `^[a-z]{2}$`, // validator_derive-0.7.0: r"^[a-z]{2}$" `[a-z]{2}`, // validator_derive-0.7.0: r"[a-z]{2}" `[a-z]{2}`, // validator_derive-0.7.0: r"[a-z]{2}" `one of \d+ options`, // nginx-config-0.8.0: r"one of \d+ options" `[\s,]`, // waltz-0.4.0: r"[\s,]" `^aws_access_key_id = (.*)`, // warheadhateus-0.2.1: r"^aws_access_key_id = (.*)" `^aws_secret_access_key = (.*)`, // warheadhateus-0.2.1: r"^aws_secret_access_key = (.*)" `^aws_access_key_id = (.*)`, // warheadhateus-0.2.1: r"^aws_access_key_id = (.*)" `^aws_secret_access_key = (.*)`, // warheadhateus-0.2.1: r"^aws_secret_access_key = (.*)" "([\u4E00-\u9FD5a-zA-Z0-9+#&\\._%]+)", // jieba-rs-0.2.2: r"([\u{4E00}-\u{9FD5}a-zA-Z0-9+#&\._%]+)" `(\r\n|\s)`, // jieba-rs-0.2.2: r"(\r\n|\s)" "([\u4E00-\u9FD5]+)", // jieba-rs-0.2.2: "([\u{4E00}-\u{9FD5}]+)" `[^a-zA-Z0-9+#\n]`, // jieba-rs-0.2.2: r"[^a-zA-Z0-9+#\n]" "([\u4E00-\u9FD5]+)", // jieba-rs-0.2.2: r"([\u{4E00}-\u{9FD5}]+)" `([a-zA-Z0-9]+(?:.\d+)?%?)`, // jieba-rs-0.2.2: r"([a-zA-Z0-9]+(?:.\d+)?%?)" `Span\([0-9 ,]*\)`, // lalrpop-0.15.2: r"Span\([0-9 ,]*\)" `Span\([0-9 ,]*\)`, // lalrpop-snap-0.15.2: r"Span\([0-9 ,]*\)" `[\S]+`, // nlp-tokenize-0.1.0: r"[\S]+" "[[:xdigit:]][70]", // kbgpg-0.1.2: "[[:xdigit:]][70]" `^((?P<address>.*):)?(?P<port>\d+)$`, // cdbd-0.1.1: r"^((?P<address>.*):)?(?P<port>\d+)$" `[\w\s=+-/]+\((\{(.|\n)*\})\);?`, // mbutiles-0.1.1: r"[\w\s=+-/]+\((\{(.|\n)*\})\);?" `^-\d+(?:ms|s|m|h|d|w|y)?$`, // extrahop-0.2.5: r"^-\d+(?:ms|s|m|h|d|w|y)?$" "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$" "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$" "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$" "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$" "^.*pn(0|[1-9][0-9]*)(-ss(0|[1-9][0-9]*)(\\.pip|-cl(0|[1-9][0-9]*)\\.piplog))?$", // pippin-0.1.0: "^.*pn(0|[1-9][0-9]*)(-ss(0|[1-9][0-9]*)(\\.pip|-cl(0|[1-9][0-9]*)\\.piplog))?$" "^(.*)-ss(?:0|[1-9][0-9]*)(?:\\.pip|-cl(?:0|[1-9][0-9]*)\\.piplog)$", // pippin-0.1.0: "^(.*)-ss(?:0|[1-9][0-9]*)(?:\\.pip|-cl(?:0|[1-9][0-9]*)\\.piplog)$" `(?i)[āáǎàēéěèōóǒòīíǐìūúǔùüǘǚǜńň]`, // pinyin-0.3.0: r"(?i)[āáǎàēéěèōóǒòīíǐìūúǔùüǘǚǜńň]" `([aeoiuvnm])([0-4])$`, // pinyin-0.3.0: r"([aeoiuvnm])([0-4])$" `(?P<value>\d+)(?P<units>[a-z])`, // duration-parser-0.2.0: r"(?P<value>\d+)(?P<units>[a-z])" `^\d+\D?$`, // dutree-0.2.7: r"^\d+\D?$" `^[A-Za-z0-9]*$`, // djangohashers-0.3.0: r"^[A-Za-z0-9]*$" `^[A-Z][A-Z0-9]{2,}$`, // rtag-0.3.5: r"^[A-Z][A-Z0-9]{2,}$" `^http://www\.emusic\.com`, // rtag-0.3.5: r"^http://www\.emusic\.com" `^[A-Z][A-Z0-9]{2,}`, // rtag-0.3.5: r"^[A-Z][A-Z0-9]{2,}" `(^[\x{0}|\x{feff}|\x{fffe}]*|[\x{0}|\x{feff}|\x{fffe}]*$)`, // rtag-0.3.5: r"(^[\x{0}|\x{feff}|\x{fffe}]*|[\x{0}|\x{feff}|\x{fffe}]*$)" `(\d+)[xX](\d+)`, // rtow-0.1.0: r"(\d+)[xX](\d+)" `\$([a-zA-Z0-9_]+)`, // pleingres-sql-plugin-0.1.0: r"\$([a-zA-Z0-9_]+)" "[\\n]+", // dono-2.0.0: "[\\n]+" "(?m)^\\n", // dono-2.0.0: "(?m)^\\n" "(?m)^\\n", // dono-2.0.0: "(?m)^\\n" `^[0-9A-Za-z\+/]{43}=\.ed25519$`, // ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{43}=\.ed25519$" `^[0-9A-Za-z\+/]{86}==\.ed25519$`, // ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{86}==\.ed25519$" `^[0-9A-Za-z\+/]{43}=\.sha256$`, // ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{43}=\.sha256$" `^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$`, // mozversion-0.1.3: r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$" `^(\d+)\.(\d+)$`, // monger-0.5.6: r"^(\d+)\.(\d+)$" `^[rv]2\.6`, // mongo_rub-0.0.2: r"^[rv]2\.6" "body value", // flow-0.3.5: "body value" "start marker", // flow-0.3.5: "start marker" "end marker", // flow-0.3.5: "end marker" "body value", // flow-0.3.5: "body value" "^([A-Za-z/ ]+): (.*)", // vobsub-0.2.3: "^([A-Za-z/ ]+): (.*)" `#([^\s=]+)*`, // voidmap-1.1.2: r"#([^\s=]+)*" `#(\S+)*`, // voidmap-1.1.2: r"#(\S+)*" `#prio=(\d+)`, // voidmap-1.1.2: r"#prio=(\d+)" `\[(\S+)\]`, // voidmap-1.1.2: r"\[(\S+)\]" `#limit=(\d+)`, // voidmap-1.1.2: r"#limit=(\d+)" `#tagged=(\S+)`, // voidmap-1.1.2: r"#tagged=(\S+)" `#rev\b`, // voidmap-1.1.2: r"#rev\b" `#done\b`, // voidmap-1.1.2: r"#done\b" `#open\b`, // voidmap-1.1.2: r"#open\b" `#since=(\S+)`, // voidmap-1.1.2: r"#since=(\S+)" `#until=(\S+)`, // voidmap-1.1.2: r"#until=(\S+)" `#plot=(\S+)`, // voidmap-1.1.2: r"#plot=(\S+)" `#n=(\d+)`, // voidmap-1.1.2: r"#n=(\d+)" `(\S+)`, // voidmap-1.1.2: r"(\S+)" `(?P<y>\d+)y`, // voidmap-1.1.2: r"(?P<y>\d+)y" `(?P<m>\d+)m`, // voidmap-1.1.2: r"(?P<m>\d+)m" `(?P<w>\d+)w`, // voidmap-1.1.2: r"(?P<w>\d+)w" `(?P<d>\d+)d`, // voidmap-1.1.2: r"(?P<d>\d+)d" `(?P<h>\d+)h`, // voidmap-1.1.2: r"(?P<h>\d+)h" `C-(.)`, // voidmap-1.1.2: r"C-(.)" `^\.\./qt[^/]+/`, // qt_generator-0.2.0: r"^\.\./qt[^/]+/" "(href|src)=\"([^\"]*)\"", // qt_generator-0.2.0: "(href|src)=\"([^\"]*)\"" `[01]{5}`, // kryptos-0.6.1: r"[01]{5}" "data_batch_[1-5].bin", // cifar_10_loader-0.2.0: "data_batch_[1-5].bin" "test_batch.bin", // cifar_10_loader-0.2.0: "test_batch.bin" `^\d+.\d+s$`, // circadian-0.6.0: r"^\d+.\d+s$" `^\d+:\d+$`, // circadian-0.6.0: r"^\d+:\d+$" `^\d+:\d+m$`, // circadian-0.6.0: r"^\d+:\d+m$" `!!`, // cicada-0.8.1: r"!!" "^([^`]*)`([^`]+)`(.*)$", // cicada-0.8.1: r"^([^`]*)`([^`]+)`(.*)$" `\*+`, // cicada-0.8.1: r"\*+" `([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)`, // cicada-0.8.1: r"([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)" `^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$`, // cicada-0.8.1: r"^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$" `hi`, // vterm-sys-0.1.0: r"hi" `.*?\t`, // skim-0.5.0: r".*?\t" `.*?[\t ]`, // skim-0.5.0: r".*?[\t ]" `(\{-?[0-9.,q]*?})`, // skim-0.5.0: r"(\{-?[0-9.,q]*?})" `[ \t\n]+`, // skim-0.5.0: r"[ \t\n]+" `[ \t\n]+`, // skim-0.5.0: r"[ \t\n]+" `([^ |]+( +\| +[^ |]*)+)|( +)`, // skim-0.5.0: r"([^ |]+( +\| +[^ |]*)+)|( +)" ` +\| +`, // skim-0.5.0: r" +\| +" `^(?P<left>-?\d+)?(?P<sep>\.\.)?(?P<right>-?\d+)?$`, // skim-0.5.0: r"^(?P<left>-?\d+)?(?P<sep>\.\.)?(?P<right>-?\d+)?$" ",", // skim-0.5.0: "," ".*?,", // skim-0.5.0: ".*?," ".*?,", // skim-0.5.0: ".*?," ",", // skim-0.5.0: "," `\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))`, // skim-0.5.0: r"\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))" `[-_./]\z`, // egg-mode-text-1.14.7: r"[-_./]\z" "^[ \t\r\n\x0c]*[#!]", // java-properties-1.1.1: "^[ \t\r\n\x0c]*[#!]" `^[ \t\x0c]*[#!][^\r\n]*$`, // java-properties-1.1.1: r"^[ \t\x0c]*[#!][^\r\n]*$" `^([ \t\x0c]*[:=][ \t\x0c]*|[ \t\x0c]+)$`, // java-properties-1.1.1: r"^([ \t\x0c]*[:=][ \t\x0c]*|[ \t\x0c]+)$" `:.+\.`, // ipaddress-0.1.2: r":.+\." `\.`, // ipaddress-0.1.2: r"\." `:`, // ipaddress-0.1.2: r":" `v(\d+)\.(\d+)\.(\d+)`, // iptables-0.2.2: r"v(\d+)\.(\d+)\.(\d+)" `^([^-]+)-(.*)\.dat\.gz$`, // rsure-0.8.1: r"^([^-]+)-(.*)\.dat\.gz$" "^(.*?)(<=|<|==|>=|>)(.*?)$", // rs-jsonpath-0.1.0: "^(.*?)(<=|<|==|>=|>)(.*?)$" `(\n|^)(\w+):([\n\w\W]+?)(\n(?:\w)|(\n\]))`, // oatie-0.3.0: r"(\n|^)(\w+):([\n\w\W]+?)(\n(?:\w)|(\n\]))" "#.*$", // weld-0.2.0: "#.*$" `^[A-Za-z$_][A-Za-z0-9$_]*$`, // weld-0.2.0: r"^[A-Za-z$_][A-Za-z0-9$_]*$" `^[0-9]+[cC]$`, // weld-0.2.0: r"^[0-9]+[cC]$" `^0b[0-1]+[cC]$`, // weld-0.2.0: r"^0b[0-1]+[cC]$" `^0x[0-9a-fA-F]+[cC]$`, // weld-0.2.0: r"^0x[0-9a-fA-F]+[cC]$" `^[0-9]+$`, // weld-0.2.0: r"^[0-9]+$" `^0b[0-1]+$`, // weld-0.2.0: r"^0b[0-1]+$" `^0x[0-9a-fA-F]+$`, // weld-0.2.0: r"^0x[0-9a-fA-F]+$" `^[0-9]+[lL]$`, // weld-0.2.0: r"^[0-9]+[lL]$" `^0b[0-1]+[lL]$`, // weld-0.2.0: r"^0b[0-1]+[lL]$" `^0x[0-9a-fA-F]+[lL]$`, // weld-0.2.0: r"^0x[0-9a-fA-F]+[lL]$" "([(, ])enum\\b", // webgl_generator-0.1.0: "([(, ])enum\\b" "\\bAcquireResourcesCallback\\b", // webgl_generator-0.1.0: "\\bAcquireResourcesCallback\\b" `^(\d+)(,(\d+))?([acd]).*$`, // weave-0.2.0: r"^(\d+)(,(\d+))?([acd]).*$" `<BinaryState>(\d)(\|-?\d+)*</BinaryState>`, // wemo-0.0.12: r"<BinaryState>(\d)(\|-?\d+)*</BinaryState>" `(http[s]?://[^\s]+)`, // webscale-0.9.4: r"(http[s]?://[^\s]+)" `^\d+.*$`, // svgrep-1.1.0: r"^\d+.*$" `^[\pL\pN]+$`, // ignore-0.4.2: r"^[\pL\pN]+$" `^([A-Za-z][0-9A-Za-z_]*)?$`, // ommui_string_patterns-0.1.2: r"^([A-Za-z][0-9A-Za-z_]*)?$" `^(\S+(?:.*\S)?)?$`, // ommui_string_patterns-0.1.2: r"^(\S+(?:.*\S)?)?$" "^(?P<min>[0-9]{1,10})(:(?P<max>[0-9]{1,10}))?$", // opcua-types-0.3.0: "^(?P<min>[0-9]{1,10})(:(?P<max>[0-9]{1,10}))?$" `^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb])=(?P<v>.+)$`, // opcua-types-0.3.0: r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb])=(?P<v>.+)$" `^(.+?)\s*:\s*(.+)$`, // open_read_later-1.1.1: r"^(.+?)\s*:\s*(.+)$" `^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*`, // youtube-downloader-0.1.0: r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*" ".", // yobot-0.1.1: "." `.`, // yobot-0.1.1: r"." `.+`, // yobot-0.1.1: r".+" `.`, // yobot-0.1.1: r"." `foo`, // ubiquity-0.1.5: r"foo" `/target/`, // ubiquity-0.1.5: r"/target/" `.DS_Store`, // ubiquity-0.1.5: r".DS_Store" `//.*`, // qasm-1.0.0: r"//.*" `\{\{ *([a-z\._]+) *\}\}`, // drill-0.3.5: r"\{\{ *([a-z\._]+) *\}\}" `^([^\]\[]+)`, // queryst-2.0.0: r"^([^\]\[]+)" `(\[[^\]\[]*\])`, // queryst-2.0.0: r"(\[[^\]\[]*\])" `^/(\w+)$`, // qui-vive-0.1.0: r"^/(\w+)$" `^/key$`, // qui-vive-0.1.0: r"^/key$" `^/key/(\w+)$`, // qui-vive-0.1.0: r"^/key/(\w+)$" `^/url$`, // qui-vive-0.1.0: r"^/url$" `^/url/(\w+)$`, // qui-vive-0.1.0: r"^/url/(\w+)$" `^/inv$`, // qui-vive-0.1.0: r"^/inv$" `^/inv/(\w+)$`, // qui-vive-0.1.0: r"^/inv/(\w+)$" // subdiff-0.1.0: r"\b" `^(\d+)/(\d+)$`, // substudy-0.4.5: r"^(\d+)/(\d+)$" `\s+`, // substudy-0.4.5: r"\s+" `<[a-z/][^>]*>`, // substudy-0.4.5: r"<[a-z/][^>]*>" `(\([^)]*\)|♪[^♪]*♪|[A-Z]{2,} ?:)`, // substudy-0.4.5: r"(\([^)]*\)|♪[^♪]*♪|[A-Z]{2,} ?:)" `\s+`, // substudy-0.4.5: r"\s+" `^(\d(-| )?){9}(x|X|\d|(\d(-| )?){3}\d)$`, // isbnid-0.1.3: r"^(\d(-| )?){9}(x|X|\d|(\d(-| )?){3}\d)$" `[^0-9X]`, // isbnid-0.1.3: r"[^0-9X]" `Intel\(r\) SPMD Program Compiler \(ispc\), (\d+\.\d+\.\d+)`, // ispc-0.3.5: r"Intel\(r\) SPMD Program Compiler \(ispc\), (\d+\.\d+\.\d+)" } func TestStringMatching(t *testing.T) { t.Parallel() for _, expr := range crateRegexps { t.Run(expr, func(t *testing.T) { re, err := regexp.Compile(expr) if err != nil { t.Fatalf("failed to compile %q: %v", expr, err) } Check(t, func(t *T) { s := StringMatching(expr).Draw(t, "s") if !re.MatchString(s) { t.Fatalf("%q does not match %q", s, expr) } }) }) } } func TestSliceOfBytesMatching(t *testing.T) { t.Parallel() for _, expr := range crateRegexps { t.Run(expr, func(t *testing.T) { re, err := regexp.Compile(expr) if err != nil { t.Fatalf("failed to compile %q: %v", expr, err) } Check(t, func(t *T) { s := SliceOfBytesMatching(expr).Draw(t, "s") if !re.Match(s) { t.Fatalf("%q does not match %q", s, expr) } }) }) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/shrink.go�������������������������������������������������������������������������������0000664�0000000�0000000�00000022575�15162516440�0014456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "encoding/binary" "fmt" "math" "math/bits" "os" "strings" "time" ) const ( labelLowerFloatExp = "lower_float_exp" labelLowerFloatSignif = "lower_float_signif" labelLowerFloatFrac = "lower_float_frac" labelMinBlockBinSearch = "minblock_binsearch" labelMinBlockShift = "minblock_shift" labelMinBlockSort = "minblock_sort" labelMinBlockTrySmall = "minblock_trysmall" labelMinBlockUnset = "minblock_unset" labelRemoveGroup = "remove_group" labelRemoveGroupAndLower = "remove_group_lower" labelRemoveGroupSpan = "remove_groupspan" labelSortGroups = "sort_groups" ) func shrink(tb tb, deadline time.Time, rec recordedBits, err *testError, prop func(*T)) ([]uint64, *testError) { rec.prune() s := &shrinker{ tb: tb, rec: rec, err: err, prop: prop, visBits: []recordedBits{rec}, tries: map[string]int{}, cache: map[string]struct{}{}, } buf, err := s.shrink(deadline) if flags.debugvis { name := fmt.Sprintf("vis-%v.html", strings.Replace(tb.Name(), "/", "_", -1)) f, err := os.Create(name) if err != nil { tb.Logf("failed to create debugvis file %v: %v", name, err) } else { defer func() { _ = f.Close() }() if err = visWriteHTML(f, tb.Name(), s.visBits); err != nil { tb.Logf("failed to write debugvis file %v: %v", name, err) } } } return buf, err } type shrinker struct { tb tb rec recordedBits err *testError prop func(*T) visBits []recordedBits tries map[string]int shrinks int cache map[string]struct{} hits int } func (s *shrinker) debugf(verbose_ bool, format string, args ...any) { if flags.debug && (!verbose_ || flags.verbose) { s.tb.Helper() s.tb.Logf("[shrink] "+format, args...) } } func (s *shrinker) shrink(deadline time.Time) (buf []uint64, err *testError) { defer func() { if r := recover(); r != nil { buf, err = s.rec.data, r.(*testError) } }() i := 0 for shrinks := -1; s.shrinks > shrinks && time.Now().Before(deadline); i++ { shrinks = s.shrinks s.debugf(false, "round %v start", i) s.removeGroups(deadline) s.minimizeBlocks(deadline) if s.shrinks == shrinks { s.debugf(false, "trying expensive algorithms for round %v", i) s.lowerFloatHack(deadline) s.removeGroupsAndLower(deadline) s.sortGroups(deadline) s.removeGroupSpans(deadline) } } tries := 0 for _, n := range s.tries { tries += n } s.debugf(false, "done, %v rounds total (%v tries, %v shrinks, %v cache hits):\n%v", i, tries, s.shrinks, s.hits, s.tries) return s.rec.data, s.err } func (s *shrinker) removeGroups(deadline time.Time) { for i := 0; i < len(s.rec.groups) && time.Now().Before(deadline); i++ { g := s.rec.groups[i] if !g.standalone || g.end < 0 { continue } if s.accept(without(s.rec.data, g), labelRemoveGroup, "remove group %q at %v: [%v, %v)", g.label, i, g.begin, g.end) { i-- } } } func (s *shrinker) minimizeBlocks(deadline time.Time) { for i := 0; i < len(s.rec.data) && time.Now().Before(deadline); i++ { minimize(s.rec.data[i], func(u uint64, label string) bool { buf := append([]uint64(nil), s.rec.data...) buf[i] = u return s.accept(buf, label, "minimize block %v: %v to %v", i, s.rec.data[i], u) }) } } func (s *shrinker) lowerFloatHack(deadline time.Time) { for i := 0; i < len(s.rec.groups) && time.Now().Before(deadline); i++ { g := s.rec.groups[i] if !g.standalone || g.end != g.begin+7 { continue } buf := append([]uint64(nil), s.rec.data...) buf[g.begin+3] -= 1 buf[g.begin+4] = math.MaxUint64 buf[g.begin+5] = math.MaxUint64 buf[g.begin+6] = math.MaxUint64 if !s.accept(buf, labelLowerFloatExp, "lower float exponent of group %q at %v to %v", g.label, i, buf[g.begin+3]) { buf := append([]uint64(nil), s.rec.data...) buf[g.begin+4] -= 1 buf[g.begin+5] = math.MaxUint64 buf[g.begin+6] = math.MaxUint64 if !s.accept(buf, labelLowerFloatSignif, "lower float significant of group %q at %v to %v", g.label, i, buf[g.begin+4]) { buf := append([]uint64(nil), s.rec.data...) buf[g.begin+5] -= 1 buf[g.begin+6] = math.MaxUint64 s.accept(buf, labelLowerFloatFrac, "lower float frac of group %q at %v to %v", g.label, i, buf[g.begin+5]) } } } } func (s *shrinker) removeGroupsAndLower(deadline time.Time) { for i := 0; i < len(s.rec.data) && time.Now().Before(deadline); i++ { if s.rec.data[i] == 0 { continue } buf := append([]uint64(nil), s.rec.data...) buf[i] -= 1 for j := 0; j < len(s.rec.groups); j++ { g := s.rec.groups[j] if !g.standalone || g.end < 0 || (i >= g.begin && i < g.end) { continue } if s.accept(without(buf, g), labelRemoveGroupAndLower, "lower block %v to %v and remove group %q at %v: [%v, %v)", i, buf[i], g.label, j, g.begin, g.end) { i-- break } } } } func (s *shrinker) sortGroups(deadline time.Time) { for i := 1; i < len(s.rec.groups) && time.Now().Before(deadline); i++ { for j := i; j > 0; { g := s.rec.groups[j] if !g.standalone || g.end < 0 { break } j_ := j for j--; j >= 0; j-- { h := s.rec.groups[j] if !h.standalone || h.end < 0 || h.end > g.begin || h.label != g.label { continue } buf := append([]uint64(nil), s.rec.data[:h.begin]...) buf = append(buf, s.rec.data[g.begin:g.end]...) buf = append(buf, s.rec.data[h.end:g.begin]...) buf = append(buf, s.rec.data[h.begin:h.end]...) buf = append(buf, s.rec.data[g.end:]...) if s.accept(buf, labelSortGroups, "swap groups %q at %v: [%v, %v) and %q at %v: [%v, %v)", g.label, j_, g.begin, g.end, h.label, j, h.begin, h.end) { break } } } } } func (s *shrinker) removeGroupSpans(deadline time.Time) { for i := 0; i < len(s.rec.groups) && time.Now().Before(deadline); i++ { g := s.rec.groups[i] if !g.standalone || g.end < 0 { continue } groups := []groupInfo{g} for j := i + 1; j < len(s.rec.groups); j++ { h := s.rec.groups[j] if !h.standalone || h.end < 0 || h.begin < groups[len(groups)-1].end { continue } groups = append(groups, h) buf := without(s.rec.data, groups...) if s.accept(buf, labelRemoveGroupSpan, "remove %v groups %v", len(groups), groups) { i-- break } } } } func (s *shrinker) accept(buf []uint64, label string, format string, args ...any) bool { if compareData(buf, s.rec.data) >= 0 { return false } bufStr := dataStr(buf) if _, ok := s.cache[bufStr]; ok { s.hits++ return false } s.debugf(true, label+": trying to reproduce the failure with a smaller test case: "+format, args...) s.tries[label]++ s1 := newBufBitStream(buf, false) err1 := checkOnce(newT(s.tb, s1, flags.debug && flags.verbose, nil), s.prop) if traceback(err1) != traceback(s.err) { s.cache[bufStr] = struct{}{} return false } s.debugf(true, label+": trying to reproduce the failure") s.tries[label]++ s.err = err1 s2 := newBufBitStream(buf, true) err2 := checkOnce(newT(s.tb, s2, flags.debug && flags.verbose, nil), s.prop) s.rec = s2.recordedBits s.rec.prune() assert(compareData(s.rec.data, buf) <= 0) if flags.debugvis { s.visBits = append(s.visBits, s.rec) } if !sameError(err1, err2) { panic(err2) } s.debugf(false, label+" success: "+format, args...) s.shrinks++ return true } func minimize(u uint64, cond func(uint64, string) bool) uint64 { if u == 0 { return 0 } for i := uint64(0); i < u && i < small; i++ { if cond(i, labelMinBlockTrySmall) { return i } } if u <= small { return u } m := &minimizer{best: u, cond: cond} m.rShift() m.unsetBits() m.sortBits() m.binSearch() return m.best } type minimizer struct { best uint64 cond func(uint64, string) bool } func (m *minimizer) accept(u uint64, label string) bool { if u >= m.best || u < small || !m.cond(u, label) { return false } m.best = u return true } func (m *minimizer) rShift() { for m.accept(m.best>>1, labelMinBlockShift) { } } func (m *minimizer) unsetBits() { size := bits.Len64(m.best) for i := size - 1; i >= 0; i-- { m.accept(m.best^1<<uint(i), labelMinBlockUnset) } } func (m *minimizer) sortBits() { size := bits.Len64(m.best) for i := size - 1; i >= 0; i-- { h := uint64(1 << uint(i)) if m.best&h != 0 { for j := 0; j < i; j++ { l := uint64(1 << uint(j)) if m.best&l == 0 { if m.accept(m.best^(l|h), labelMinBlockSort) { break } } } } } } func (m *minimizer) binSearch() { if !m.accept(m.best-1, labelMinBlockBinSearch) { return } i := uint64(0) j := m.best for i < j { h := i + (j-i)/2 if m.accept(h, labelMinBlockBinSearch) { j = h } else { i = h + 1 } } } func without(data []uint64, groups ...groupInfo) []uint64 { buf := append([]uint64(nil), data...) for i := len(groups) - 1; i >= 0; i-- { g := groups[i] buf = append(buf[:g.begin], buf[g.end:]...) } return buf } func dataStr(data []uint64) string { b := &strings.Builder{} err := binary.Write(b, binary.LittleEndian, data) assert(err == nil) return b.String() } func compareData(a []uint64, b []uint64) int { if len(a) < len(b) { return -1 } if len(a) > len(b) { return 1 } for i := range a { if a[i] < b[i] { return -1 } if a[i] > b[i] { return 1 } } return 0 } �����������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/shrink_test.go��������������������������������������������������������������������������0000664�0000000�0000000�00000011603�15162516440�0015503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "fmt" "math" "math/bits" "sort" "strconv" "testing" ) const shrinkTestRuns = 10 func TestShrink_IntCmp(t *testing.T) { t.Parallel() ref := []struct { gt bool a int b int eq bool }{ {true, 1000000, 1000001, false}, {true, -1000000, 0, false}, {true, 0, 0, true}, {false, 1000000, 0, false}, {false, -1000000, -1000001, false}, {false, 0, 0, true}, } for _, r := range ref { t.Run(fmt.Sprintf("%v", r), func(t *testing.T) { checkShrink(t, func(t *T) { i := Int().Draw(t, "i") if ((r.gt && i > r.a) || (!r.gt && i < r.a)) || (r.eq && i == r.a) { t.Fail() } }, r.b) }) } } func TestShrink_FloatCmp(t *testing.T) { t.Parallel() type cmp struct { gt bool a float64 b float64 eq bool } ref := []cmp{ {true, 1000000, 1000000.5, false}, {true, math.Pi, 3.5, false}, {true, 1, 1, true}, {true, -1000000, 1, false}, {false, -1000000, -1000000.5, false}, {false, -math.E, -2.75, false}, } if *flaky { ref = append(ref, cmp{false, 0, -1, true}) // sometimes we end up at exactly 0 } for _, r := range ref { t.Run(fmt.Sprintf("%v", r), func(t *testing.T) { checkShrink(t, func(t *T) { f := Float64().Draw(t, "f") if ((r.gt && f > r.a) || (!r.gt && f < r.a)) || (r.eq && f == r.a) { t.Fail() } }, r.b) }) } } func TestShrink_IntSliceNElemsGt(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { s := SliceOf(Int()).Draw(t, "s") n := 0 for _, i := range s { if i > 1000000 { n++ } } if n > 1 { t.Fail() } }, []int{1000001, 1000001}) } func TestShrink_IntSliceElemGe(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { s := SliceOfN(Int(), 1, -1).Draw(t, "s") ix := IntRange(0, len(s)-1).Draw(t, "ix") if s[ix] >= 100 { t.Fail() } }, []int{100}, 0) } func TestShrink_IntSliceElemSpanGe(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { s := SliceOfN(Int(), 4, -1).Draw(t, "s") if len(s)%3 == 1 && s[len(s)-1] >= 100 { t.Fail() } }, []int{0, 0, 0, 100}) } func TestShrink_IntSliceNoDuplicates(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { s := SliceOfN(IntMin(1), 5, -1).Draw(t, "s") sort.Ints(s) last := 0 for _, i := range s { if i == last { return } last = i } t.Fail() }, []int{1, 2, 3, 4, 5}) } func TestShrink_String(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { s1 := String().Draw(t, "s1") s2 := String().Draw(t, "s2") if len(s1) > len(s2) { t.Fail() } }, "A", "") } func TestShrink_StringOf(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { s1 := StringOf(RuneFrom([]rune{'X', 'Y', 'Z'})).Draw(t, "s1") s2 := StringOf(RuneFrom([]rune{'U', 'V', 'W'})).Draw(t, "s2") if len(s1) > len(s2) { t.Fail() } }, "X", "") } func TestMinimize_UnsetBits(t *testing.T) { t.Parallel() Check(t, func(t *T) { mask := Uint64Range(0, math.MaxUint64).Draw(t, "mask") best := minimize(math.MaxUint64, func(x uint64, s string) bool { return x&mask == mask }) if best != mask { t.Fatalf("unset to %v instead of %v", bin(best), bin(mask)) } }) } func TestMinimize_SortBits(t *testing.T) { t.Parallel() Check(t, func(t *T) { u := Uint64Range(0, math.MaxUint64).Draw(t, "u") n := bits.OnesCount64(u) v := uint64(1<<uint(n) - 1) best := minimize(u, func(x uint64, s string) bool { return bits.OnesCount64(x) == n }) if best != v { t.Fatalf("minimized to %v instead of %v (%v bits set)", bin(best), bin(v), n) } }) } func TestMinimize_LowerBound(t *testing.T) { t.Parallel() Check(t, func(t *T) { min := Uint64().Draw(t, "min") u := Uint64Min(min).Draw(t, "u") best := minimize(u, func(x uint64, s string) bool { return x >= min }) if best != min { t.Fatalf("found %v instead of %v", best, min) } }) } func checkShrink(t *testing.T, prop func(*T), draws ...any) { t.Helper() for i := 0; i < shrinkTestRuns; i++ { t.Run(strconv.Itoa(i), func(t *testing.T) { t.Helper() _, _, _, seed, _, buf, err1, err2 := doCheck(t, checkDeadline(nil), 100, baseSeed(), "", false, prop) if seed != 0 && err1 == nil && err2 == nil { t.Fatalf("shrink test did not fail (seed %v)", seed) } if traceback(err1) != traceback(err2) { t.Fatalf("flaky shrink test (seed %v)\nTraceback (%v):\n%vOriginal traceback (%v):\n%v", seed, err2, traceback(err2), err1, traceback(err1)) } nt := newT(t, newBufBitStream(buf, false), false, nil, draws...) _ = checkOnce(nt, prop) if nt.draws != len(draws) { t.Fatalf("different number of draws: %v vs expected %v", nt.draws, len(draws)) } }) } } func bin(u uint64) string { return "0b" + strconv.FormatUint(u, 2) } �����������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/statemachine.go�������������������������������������������������������������������������0000664�0000000�0000000�00000006731�15162516440�0015621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "reflect" "sort" "testing" ) const ( actionLabel = "action" validActionTries = 100 // hack, but probably good enough for now checkMethodName = "Check" noValidActionsMsg = "can't find a valid (non-skipped) action" ) // Repeat executes a random sequence of actions (often called a "state machine" test). // actions[""], if set, is executed before/after every other action invocation // and should only contain invariant checking code. // // For complex state machines, it can be more convenient to specify actions as // methods of a special state machine type. In this case, [StateMachineActions] // can be used to create an actions map from state machine methods using reflection. func (t *T) Repeat(actions map[string]func(*T)) { t.Helper() check := func(*T) {} actionKeys := make([]string, 0, len(actions)) for key, action := range actions { if key != "" { actionKeys = append(actionKeys, key) } else { check = action } } if len(actionKeys) == 0 { return } sort.Strings(actionKeys) steps := flags.steps if testing.Short() { steps /= 2 } repeat := newRepeat(-1, -1, float64(steps), "Repeat") sm := stateMachine{ check: check, actionKeys: SampledFrom(actionKeys), actions: actions, } sm.check(t) t.failOnError() for repeat.more(t.s) { ok := sm.executeAction(t) if ok { sm.check(t) t.failOnError() } else { repeat.reject() } } } type StateMachine interface { // Check is ran after every action and should contain invariant checks. // // All other public methods should have a form ActionName(t *rapid.T) // or ActionName(t rapid.TB) and are used as possible actions. // At least one action has to be specified. Check(*T) } // StateMachineActions creates an actions map for [*T.Repeat] // from methods of a [StateMachine] type instance using reflection. func StateMachineActions(sm StateMachine) map[string]func(*T) { var ( v = reflect.ValueOf(sm) t = v.Type() n = t.NumMethod() ) actions := make(map[string]func(*T), n) for i := 0; i < n; i++ { name := t.Method(i).Name if name == checkMethodName { continue } m, ok := v.Method(i).Interface().(func(*T)) if ok { actions[name] = m } m2, ok := v.Method(i).Interface().(func(TB)) if ok { actions[name] = func(t *T) { m2(t) } } } assertf(len(actions) > 0, "state machine of type %v has no actions specified", t) actions[""] = sm.Check return actions } type stateMachine struct { check func(*T) actionKeys *Generator[string] actions map[string]func(*T) } func (sm *stateMachine) executeAction(t *T) bool { t.Helper() for n := 0; n < validActionTries; n++ { i := t.s.beginGroup(actionLabel, false) action := sm.actions[sm.actionKeys.Draw(t, "action")] invalid, skipped := runAction(t, action) t.s.endGroup(i, false) if skipped { continue } else { return !invalid } } panic(stopTest(noValidActionsMsg)) } func runAction(t *T, action func(*T)) (invalid bool, skipped bool) { defer func(draws int) { if r := recover(); r != nil { if _, ok := r.(invalidData); ok { invalid = true skipped = t.draws == draws } else { panic(r) } } }(t.draws) action(t) t.failOnError() return false, false } ���������������������������������������rapid-1.3.0/statemachine_test.go��������������������������������������������������������������������0000664�0000000�0000000�00000013515�15162516440�0016656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "reflect" "testing" ) // https://github.com/leanovate/gopter/blob/master/commands/example_circularqueue_test.go var gopterBug = false // https://pkg.go.dev/github.com/leanovate/gopter/commands?tab=doc#example-package-BuggyCounter type buggyCounter struct { n int } func (c *buggyCounter) Get() int { return c.n } func (c *buggyCounter) Inc() { c.n++ } func (c *buggyCounter) Dec() { if c.n > 3 { c.n -= 2 } else { c.n-- } } func (c *buggyCounter) Reset() { c.n = 0 } func TestStateMachine_Counter(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { var c buggyCounter var incs, decs int t.Repeat(map[string]func(*T){ "Inc": func(_ *T) { c.Inc() incs++ }, "Dec": func(_ *T) { c.Dec() decs++ }, "Reset": func(_ *T) { c.Reset() incs = 0 decs = 0 }, "": func(t *T) { if c.Get() != incs-decs { t.Fatalf("counter value is %v with %v incs and %v decs", c.Get(), incs, decs) } }, }) }, "Inc", "Inc", "Inc", "Inc", "Dec", ) } func TestStateMachine_Halting(t *testing.T) { t.Parallel() a := []any{"A", 0, "A", 0, "A", 0} for i := 0; i < 100; i++ { a = append(a, "A") // TODO proper shrinking of "stuck" state machines } checkShrink(t, func(t *T) { var a, b, c []int t.Repeat(map[string]func(*T){ "A": func(t *T) { if len(a) == 3 { t.SkipNow() } a = append(a, Int().Draw(t, "a")) }, "B": func(t *T) { if len(b) == 3 { t.SkipNow() } b = append(b, Int().Draw(t, "b")) }, "C": func(t *T) { if len(c) == 3 { t.SkipNow() } c = append(c, Int().Draw(t, "c")) }, "": func(t *T) { if len(a) > 3 || len(b) > 3 || len(c) > 3 { t.Fatalf("too many elements: %v, %v, %v", len(a), len(b), len(c)) } }, }) }, a...) } // https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quviq-testing.pdf type buggyQueue struct { buf []int in int out int } func newBuggyQueue(size int) *buggyQueue { return &buggyQueue{ buf: make([]int, size+1), } } func (q *buggyQueue) Get() int { n := q.buf[q.out] q.out = (q.out + 1) % len(q.buf) return n } func (q *buggyQueue) Put(i int) { if gopterBug && q.in == 4 && i > 0 { q.buf[len(q.buf)-1] *= i } q.buf[q.in] = i q.in = (q.in + 1) % len(q.buf) } func (q *buggyQueue) Size() int { if gopterBug { return (q.in - q.out + len(q.buf)) % len(q.buf) } else { return (q.in - q.out) % len(q.buf) } } func queueTest(t *T) { size := IntRange(1, 1000).Draw(t, "size") q := newBuggyQueue(size) var state []int t.Repeat(map[string]func(*T){ "Get": func(t *T) { if q.Size() == 0 { t.Skip("queue empty") } n := q.Get() if n != state[0] { t.Fatalf("got invalid value: %v vs expected %v", n, state[0]) } state = state[1:] }, "Put": func(t *T) { if q.Size() == size { t.Skip("queue full") } n := Int().Draw(t, "n") q.Put(n) state = append(state, n) }, "": func(t *T) { if q.Size() != len(state) { t.Fatalf("queue size mismatch: %v vs expected %v", q.Size(), len(state)) } }, }) } func TestStateMachine_Queue(t *testing.T) { t.Parallel() checkShrink(t, queueTest, 1, "Put", 0, "Get", "Put", 0, ) } func TestStateMachine_DiscardGarbage(t *testing.T) { t.Parallel() checkShrink(t, func(t *T) { var a, b []int t.Repeat(map[string]func(*T){ "AddA": func(t *T) { if len(b) < 3 { t.Skip("too early") } n := Int().Draw(t, "a") a = append(a, n) }, "AddB": func(t *T) { n := Int().Draw(t, "b") b = append(b, n) }, "Whatever1": func(t *T) { b := Bool().Draw(t, "whatever 1/1") if b { t.Skip("arbitrary decision") } Float64().Draw(t, "whatever 1/2") }, "Whatever2": func(t *T) { SliceOfDistinct(Int(), ID[int]).Draw(t, "whatever 2") }, "Whatever3": func(t *T) { OneOf(SliceOf(Byte()), SliceOf(ByteMax(239))).Draw(t, "whatever 3") }, "": func(t *T) { if len(a) > len(b) { t.Fatalf("`a` has outgrown `b`: %v vs %v", len(a), len(b)) } }, }) }, "AddB", 0, "AddB", 0, "AddB", 0, "AddA", 0, "AddA", 0, "AddA", 0, "AddA", 0, ) } type stateMachineTest struct { run []string } func (sm *stateMachineTest) Check(t *T) {} func (sm *stateMachineTest) ActionT(t *T) { sm.run = append(sm.run, "ActionT") } func (sm *stateMachineTest) ActionTB(t TB) { if len(sm.run) > 2 { // Add a value to run that isn't expected to ensure the post-action check is skipped. sm.run = append(sm.run, "Post-Skip") t.Skip() } sm.run = append(sm.run, "ActionTB") } func TestStateMachineActions(t *testing.T) { t.Run("Check", MakeCheck(func(t *T) { sm := &stateMachineTest{} actions := StateMachineActions(sm) actionT, ok := actions["ActionT"] if !ok { t.Fatalf("ActionA missing") } actionTB, ok := actions["ActionTB"] if !ok { t.Fatalf("ActionTB missing") } var want []string for i := 0; i < Int().Draw(t, "ActionT count"); i++ { actionT(t) want = append(want, "ActionT") } for i := 0; i < Int().Draw(t, "ActionTB count"); i++ { actionTB(t) want = append(want, "ActionTB") } if !reflect.DeepEqual(want, sm.run) { t.Fatalf("expected state %v, got %v", want, sm.run) } })) t.Run("directly use action with testing.T", func(t *testing.T) { sm := &stateMachineTest{} sm.ActionTB(t) if want := []string{"ActionTB"}; !reflect.DeepEqual(want, sm.run) { t.Fatalf("expected state %v, got %v", want, sm.run) } }) } func BenchmarkCheckQueue(b *testing.B) { for i := 0; i < b.N; i++ { _, _, _, _, _, _, _, _ = doCheck(b, checkDeadline(nil), 100, baseSeed(), "", false, queueTest) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/strings.go������������������������������������������������������������������������������0000664�0000000�0000000�00000027341�15162516440�0014645�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "bytes" "fmt" "math" "regexp" "regexp/syntax" "strings" "sync" "unicode" "unicode/utf8" ) var ( defaultRunes = []rune{ 'A', 'a', '?', '~', '!', '@', '#', '$', '%', '^', '&', '*', '_', '-', '+', '=', '.', ',', ':', ';', ' ', '\t', '\r', '\n', '/', '\\', '|', '(', '[', '{', '<', '\'', '"', '`', '\x00', '\x0B', '\x1B', '\x7F', // NUL, VT, ESC, DEL '\uFEFF', '\uFFFD', '\u202E', // BOM, replacement character, RTL override 'Ⱥ', // In UTF-8, Ⱥ increases in length from 2 to 3 bytes when lowercased } // unicode.Categories without surrogates (which are not allowed in UTF-8), ordered by taste defaultTables = []*unicode.RangeTable{ unicode.Lu, // Letter, uppercase (1781) unicode.Ll, // Letter, lowercase (2145) unicode.Lt, // Letter, titlecase (31) unicode.Lm, // Letter, modifier (250) unicode.Lo, // Letter, other (121212) unicode.Nd, // Number, decimal digit (610) unicode.Nl, // Number, letter (236) unicode.No, // Number, other (807) unicode.P, // Punctuation (788) unicode.Sm, // Symbol, math (948) unicode.Sc, // Symbol, currency (57) unicode.Sk, // Symbol, modifier (121) unicode.So, // Symbol, other (5984) unicode.Mn, // Mark, nonspacing (1805) unicode.Me, // Mark, enclosing (13) unicode.Mc, // Mark, spacing combining (415) unicode.Z, // Separator (19) unicode.Cc, // Other, control (65) unicode.Cf, // Other, format (152) unicode.Co, // Other, private use (137468) } expandedTables = sync.Map{} // *unicode.RangeTable / regexp name -> []rune compiledRegexps = sync.Map{} // regexp -> compiledRegexp regexpNames = sync.Map{} // *regexp.Regexp -> string charClassGens = sync.Map{} // regexp name -> *Generator anyRuneGen = Rune() anyRuneGenNoNL = Rune().Filter(func(r rune) bool { return r != '\n' }) ) type compiledRegexp struct { syn *syntax.Regexp re *regexp.Regexp } // Rune creates a rune generator. Rune is equivalent to [RuneFrom] with default set of runes and tables. func Rune() *Generator[rune] { return runesFrom(true, defaultRunes, defaultTables...) } // RuneFrom creates a rune generator from provided runes and tables. // RuneFrom panics if both runes and tables are empty. RuneFrom panics if tables contain an empty table. func RuneFrom(runes []rune, tables ...*unicode.RangeTable) *Generator[rune] { return runesFrom(false, runes, tables...) } func runesFrom(default_ bool, runes []rune, tables ...*unicode.RangeTable) *Generator[rune] { if len(tables) == 0 { assertf(len(runes) > 0, "at least one rune should be specified") } if len(runes) == 0 { assertf(len(tables) > 0, "at least one *unicode.RangeTable should be specified") } var weights []int if len(runes) > 0 { weights = append(weights, len(tables)) } for range tables { weights = append(weights, 1) } tables_ := make([][]rune, len(tables)) for i := range tables { tables_[i] = expandRangeTable(tables[i], tables[i]) assertf(len(tables_[i]) > 0, "empty *unicode.RangeTable %v", i) } return newGenerator[rune](&runeGen{ die: newLoadedDie(weights), runes: runes, tables: tables_, default_: default_, }) } type runeGen struct { die *loadedDie runes []rune tables [][]rune default_ bool } func (g *runeGen) String() string { if g.default_ { return "Rune()" } else { return fmt.Sprintf("Rune(%v runes, %v tables)", len(g.runes), len(g.tables)) } } func (g *runeGen) value(t *T) rune { n := g.die.roll(t.s) runes := g.runes if len(g.runes) == 0 { runes = g.tables[n] } else if n > 0 { runes = g.tables[n-1] } return runes[genIndex(t.s, len(runes), true)] } // String is a shorthand for [StringOf]([Rune]()). func String() *Generator[string] { return StringOf(anyRuneGen) } // StringN is a shorthand for [StringOfN]([Rune](), minRunes, maxRunes, maxLen). func StringN(minRunes int, maxRunes int, maxLen int) *Generator[string] { return StringOfN(anyRuneGen, minRunes, maxRunes, maxLen) } // StringOf is a shorthand for [StringOfN](elem, -1, -1, -1). func StringOf(elem *Generator[rune]) *Generator[string] { return StringOfN(elem, -1, -1, -1) } // StringOfN creates a UTF-8 string generator. // If minRunes >= 0, generated strings have minimum minRunes runes. // If maxRunes >= 0, generated strings have maximum maxRunes runes. // If maxLen >= 0, generates strings have maximum length of maxLen. // StringOfN panics if maxRunes >= 0 and minRunes > maxRunes. // StringOfN panics if maxLen >= 0 and maxLen < maxRunes. func StringOfN(elem *Generator[rune], minRunes int, maxRunes int, maxLen int) *Generator[string] { assertValidRange(minRunes, maxRunes) assertf(maxLen < 0 || maxLen >= maxRunes, "maximum length (%v) should not be less than maximum number of runes (%v)", maxLen, maxRunes) return newGenerator[string](&stringGen{ elem: elem, minRunes: minRunes, maxRunes: maxRunes, maxLen: maxLen, }) } type stringGen struct { elem *Generator[rune] minRunes int maxRunes int maxLen int } func (g *stringGen) String() string { if g.elem == anyRuneGen { if g.minRunes < 0 && g.maxRunes < 0 && g.maxLen < 0 { return "String()" } else { return fmt.Sprintf("StringN(minRunes=%v, maxRunes=%v, maxLen=%v)", g.minRunes, g.maxRunes, g.maxLen) } } else { if g.minRunes < 0 && g.maxRunes < 0 && g.maxLen < 0 { return fmt.Sprintf("StringOf(%v)", g.elem) } else { return fmt.Sprintf("StringOfN(%v, minRunes=%v, maxRunes=%v, maxLen=%v)", g.elem, g.minRunes, g.maxRunes, g.maxLen) } } } func (g *stringGen) value(t *T) string { repeat := newRepeat(g.minRunes, g.maxRunes, -1, g.elem.String()) var b strings.Builder b.Grow(repeat.avg()) maxLen := g.maxLen if maxLen < 0 { maxLen = math.MaxInt } for repeat.more(t.s) { r := g.elem.value(t) n := utf8.RuneLen(r) if n < 0 || b.Len()+n > maxLen { repeat.reject() } else { b.WriteRune(r) } } return b.String() } // StringMatching creates a UTF-8 string generator matching the provided [syntax.Perl] regular expression. func StringMatching(expr string) *Generator[string] { compiled, err := compileRegexp(expr) assertf(err == nil, "%v", err) return newGenerator[string](®expStringGen{ regexpGen{ expr: expr, syn: compiled.syn, re: compiled.re, }, }) } // SliceOfBytesMatching creates a UTF-8 byte slice generator matching the provided [syntax.Perl] regular expression. func SliceOfBytesMatching(expr string) *Generator[[]byte] { compiled, err := compileRegexp(expr) assertf(err == nil, "%v", err) return newGenerator[[]byte](®expSliceGen{ regexpGen{ expr: expr, syn: compiled.syn, re: compiled.re, }, }) } type runeWriter interface { WriteRune(r rune) (int, error) } type regexpGen struct { expr string syn *syntax.Regexp re *regexp.Regexp } type regexpStringGen struct{ regexpGen } type regexpSliceGen struct{ regexpGen } func (g *regexpStringGen) String() string { return fmt.Sprintf("StringMatching(%q)", g.expr) } func (g *regexpSliceGen) String() string { return fmt.Sprintf("SliceOfBytesMatching(%q)", g.expr) } func (g *regexpStringGen) maybeString(t *T) (string, bool) { b := &strings.Builder{} g.build(b, g.syn, t) v := b.String() if g.re.MatchString(v) { return v, true } else { return "", false } } func (g *regexpSliceGen) maybeSlice(t *T) ([]byte, bool) { b := &bytes.Buffer{} g.build(b, g.syn, t) v := b.Bytes() if g.re.Match(v) { return v, true } else { return nil, false } } func (g *regexpStringGen) value(t *T) string { return find(g.maybeString, t, small) } func (g *regexpSliceGen) value(t *T) []byte { return find(g.maybeSlice, t, small) } func (g *regexpGen) build(w runeWriter, re *syntax.Regexp, t *T) { i := t.s.beginGroup(re.Op.String(), false) switch re.Op { case syntax.OpNoMatch: panic(invalidData("no possible regexp match")) case syntax.OpEmptyMatch: t.s.drawBits(0) case syntax.OpLiteral: t.s.drawBits(0) for _, r := range re.Rune { _, _ = w.WriteRune(maybeFoldCase(t.s, r, re.Flags)) } case syntax.OpCharClass, syntax.OpAnyCharNotNL, syntax.OpAnyChar: sub := anyRuneGen switch re.Op { case syntax.OpCharClass: sub = charClassGen(re) case syntax.OpAnyCharNotNL: sub = anyRuneGenNoNL } r := sub.value(t) _, _ = w.WriteRune(maybeFoldCase(t.s, r, re.Flags)) case syntax.OpBeginLine, syntax.OpEndLine, syntax.OpBeginText, syntax.OpEndText, syntax.OpWordBoundary, syntax.OpNoWordBoundary: t.s.drawBits(0) // do nothing and hope that find() is enough case syntax.OpCapture: g.build(w, re.Sub[0], t) case syntax.OpStar, syntax.OpPlus, syntax.OpQuest, syntax.OpRepeat: min, max := re.Min, re.Max switch re.Op { case syntax.OpStar: min, max = 0, -1 case syntax.OpPlus: min, max = 1, -1 case syntax.OpQuest: min, max = 0, 1 } repeat := newRepeat(min, max, -1, regexpName(re.Sub[0])) for repeat.more(t.s) { g.build(w, re.Sub[0], t) } case syntax.OpConcat: for _, sub := range re.Sub { g.build(w, sub, t) } case syntax.OpAlternate: ix := genIndex(t.s, len(re.Sub), true) g.build(w, re.Sub[ix], t) default: assertf(false, "invalid regexp op %v", re.Op) } t.s.endGroup(i, false) } func maybeFoldCase(s bitStream, r rune, flags syntax.Flags) rune { n := uint64(0) if flags&syntax.FoldCase != 0 { n, _, _ = genUintN(s, 4, false) } for i := 0; i < int(n); i++ { r = unicode.SimpleFold(r) } return r } func expandRangeTable(t *unicode.RangeTable, key any) []rune { cached, ok := expandedTables.Load(key) if ok { return cached.([]rune) } n := 0 for _, r := range t.R16 { n += int(r.Hi-r.Lo)/int(r.Stride) + 1 } for _, r := range t.R32 { n += int(r.Hi-r.Lo)/int(r.Stride) + 1 } ret := make([]rune, 0, n) for _, r := range t.R16 { for i := uint32(r.Lo); i <= uint32(r.Hi); i += uint32(r.Stride) { ret = append(ret, rune(i)) } } for _, r := range t.R32 { for i := uint64(r.Lo); i <= uint64(r.Hi); i += uint64(r.Stride) { ret = append(ret, rune(i)) } } expandedTables.Store(key, ret) return ret } func compileRegexp(expr string) (compiledRegexp, error) { cached, ok := compiledRegexps.Load(expr) if ok { return cached.(compiledRegexp), nil } syn, err := syntax.Parse(expr, syntax.Perl) if err != nil { return compiledRegexp{}, fmt.Errorf("failed to parse regexp %q: %v", expr, err) } re, err := regexp.Compile(expr) if err != nil { return compiledRegexp{}, fmt.Errorf("failed to compile regexp %q: %v", expr, err) } ret := compiledRegexp{syn, re} compiledRegexps.Store(expr, ret) return ret, nil } func regexpName(re *syntax.Regexp) string { cached, ok := regexpNames.Load(re) if ok { return cached.(string) } s := re.String() regexpNames.Store(re, s) return s } func charClassGen(re *syntax.Regexp) *Generator[rune] { cached, ok := charClassGens.Load(regexpName(re)) if ok { return cached.(*Generator[rune]) } t := &unicode.RangeTable{R32: make([]unicode.Range32, 0, len(re.Rune)/2)} for i := 0; i < len(re.Rune); i += 2 { // not a valid unicode.Range32, since it requires that Lo and Hi must always be >= 1<<16 // however, we don't really care, since the only use of these ranges is as input to expandRangeTable t.R32 = append(t.R32, unicode.Range32{ Lo: uint32(re.Rune[i]), Hi: uint32(re.Rune[i+1]), Stride: 1, }) } g := newGenerator[rune](&runeGen{ die: newLoadedDie([]int{1}), tables: [][]rune{expandRangeTable(t, regexpName(re))}, }) charClassGens.Store(regexpName(re), g) return g } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/strings_example_test.go�����������������������������������������������������������������0000664�0000000�0000000�00000005341�15162516440�0017413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2023 Gregory Petrosyan <pgregory@pgregory.net> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "fmt" "unicode" "pgregory.net/rapid" ) func ExampleRune() { gen := rapid.Rune() for i := 0; i < 25; i++ { if i%5 == 0 { fmt.Println() } else { fmt.Print(" ") } fmt.Printf("%q", gen.Example(i)) } // Output: // '\n' '\x1b' 'A' 'a' '*' // '0' '@' '?' '\'' '\ue05d' // '<' '%' '!' '\u0604' 'A' // '%' '╷' '~' '!' '/' // '\u00ad' '𝅾' '@' '҈' ' ' } func ExampleRuneFrom() { gens := []*rapid.Generator[rune]{ rapid.RuneFrom([]rune{'A', 'B', 'C'}), rapid.RuneFrom(nil, unicode.Cyrillic, unicode.Greek), rapid.RuneFrom([]rune{'⌘'}, &unicode.RangeTable{ R32: []unicode.Range32{{0x1F600, 0x1F64F, 1}}, }), } for _, gen := range gens { for i := 0; i < 5; i++ { if i > 0 { fmt.Print(" ") } fmt.Printf("%q", gen.Example(i)) } fmt.Println() } // Output: // 'A' 'A' 'A' 'B' 'A' // 'Ͱ' 'Ѥ' 'Ͱ' 'ͱ' 'Ϳ' // '😀' '⌘' '😀' '😁' '😋' } func ExampleString() { gen := rapid.String() for i := 0; i < 5; i++ { fmt.Printf("%q\n", gen.Example(i)) } // Output: // "\n߾⃝?\rA�֍" // "\u2006𑨳" // "A$\u0603ᾢ" // "+^#.[#৲" // "" } func ExampleStringOf() { gen := rapid.StringOf(rapid.RuneFrom(nil, unicode.Tibetan)) for i := 0; i < 5; i++ { fmt.Printf("%q\n", gen.Example(i)) } // Output: // "༁༭༇ཬ༆༐༖ༀྸ༁༆༎ༀ༁ཱི༂༨ༀ༂" // "༂༁ༀ༂༴ༀ༁ྵ" // "ༀ༴༁༅ན༃༁༎ྼ༄༽" // "༎༂༎ༀༀༀཌྷ༂ༀྥ" // "" } func ExampleStringN() { gen := rapid.StringN(5, 5, -1) for i := 0; i < 5; i++ { fmt.Printf("%q\n", gen.Example(i)) } // Output: // "\n߾⃝?\r" // "\u2006𑨳#`\x1b" // "A$\u0603ᾢÉ" // "+^#.[" // ".A<a¤" } func ExampleStringOfN() { gen := rapid.StringOfN(rapid.RuneFrom(nil, unicode.ASCII_Hex_Digit), 6, 6, -1) for i := 0; i < 5; i++ { fmt.Printf("%q\n", gen.Example(i)) } // Output: // "1D7B6a" // "2102e0" // "0e15c3" // "E2E000" // "aEd623" } func ExampleStringMatching() { gen := rapid.StringMatching(`\(?([0-9]{3})\)?([ .-]?)([0-9]{3})([ .-]?)([0-9]{4})`) for i := 0; i < 5; i++ { fmt.Printf("%q\n", gen.Example(i)) } // Output: // "(532) 649-9610" // "901)-5783983" // "914.444.1575" // "(316 696.3584" // "816)0861080" } func ExampleSliceOfBytesMatching() { gen := rapid.SliceOfBytesMatching(`[CAGT]+`) for i := 0; i < 5; i++ { fmt.Printf("%q\n", gen.Example(i)) } // Output: // "CCTTGAGAGCGATACGGAAG" // "GCAGAACT" // "AACCGTCGAG" // "GGGAAAAGAT" // "AGTG" } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/strings_external_test.go����������������������������������������������������������������0000664�0000000�0000000�00000004656�15162516440�0017612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid_test import ( "strconv" "testing" "unicode" "unicode/utf8" . "pgregory.net/rapid" ) func TestStringExamples(t *testing.T) { g := StringN(10, -1, -1) for i := 0; i < 100; i++ { s := g.Example() t.Log(len(s), s) } } func TestRegexpExamples(t *testing.T) { g := StringMatching("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") for i := 0; i < 100; i++ { s := g.Example() t.Log(len(s), s) } } func TestStringOfRunesIsUTF8(t *testing.T) { t.Parallel() gens := []*Generator[string]{ String(), StringN(2, 10, -1), StringOf(Rune()), StringOfN(Rune(), 2, 10, -1), StringOf(RuneFrom(nil, unicode.Cyrillic)), StringOf(RuneFrom([]rune{'a', 'b', 'c'})), } for _, g := range gens { t.Run(g.String(), MakeCheck(func(t *T) { s := g.Draw(t, "s") if !utf8.ValidString(s) { t.Fatalf("invalid UTF-8 string: %q", s) } })) } } func TestStringRuneCountLimits(t *testing.T) { t.Parallel() genFuncs := []func(i, j int) *Generator[string]{ func(i, j int) *Generator[string] { return StringN(i, j, -1) }, func(i, j int) *Generator[string] { return StringOfN(Rune(), i, j, -1) }, } for i, gf := range genFuncs { t.Run(strconv.Itoa(i), MakeCheck(func(t *T) { minRunes := IntRange(0, 256).Draw(t, "minRunes") maxRunes := IntMin(minRunes).Draw(t, "maxRunes") s := gf(minRunes, maxRunes).Draw(t, "s") n := utf8.RuneCountInString(s) if n < minRunes { t.Fatalf("got string with %v runes with lower limit %v", n, minRunes) } if n > maxRunes { t.Fatalf("got string with %v runes with upper limit %v", n, maxRunes) } })) } } func TestStringNMaxLen(t *testing.T) { t.Parallel() genFuncs := []func(int) *Generator[string]{ func(i int) *Generator[string] { return StringN(-1, -1, i) }, func(i int) *Generator[string] { return StringOfN(Rune(), -1, -1, i) }, } for i, gf := range genFuncs { t.Run(strconv.Itoa(i), MakeCheck(func(t *T) { maxLen := IntMin(0).Draw(t, "maxLen") s := gf(maxLen).Draw(t, "s") if len(s) > maxLen { t.Fatalf("got string of length %v with maxLen %v", len(s), maxLen) } })) } } ����������������������������������������������������������������������������������rapid-1.3.0/synctest_disabled.go��������������������������������������������������������������������0000664�0000000�0000000�00000000364�15162516440�0016653�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//go:build !go1.25 package rapid // SyncTest is only available on Go 1.25+. func SyncTest(t *T, _ func(*T)) { if t == nil { panic("rapid.SyncTest requires *rapid.T") } t.Helper() t.Fatalf("[rapid] SyncTest requires Go 1.25 or newer") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/synctest_enabled.go���������������������������������������������������������������������0000664�0000000�0000000�00000006361�15162516440�0016501�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//go:build go1.25 package rapid import ( "testing" "testing/synctest" ) // SyncTest runs prop within a testing/synctest bubble. // Callers must already be executing inside a rapid.Check-style helper; // SyncTest forwards failures to the parent *rapid.T and restores its state afterwards. func SyncTest(t *T, prop func(*T)) { if t == nil { panic("rapid.SyncTest requires *rapid.T") } t.Helper() testT, ok := underlyingTestingT(t.tb) if !ok { t.Fatalf("[rapid] SyncTest requires a *testing.T backing the current rapid test") return } syncTestWithinRapid(t, testT, prop) } func syncTestWithinRapid(t *T, parent *testing.T, prop func(*T)) { // synctest.Test converts failures inside the bubble into parent.FailNow (runtime.Goexit), // which would bypass rapid's panic-based failure capture/shrinking. Run the bubble in a // separate goroutine, swallow failures inside the bubble, and re-panic outside as a // *testError so checkOnce can shrink and generate failfiles as usual. resultCh := make(chan *testError, 1) go func() { var captured *testError returned := false defer func() { if r := recover(); r != nil { captured = panicToError(r, 3) } else if !returned && captured == nil { captured = panicToError(stopTest("[rapid] SyncTest aborted via testing.FailNow"), 3) } resultCh <- captured }() synctest.Test(parent, func(st *testing.T) { st.Helper() prevTB := t.tb prevTBLog := t.tbLog // preserved so we keep the original logging behaviour prevCtx := t.ctx prevCancel := t.cancelCtx prevCleanups := t.cleanups prevCleaning := t.cleaning.Load() t.tb = st // Reset per-run state before the property runs in the bubble. // No lock is needed because no other goroutine touches t before we hand control to prop. t.ctx = nil t.cancelCtx = nil t.cleanups = nil t.cleaning.Store(false) var panicValue any defer func() { if r := recover(); r != nil { panicValue = r } func() { // Always run rapid cleanups, even if the property panicked. defer func() { if r := recover(); r != nil { panicValue = r } }() t.cleanup() }() t.tb = prevTB t.tbLog = prevTBLog t.ctx = prevCtx t.cancelCtx = prevCancel t.cleanups = prevCleanups t.cleaning.Store(prevCleaning) if panicValue != nil { captured = panicToError(panicValue, 3) } }() prop(t) t.failOnError() }) returned = true }() if err := <-resultCh; err != nil { panic(err) } } // underlyingTestingT returns the *testing.T associated with tb, if any. func underlyingTestingT(tbValue TB) (*testing.T, bool) { if tbValue == nil { return nil, false } return underlyingTestingTPrivate(tb(tbValue)) } func underlyingTestingTPrivate(tb tb) (*testing.T, bool) { // Some rapid helpers clone the TB they receive by wrapping it in a new *rapid.T. // This happens, for example, when Custom generators spin up helper *T instances. // When SyncTest needs the underlying *testing.T we peel through any number of *rapid.T // layers until we reach the real testing object. switch t := any(tb).(type) { case *testing.T: return t, true case *T: return underlyingTestingTPrivate(t.tb) case nilTB: return nil, false default: return nil, false } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/synctest_enabled_internal_test.go�������������������������������������������������������0000664�0000000�0000000�00000001351�15162516440�0021426�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//go:build go1.25 package rapid import ( "strings" "testing" ) func TestSyncTest_FailureIsCapturedForShrinking(t *testing.T) { rt := newT(tb(t), newRandomBitStream(1, true), false, nil) err := checkOnce(rt, func(t *T) { SyncTest(t, func(inner *T) { inner.Fatalf("boom") }) }) if err == nil { t.Fatalf("checkOnce did not report failure from SyncTest") } if !err.isStopTest() { t.Fatalf("expected stopTest failure, got %T (%v)", err.data, err) } if !strings.Contains(errorString(err), "boom") { t.Fatalf("missing failure message: %q", errorString(err)) } if !strings.Contains(traceback(err), "synctest_enabled_internal_test.go") { t.Fatalf("traceback does not include property call site:\n%v", traceback(err)) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/synctest_enabled_test.go����������������������������������������������������������������0000664�0000000�0000000�00000001465�15162516440�0017540�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//go:build go1.25 package rapid_test import ( "sync/atomic" "testing" "testing/synctest" "time" "pgregory.net/rapid" ) func TestSyncTest(t *testing.T) { rapid.Check(t, func(rt *rapid.T) { var cleaned atomic.Bool rapid.SyncTest(rt, func(inner *rapid.T) { inner.Cleanup(func() { cleaned.Store(true) }) const sleep = 2 * time.Second start := time.Now() time.Sleep(sleep) if got := time.Since(start); got != sleep { inner.Fatalf("virtual time advanced by %v, want %v", got, sleep) } done := make(chan struct{}) go func() { close(done) }() synctest.Wait() select { case <-done: default: inner.Fatalf("goroutine did not finish inside synctest bubble") } }) if !cleaned.Load() { rt.Fatalf("cleanup registered inside SyncTest did not run") } }) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/utils.go��������������������������������������������������������������������������������0000664�0000000�0000000�00000012501�15162516440�0014304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "math" "math/bits" ) const ( biasLabel = "bias" intBitsLabel = "intbits" coinFlipLabel = "coinflip" dieRollLabel = "dieroll" repeatLabel = "@repeat" ) func bitmask64(n uint) uint64 { return uint64(1)<<n - 1 } func genFloat01(s bitStream) float64 { return float64(s.drawBits(53)) * 0x1.0p-53 } func genGeom(s bitStream, p float64) uint64 { assert(p > 0 && p <= 1) f := genFloat01(s) n := math.Log1p(-f) / math.Log1p(-p) return uint64(n) } func genUintNNoReject(s bitStream, max uint64) uint64 { bitlen := bits.Len64(max) i := s.beginGroup(intBitsLabel, false) u := s.drawBits(bitlen) s.endGroup(i, false) if u > max { u = max } return u } func genUintNUnbiased(s bitStream, max uint64) uint64 { bitlen := bits.Len64(max) for { i := s.beginGroup(intBitsLabel, false) u := s.drawBits(bitlen) ok := u <= max s.endGroup(i, !ok) if ok { return u } } } func genUintNBiased(s bitStream, max uint64) (uint64, bool, bool) { bitlen := bits.Len64(max) i := s.beginGroup(biasLabel, false) m := math.Max(8, (float64(bitlen)+48)/7) n := genGeom(s, 1/(m+1)) + 1 s.endGroup(i, false) if int(n) < bitlen { bitlen = int(n) } else if int(n) >= 64-(16-int(m))*4 { bitlen = 65 } for { i := s.beginGroup(intBitsLabel, false) u := s.drawBits(bitlen) ok := bitlen > 64 || u <= max s.endGroup(i, !ok) if bitlen > 64 { u = max } if u <= max { return u, u == 0 && n == 1, u == max && bitlen >= int(n) } } } func genUintN(s bitStream, max uint64, bias bool) (uint64, bool, bool) { if bias { return genUintNBiased(s, max) } else { return genUintNUnbiased(s, max), false, false } } func genUintRange(s bitStream, min uint64, max uint64, bias bool) (uint64, bool, bool) { if min > max { assertf(false, "invalid range [%v, %v]", min, max) // avoid allocations in the fast path } u, lOverflow, rOverflow := genUintN(s, max-min, bias) return min + u, lOverflow, rOverflow } func genIntRange(s bitStream, min int64, max int64, bias bool) (int64, bool, bool) { if min > max { assertf(false, "invalid range [%v, %v]", min, max) // avoid allocations in the fast path } var posMin, negMin uint64 var pNeg float64 if min >= 0 { posMin = uint64(min) pNeg = 0 } else if max <= 0 { negMin = uint64(-max) pNeg = 1 } else { posMin = 0 negMin = 1 pos := uint64(max) + 1 neg := uint64(-min) pNeg = float64(neg) / (float64(neg) + float64(pos)) if bias { pNeg = 0.5 } } if flipBiasedCoin(s, pNeg) { u, lOverflow, rOverflow := genUintRange(s, negMin, uint64(-min), bias) return -int64(u), rOverflow, lOverflow && max <= 0 } else { u, lOverflow, rOverflow := genUintRange(s, posMin, uint64(max), bias) return int64(u), lOverflow && min >= 0, rOverflow } } func genIndex(s bitStream, n int, bias bool) int { assert(n > 0) u, _, _ := genUintN(s, uint64(n-1), bias) return int(u) } func flipBiasedCoin(s bitStream, p float64) bool { assert(p >= 0 && p <= 1) i := s.beginGroup(coinFlipLabel, false) f := genFloat01(s) s.endGroup(i, false) return f >= 1-p } type loadedDie struct { table []int } func newLoadedDie(weights []int) *loadedDie { assert(len(weights) > 0) if len(weights) == 1 { return &loadedDie{ table: []int{0}, } } total := 0 for _, w := range weights { assert(w > 0 && w < 100) total += w } table := make([]int, total) i := 0 for n, w := range weights { for j := i; i < j+w; i++ { table[i] = n } } return &loadedDie{ table: table, } } func (d *loadedDie) roll(s bitStream) int { i := s.beginGroup(dieRollLabel, false) ix := genIndex(s, len(d.table), false) s.endGroup(i, false) return d.table[ix] } type repeat struct { minCount int maxCount int avgCount float64 pContinue float64 count int group int rejected bool rejections int forceStop bool label string } func newRepeat(minCount int, maxCount int, avgCount float64, label string) *repeat { if minCount < 0 { minCount = 0 } if maxCount < 0 { maxCount = math.MaxInt } if avgCount < 0 { avgCount = float64(minCount) + math.Min(math.Max(float64(minCount), small), (float64(maxCount)-float64(minCount))/2) } return &repeat{ minCount: minCount, maxCount: maxCount, avgCount: avgCount, pContinue: 1 - 1/(1+avgCount-float64(minCount)), // TODO was no -minCount intentional? group: -1, label: label + repeatLabel, } } func (r *repeat) avg() int { return int(math.Ceil(r.avgCount)) } func (r *repeat) more(s bitStream) bool { if r.group >= 0 { s.endGroup(r.group, r.rejected) } r.group = s.beginGroup(r.label, true) r.rejected = false pCont := r.pContinue if r.count < r.minCount { pCont = 1 } else if r.forceStop || r.count >= r.maxCount { pCont = 0 } cont := flipBiasedCoin(s, pCont) if cont { r.count++ } else { s.endGroup(r.group, false) } return cont } func (r *repeat) reject() { assert(r.count > 0) r.count-- r.rejected = true r.rejections++ if r.rejections > r.count*2 { if r.count >= r.minCount { r.forceStop = true } else { panic(invalidData("too many rejections in repeat")) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������rapid-1.3.0/utils_test.go���������������������������������������������������������������������������0000664�0000000�0000000�00000014675�15162516440�0015361�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "flag" "fmt" "math" "math/bits" "sort" "strconv" "strings" "testing" ) var flaky = flag.Bool("flaky", false, "run flaky tests") func createRandomBitStream(t *testing.T) bitStream { t.Helper() seed := baseSeed() t.Logf("random seed %v", seed) return newRandomBitStream(seed, false) } func TestGenFloat01(t *testing.T) { t.Parallel() s1 := &bufBitStream{buf: []uint64{0}} f1 := genFloat01(s1) if f1 != 0 { t.Errorf("got %v instead of 0", f1) } s2 := &bufBitStream{buf: []uint64{math.MaxUint64}} f2 := genFloat01(s2) if f2 == 1 { t.Errorf("got impossible 1") } } func TestGenGeom(t *testing.T) { t.Parallel() s1 := &bufBitStream{buf: []uint64{0}} i1 := genGeom(s1, 0.1) if i1 != 0 { t.Errorf("got %v instead of 0 for 0.1", i1) } s2 := &bufBitStream{buf: []uint64{0}} i2 := genGeom(s2, 1) if i2 != 0 { t.Errorf("got %v instead of 0 for 1", i2) } } func TestGenGeomMean(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := newRandomBitStream(baseSeed(), false) for i := 0; i < 100; i++ { t.Run(strconv.Itoa(i), func(t *testing.T) { p := genFloat01(s) var geoms []uint64 for i := 0; i < 10000; i++ { geoms = append(geoms, genGeom(s, p)) } avg := 0.0 for _, f := range geoms { avg += float64(f) } avg /= float64(len(geoms)) mean := (1 - p) / p if math.Abs(avg-mean) > 0.5 { // true science t.Fatalf("for p=%v geom avg=%v vs expected mean=%v", p, avg, mean) } }) } } func TestUintsExamplesHist(t *testing.T) { s := newRandomBitStream(baseSeed(), false) for _, n := range []int{2, 3, 4, 5, 6, 8, 16, 32, 64} { t.Run(strconv.Itoa(n), func(t *testing.T) { var lines []string for i := 0; i < 50; i++ { n, _, _ := genUintN(s, bitmask64(uint(n)), true) b := bits.Len64(n) l := fmt.Sprintf("% 24d %s % 3d", n, strings.Repeat("*", b)+strings.Repeat(" ", 64-b), b) lines = append(lines, l) } sort.Strings(lines) t.Log("\n" + strings.Join(lines, "\n")) }) } } func ensureWithin3Sigma(t *testing.T, ctx any, y int, n int, p float64) { t.Helper() mu := float64(n) * p s := math.Sqrt(float64(n) * p * (1 - p)) if float64(y) < mu-3*s || float64(y) > mu+3*s { if ctx != nil { t.Errorf("for %v: got %v out of %v (p %v, mu %v, stddev %v)", ctx, y, n, p, mu, s) } else { t.Errorf("got %v out of %v (p %v, mu %v, stddev %v)", y, n, p, mu, s) } } } func TestGenUintN(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := createRandomBitStream(t) max := []uint64{0, 1, 2, 5, 13} for _, m := range max { r := make([]int, m+1) n := 1000 for i := 0; i < n; i++ { u, _, _ := genUintN(s, m, false) r[u]++ } for u := range r { ensureWithin3Sigma(t, m, r[u], n, 1/float64(m+1)) } } } func TestGenUintRange(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := createRandomBitStream(t) ranges := [][]uint64{ {0, 0}, {0, 1}, {0, 2}, {1, 1}, {1, 3}, {3, 7}, {math.MaxUint64 - 3, math.MaxUint64}, {math.MaxUint64 - 1, math.MaxUint64}, {math.MaxUint64, math.MaxUint64}, } for _, r := range ranges { m := map[uint64]int{} n := 1000 for i := 0; i < n; i++ { u, _, _ := genUintRange(s, r[0], r[1], false) m[u]++ } for u := range m { if u < r[0] || u > r[1] { t.Errorf("%v out of range [%v, %v]", u, r[0], r[1]) } ensureWithin3Sigma(t, fmt.Sprintf("%v from %v", u, r), m[u], n, 1/float64(r[1]-r[0]+1)) } } } func TestGenIntRange(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := createRandomBitStream(t) ranges := [][]int64{ {0, 0}, {0, 1}, {0, 2}, {1, 1}, {1, 3}, {3, 7}, {math.MaxInt64 - 3, math.MaxInt64}, {math.MaxInt64 - 1, math.MaxInt64}, {math.MaxInt64, math.MaxInt64}, {-1, -1}, {-2, -1}, {-3, 0}, {-1, 1}, {-1, 3}, {-3, 7}, {-7, -3}, {math.MinInt64, math.MinInt64 + 3}, {math.MinInt64, math.MinInt64 + 1}, {math.MinInt64, math.MinInt64}, } for _, r := range ranges { m := map[int64]int{} n := 1000 for i := 0; i < n; i++ { u, _, _ := genIntRange(s, r[0], r[1], false) m[u]++ } for u := range m { if u < r[0] || u > r[1] { t.Errorf("%v out of range [%v, %v]", u, r[0], r[1]) } ensureWithin3Sigma(t, fmt.Sprintf("%v from %v", u, r), m[u], n, 1/float64(r[1]-r[0]+1)) } } } func TestFlipBiasedCoin(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := createRandomBitStream(t) ps := []float64{0, 0.3, 0.5, 0.7, 1} for _, p := range ps { n := 1000 y := 0 for i := 0; i < n; i++ { if flipBiasedCoin(s, p) { y++ } } ensureWithin3Sigma(t, p, y, n, p) } } func TestLoadedDie(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := createRandomBitStream(t) weights := [][]int{ {1}, {1, 2}, {3, 2, 1}, {1, 2, 4, 2, 1}, } for _, ws := range weights { d := newLoadedDie(ws) n := 1000 r := make([]int, len(ws)) for i := 0; i < n; i++ { r[d.roll(s)]++ } total := 0 for _, w := range ws { total += w } for i, w := range ws { ensureWithin3Sigma(t, ws, r[i], n, float64(w)/float64(total)) } } } func TestRepeat(t *testing.T) { t.Parallel() if !*flaky { t.Skip("flaky") } s := createRandomBitStream(t) mmas := [][3]int{ {0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}, {3, 3, 3}, {3, 7, 3}, {3, 7, 5}, {3, 7, 7}, {0, 10, 5}, {1, 10, 6}, {0, 50, 5}, {1, 50, 6}, {1000, math.MaxInt32, 1000 + 1}, {1000, math.MaxInt32, 1000 + 2}, {1000, math.MaxInt32, 1000 + 7}, {1000, math.MaxInt32, 1000 + 13}, {1000, math.MaxInt32, 1000 + 100}, {1000, math.MaxInt32, 1000 + 1000}, } for _, mma := range mmas { min, max, avg := mma[0], mma[1], mma[2] n := 5000 c := make([]int, n) for i := 0; i < n; i++ { r := newRepeat(min, max, float64(avg), "") for r.more(s) { c[i]++ } if c[i] < min || c[i] > max { t.Errorf("got %v tries with bounds [%v, %v]", c[i], min, max) } } if min == 1000 && max == math.MaxInt32 { mu := float64(0) for _, e := range c { mu += float64(e) } mu /= float64(len(c)) diff := math.Abs(mu - float64(avg)) if diff > 0.5 { // true science t.Errorf("real avg %v vs desired %v, diff %v (%v tries)", mu, avg, diff, n) } } } } �������������������������������������������������������������������rapid-1.3.0/vis.go����������������������������������������������������������������������������������0000664�0000000�0000000�00000027102�15162516440�0013750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "bytes" "encoding/base64" "fmt" "html/template" "image" "image/color" "image/png" "io" "math" "time" ) var ( visBitSetColor = color.Black visBitUnsetColor = color.White visTmpl = template.Must(template.New("rapid-vis").Parse(visHTML)) ) type visTmplData struct { Title string Images [][]*visTmplImage VisCSS template.CSS RebootCSS template.CSS } type visGroupInfo struct { Classes string Label string } type visTmplImage struct { Base64 string Title string Alt string Width int Height int GroupBegins []visGroupInfo GroupEnds []visGroupInfo } func visWriteHTML(w io.Writer, title string, recData []recordedBits) error { d := &visTmplData{ Title: fmt.Sprintf("%v (%v)", title, time.Now().Format(time.RFC1123)), VisCSS: template.CSS(visCSS), RebootCSS: template.CSS(visRebootCSS), } labelClasses := map[string]string{} lastLabelClass := 0 for _, rd := range recData { var images []*visTmplImage for _, u := range rd.data { tmplImg, err := visNewUint64Image(u).toTmplImage() if err != nil { return err } images = append(images, tmplImg) } for _, group := range rd.groups { if _, ok := labelClasses[group.label]; !ok { labelClasses[group.label] = fmt.Sprintf("label-%v", lastLabelClass) lastLabelClass++ } } for _, group := range rd.groups { images[group.begin].GroupBegins = append(images[group.begin].GroupBegins, visGroupToInfo(labelClasses, group)) if group.end > 0 { images[group.end-1].GroupEnds = append(images[group.end-1].GroupEnds, visGroupToInfo(labelClasses, group)) } } d.Images = append(d.Images, images) } return visTmpl.Execute(w, d) } func visGroupToInfo(labelClasses map[string]string, group groupInfo) visGroupInfo { discardClass := "" if group.discard { discardClass = "discard " } endlessClass := "" if group.end <= 0 { endlessClass = "endless " } return visGroupInfo{ Classes: discardClass + endlessClass + labelClasses[group.label], Label: group.label, } } type visUint64Image struct { u uint64 p color.Palette } func visNewUint64Image(u uint64) *visUint64Image { s := newRandomBitStream(u, false) h1 := genFloat01(s) h2 := genFloat01(s) return &visUint64Image{ u: u, p: color.Palette{visBitSetColor, visBitUnsetColor, visHsv(h1*360, 1, 1), visHsv(h2*360, 1, 1)}, } } func (img *visUint64Image) ColorModel() color.Model { return img.p } func (img *visUint64Image) Bounds() image.Rectangle { return image.Rect(0, 0, 64, 2) } func (img *visUint64Image) ColorIndexAt(x, y int) uint8 { switch y { case 0: if (x/8)%2 == 0 { return 2 } return 3 default: if img.u&(1<<uint(63-x)) != 0 { return 0 } return 1 } } func (img *visUint64Image) At(x, y int) color.Color { return img.p[img.ColorIndexAt(x, y)] } func (img *visUint64Image) toTmplImage() (*visTmplImage, error) { enc := png.Encoder{ CompressionLevel: png.BestCompression, } var buf bytes.Buffer err := enc.Encode(&buf, img) if err != nil { return nil, err } return &visTmplImage{ Base64: base64.StdEncoding.EncodeToString(buf.Bytes()), Title: fmt.Sprintf("0x%x / %d", img.u, img.u), Alt: fmt.Sprintf("0x%x", img.u), Width: 64, Height: 2, }, nil } const visHTML = `<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="description" content="rapid debug data visualization"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>[rapid] {{.Title}}

{{.Title}}

{{range .Images -}}
{{range . -}} {{range .GroupBegins}}{{.Label}}{{end}} {{.Alt}} {{range .GroupEnds}}{{end}} {{end}}
{{end}} ` const visCSS = ` body { margin: 1rem; } .vis { display: flex; margin-bottom: 1rem; } .vis img { height: 1rem; margin: 0.3rem; image-rendering: pixelated; image-rendering: crisp-edges; image-rendering: -moz-crisp-edges; -ms-interpolation-mode: nearest-neighbor; } .vis .group { position: relative; display: inline-flex; padding: 0 0 1rem; background-color: rgba(0, 0, 0, 0.05); border-radius: 0 0 0.5rem 0.5rem; border-bottom: 2px solid black; } .vis .group.discard { background-color: rgba(255, 0, 0, 0.2); } .vis .group.endless { background-color: white; } .vis .group .label { font-size: 80%; white-space: nowrap; position: absolute; bottom: 0; left: 0.3rem; max-width: 100%; overflow: hidden; } .vis .group.discard > .label { color: red; } .vis .group.label-0 { border-bottom-color: red; } .vis .group.label-1 { border-bottom-color: maroon; } .vis .group.label-2 { border-bottom-color: yellow; } .vis .group.label-3 { border-bottom-color: olive; } .vis .group.label-4 { border-bottom-color: lime; } .vis .group.label-5 { border-bottom-color: green; } .vis .group.label-6 { border-bottom-color: aqua; } .vis .group.label-7 { border-bottom-color: teal; } .vis .group.label-8 { border-bottom-color: blue; } .vis .group.label-9 { border-bottom-color: navy; } .vis .group.label-10 { border-bottom-color: fuchsia; } .vis .group.label-11 { border-bottom-color: purple; } .vis .group.label-12 { border-bottom-color: red; } .vis .group.label-13 { border-bottom-color: maroon; } .vis .group.label-14 { border-bottom-color: yellow; } .vis .group.label-15 { border-bottom-color: olive; } .vis .group.label-16 { border-bottom-color: lime; } .vis .group.label-17 { border-bottom-color: green; } .vis .group.label-18 { border-bottom-color: aqua; } .vis .group.label-19 { border-bottom-color: teal; } .vis .group.label-20 { border-bottom-color: blue; } .vis .group.label-21 { border-bottom-color: navy; } .vis .group.label-22 { border-bottom-color: fuchsia; } .vis .group.label-23 { border-bottom-color: purple; } ` const visRebootCSS = ` /*! * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/) * Copyright 2011-2018 The Bootstrap Authors * Copyright 2011-2018 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) */ *, *::before, *::after { box-sizing: border-box; } html { font-family: sans-serif; line-height: 1.15; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -ms-overflow-style: scrollbar; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } @-ms-viewport { width: device-width; } article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { display: block; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #212529; text-align: left; background-color: #fff; } [tabindex="-1"]:focus { outline: 0 !important; } hr { box-sizing: content-box; height: 0; overflow: visible; } h1, h2, h3, h4, h5, h6 { margin-top: 0; margin-bottom: 0.5rem; } p { margin-top: 0; margin-bottom: 1rem; } abbr[title], abbr[data-original-title] { text-decoration: underline; -webkit-text-decoration: underline dotted; text-decoration: underline dotted; cursor: help; border-bottom: 0; } address { margin-bottom: 1rem; font-style: normal; line-height: inherit; } ol, ul, dl { margin-top: 0; margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { margin-bottom: 0; } dt { font-weight: 700; } dd { margin-bottom: .5rem; margin-left: 0; } blockquote { margin: 0 0 1rem; } dfn { font-style: italic; } b, strong { font-weight: bolder; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sub { bottom: -.25em; } sup { top: -.5em; } a { color: #007bff; text-decoration: none; background-color: transparent; } a:hover { color: #0056b3; text-decoration: underline; } a:not([href]):not([tabindex]) { color: inherit; text-decoration: none; } a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { color: inherit; text-decoration: none; } a:not([href]):not([tabindex]):focus { outline: 0; } pre, code, kbd, samp { font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 1em; } pre { margin-top: 0; margin-bottom: 1rem; overflow: auto; -ms-overflow-style: scrollbar; } figure { margin: 0 0 1rem; } img { vertical-align: middle; border-style: none; } svg { overflow: hidden; vertical-align: middle; } table { border-collapse: collapse; } caption { padding-top: 0.75rem; padding-bottom: 0.75rem; color: #6c757d; text-align: left; caption-side: bottom; } th { text-align: inherit; } label { display: inline-block; margin-bottom: 0.5rem; } button { border-radius: 0; } button:focus { outline: 1px dotted; outline: 5px auto -webkit-focus-ring-color; } input, button, select, optgroup, textarea { margin: 0; font-family: inherit; font-size: inherit; line-height: inherit; } button, input { overflow: visible; } button, select { text-transform: none; } button, html [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { padding: 0; border-style: none; } input[type="radio"], input[type="checkbox"] { box-sizing: border-box; padding: 0; } input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { -webkit-appearance: listbox; } textarea { overflow: auto; resize: vertical; } fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; max-width: 100%; padding: 0; margin-bottom: .5rem; font-size: 1.5rem; line-height: inherit; color: inherit; white-space: normal; } progress { vertical-align: baseline; } [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; } [type="search"] { outline-offset: -2px; -webkit-appearance: none; } [type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-file-upload-button { font: inherit; -webkit-appearance: button; } output { display: inline-block; } summary { display: list-item; cursor: pointer; } template { display: none; } [hidden] { display: none !important; } ` // Copyright 2013 Lucas Beyer // Licensed under MIT (https://github.com/lucasb-eyer/go-colorful/blob/master/LICENSE) // // Hsv creates a new Color given a Hue in [0..360], a Saturation and a Value in [0..1] func visHsv(H, S, V float64) color.RGBA { Hp := H / 60.0 C := V * S X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0)) m := V - C r, g, b := 0.0, 0.0, 0.0 switch { case 0.0 <= Hp && Hp < 1.0: r = C g = X case 1.0 <= Hp && Hp < 2.0: r = X g = C case 2.0 <= Hp && Hp < 3.0: g = C b = X case 3.0 <= Hp && Hp < 4.0: g = X b = C case 4.0 <= Hp && Hp < 5.0: r = X b = C case 5.0 <= Hp && Hp < 6.0: r = C b = X } return color.RGBA{R: uint8((m + r) * 255), G: uint8((m + g) * 255), B: uint8((m + b) * 255), A: 255} } rapid-1.3.0/vis_test.go000066400000000000000000000031471516251644000150120ustar00rootroot00000000000000// Copyright 2019 Gregory Petrosyan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import ( "math" "os" "testing" ) func TestDataVis(t *testing.T) { t.Parallel() f, err := os.Create("vis-test.html") if err != nil { t.Fatalf("failed to create vis html file: %v", err) } defer func() { _ = f.Close() }() data := []uint64{ 0, 0x55, 0xaa, math.MaxUint8, 0x5555, 0xaaaa, math.MaxUint16, 0x55555555, 0xaaaaaaaa, math.MaxUint32, 0x5555555555555555, 0xaaaaaaaaaaaaaaaa, math.MaxUint64, } groups := []groupInfo{ {begin: 0, end: 13, label: ""}, {begin: 1, end: 1 + 3, label: "8-bit"}, {begin: 3, end: 4, label: "0xff", discard: true}, {begin: 4, end: 4 + 3, label: "16-bit"}, {begin: 7, end: 13, label: "big integers"}, {begin: 7, end: 7 + 3, label: "32-bit"}, {begin: 10, end: 10 + 3, label: "64-bit"}, } rd := []recordedBits{ {data: data, groups: groups}, } g := SliceOf(SliceOf(Uint().Filter(func(i uint) bool { return i%2 == 1 }))).Filter(func(s [][]uint) bool { return len(s) > 0 }) for { s := newRandomBitStream(baseSeed(), true) _, err := recoverValue(g, newT(nil, s, false, nil)) if err != nil && !err.isInvalidData() { t.Errorf("unexpected error %v", err) } rd = append(rd, recordedBits{data: s.data, groups: s.groups}) if err == nil { break } } err = visWriteHTML(f, "test", rd) if err != nil { t.Errorf("visWriteHTML error: %v", err) } }