pax_global_header 0000666 0000000 0000000 00000000064 14151735222 0014514 g ustar 00root root 0000000 0000000 52 comment=50f816191dfa6185d8b72b5c7fd84fc56241d359
clojure-test.check-b2ec872/ 0000775 0000000 0000000 00000000000 14151735222 0015626 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/.gitignore 0000664 0000000 0000000 00000000273 14151735222 0017620 0 ustar 00root root 0000000 0000000 /target
/lib
/classes
/checkouts
pom.xml.asc
*.jar
*.class
.lein-deps-sum
.lein-failures
.lein-plugins
.lein-repl-history
.nrepl-port
/bin/
/.classpath
/.project
/.settings/
.idea/
*.iml
clojure-test.check-b2ec872/CHANGELOG.markdown 0000664 0000000 0000000 00000024501 14151735222 0020663 0 ustar 00root root 0000000 0000000 # Changelog
## 1.1.0 (2020-07-10)
* TCHECK-155 - don't generate :/ for keywords
## 1.0.0 (2020-02-18)
* No changes
## 0.10.0 (2019-06-30 as RC1, 2019-08-11 as final)
* Docstring improvements
* Deprecated five small integer generators
* `gen/int`
* `gen/pos-int`
* `gen/neg-int`
* `gen/s-pos-int`
* `gen/s-neg-int`
Added a `gen/small-integer` to replace `gen/int`, and the
docstrings for all the deprecated generators suggest alternatives
* Added `gen/size-bounded-bigint` and `gen/big-ratio`, both jvm-only
* Added `*-equatable` variants of `gen/simple-type`,
`gen/simple-type-printable`, `gen/any`, and `gen/any-printable`;
the only current difference is that the new generators never generate
a `NaN`, and so they should always be `=` to equivalent objects
## 0.10.0-alpha4 (2019-03-10)
* Automatically require cljs macros so users don't have to
([TCHECK-154](http://dev.clojure.org/jira/browse/TCHECK-154))
## 0.10.0-alpha3 (2018-05-27)
* Improve failure reporting
([TCHECK-34](http://dev.clojure.org/jira/browse/TCHECK-34))
* `gen/frequency` doesn't shrink to zero-weighted entries
([TCHECK-129](http://dev.clojure.org/jira/browse/TCHECK-129))
* Faster PRNG in clojurescript
* General symbol/keyword generator improvements (shorter on average,
keyword generator includes colons and can generate `:/`)
* passing test reporting is optional
([TCHECK-116](http://dev.clojure.org/jira/browse/TCHECK-116))
* Doesn't crash when some other plugin redefines `clojure.test/report`
([TCHECK-125](http://dev.clojure.org/jira/browse/TCHECK-125))
* test names used more reliably in certain reportings
([TCHECK-124](http://dev.clojure.org/jira/browse/TCHECK-124))
* Removed the map-style bindings in `gen/let` introduced in `alpha1`
([TCHECK-133](http://dev.clojure.org/jira/browse/TCHECK-133))
* Changed some of the key names in the `reporter-fn` calls to more
closely match the data returned from `quick-check`, to minimize
confusion
* Clarified meaning of bindings in the `for-all` docstring
([TCHECK-121](http://dev.clojure.org/jira/browse/TCHECK-121))
* Updated the `:result` key to be more backwards compatible, added a
`:pass?` key, renamed `results/passing?` to `results/pass?`
([TCHECK-142](http://dev.clojure.org/jira/browse/TCHECK-142))
* Added timing keys to the `quick-check` return data
([TCHECK-95](http://dev.clojure.org/jira/browse/TCHECK-95))
## 0.10.0-alpha2 (2017-06-27)
* Added a 3rd optional argument to `gen/generate`, the `seed`
* Reverted behavioral change in `prop/for-all` so that returned
exceptions are treated as thrown exceptions
([TCHECK-131](http://dev.clojure.org/jira/browse/TCHECK-131))
## 0.10.0-alpha1 (2017-06-07)
* Major changes
* Adds a `:reporter-fn` callback for the `quick-check` function,
fixing [TCHECK-33](http://dev.clojure.org/jira/browse/TCHECK-33)
(this item is the most subject to change before the final release)
* Rewrote `recursive-gen` to be more careful about sizing
([TCHECK-83](http://dev.clojure.org/jira/browse/TCHECK-83))
* A new protocol clojure.test.check.results/Result that gives a standard
way for a test to return metadata to the test runner
* Minor changes
* Generated keywords and symbols are now smaller, on average
* `gen/any` and `gen/any-printable` can generate sets now
* Collections shrink faster now
* Created `clojure.test.check.clojure-test/*default-opts*`
* `gen/frequency` can shrink to earlier generators
([TCHECK-114](http://dev.clojure.org/jira/browse/TCHECK-114))
* `gen/such-that` can take an `ex-fn` option to customize exceptions
* `gen/let` supports map bindings to specify independence
([TCHECK-98](http://dev.clojure.org/jira/browse/TCHECK-98))
* Internal tweaks for compatibility with self-hosted cljs
([TCHECK-105](http://dev.clojure.org/jira/browse/TCHECK-105))
* `gen/sample` uses size up to `200` instead of `100`
* An assortment of internal changes
## 0.9.0 (2015-11-12)
0.9.0 contains an assortment of new generators, and is the first
release that requires Clojure 1.7.0 (due to using `cljc` files to unify
the clj & cljs code).
* `gen/let`
* `gen/uuid`
* `gen/double`
* `gen/large-integer`
* `gen/set`
* `gen/sorted-set`
* `gen/vector-distinct`
* `gen/vector-distinct-by`
* `gen/list-distinct`
* `gen/list-distinct-by`
* `gen/map` now takes sizing options, the same as the preceding
collection generators
## 0.8.2
* Bugfix for [TCHECK-77](http://dev.clojure.org/jira/browse/TCHECK-77),
which was a regression in the precision of `gen/choose` introduced in
0.8.1.
## 0.8.1
* Bugfix for [TCHECK-73](http://dev.clojure.org/jira/browse/TCHECK-73),
in which `gen/int` would sometimes generate doubles.
## 0.8.0
* **Breaking ClojureScript Change**:
The namespace names have changed:
- `cljs.*` → `clojure.*`
- `cljs.test.check.cljs-test` → `clojure.test.check.clojure-test`
* Randomness is now provided by a port of the
`java.util.SplittableRandom` algorithm, instead of
`java.util.Random` and `goog.testing.PsuedoRandom`.
* New functions in `clojure.test.check.generators`:
* `scale`
* `generate`
## 0.7.0
* Add ClojureScript support, written by @swannodette. More usage can be
found [in the
README](https://github.com/clojure/test.check#clojurescript).
* Raise an error if the incorrect arity of `defspec` is used.
* Don't export the following private functions:
* `make-rng`
* `not-falsey-or-exception?`
* `make-gen`
* `bind-helper`
* `recursive-helper`
* `binding-vars`
* `binding-gens`
## 0.6.2
* Fix regression where floating point numbers weren't allowed to describe
the number of tests in the defspec macro. Ex: (defspec foo 1e5 ...) now
works again.
* Allow `gen/shuffle` to work on anything that can be turned into a
sequence.
* Allow for testing to be cancelled inside the REPL with ctrl-c.
* Fix StackOverflow error that would be caused when generating vector with
more than about 10k elements.
## 0.6.1
* Fix bug introduced in 0.6.0: The `defspec` macro could only accept map or
numeric _literals_ as options, instead of a symbol.
## 0.6.0
* Add a `shuffle` generator, which generates permutations of a given
sequence.
* Rename `alpha-numeric` functions to `alphanumeric`. `char-alpha-numeric`
and `string-alpha-numeric` are now deprecated in favor of
`char-alphanumeric` and `string-alphanumeric`. The deprecated versions
will be removed in a future version of `test.check`.
* Update the `defspec` macro to allow an optional map argument, which
allows for the setting of `:seed`, `:num-tests` and `:max-size`. Examples
below:
```clojure
(defspec run-with-map {:num-tests 1} (prop/for-all* [gen/int] (constantly true)))
(defspec run-with-map {:num-tests 1
:seed 1}
my-prop)
```
* Provide better error-messages for the misuse of generator combinators.
Many of the functions now test that their arguments are of the
appropriate type, and will throw a runtime error otherwise.
* Print test failures that can be copied directly. For example, print the
empty string as `""`, instead of a blank value. This is fixed by using
`prn` instead of `println`.
## 0.5.9
* Better sizing for recursive generators
* Add `gen/recursive-gen` function for writing recursive generators
* Add keyword and symbol generators that may include namespaces
* `gen/keyword-ns`
* `gen/symbol-ns`
## 0.5.8
* Limit the number of retries for gen/such-that. A two-arity version is
provided if you need to retry more than 10 times. This should be a
code-smell, though.
* Return random seed used on test failure
* Fix keyword generator to conform to reader specs
* Correct documentation mentions of namespaces
* Add more detailed contributing instructions
* Internal: use a record internally for generators. This is meant to help
convey the fact that generators are opaque
* Extract rose-tree code into a separate namespace
## 0.5.7
* Rename project to test.check. See README for migrating
from _simple-check_.
## simple-check 0.5.6
* Fix `choose` bug introduced in 0.5.4, the upper-bound was not inclusive.
## simple-check 0.5.5
* Fix botched release
## simple-check 0.5.4
* Fix documentation typos
* Fix defspec default num-tests bug (#52)
* Fix docstring position on `exclude-nth` function (#50)
* Add rose-seq helper function
* Use full Long range in rand-range (#42)
* More useful error-message with `one-of`
* Add `no-shrink` and `shrink-2` combinators
* Fix `interpose-twice-the-length` test (#52)
## simple-check 0.5.3
* All dependencies are now dev-dependencies
* Minor doc typo correction
## simple-check 0.5.2
* Improve shrinking for sequences
* __BACKWARD_INCOMPATIBILITY__: update API for gen/hash-map,
now mirrors closer the clojure.core API
## simple-check 0.5.1
* Remove unused dependency (clj-tuple)
* Add 'any' generator
* Add not-empty generator modifier
* Change one-of to shrink toward earlier generators on failure
## simple-check 0.5.0
* Shrinking will only shrink to values that could have been created by the
generator
* Bugfix with the byte and bytes generator
* Create strings of variable length (instead of always length=size)
* Fix off-by-one error in number of tests reported
* Generate sizes starting at 0, not 1
## simple-check 0.4.1
* When a property fails, add the result of the final shrink to the output
map. This can be found in [:shrunk :result]
* Make pairs respect their size during shrinking (they're just tuples)
* Add a default num-tests to `defspec`
## simple-check 0.4.0
* tuple generator now creates tuple types (from `clj-tuple`)
* __BACKWARD_INCOMPATIBILITY__: `gen/tuple` now takes var-args, instead
of a vector of arguments. So change:
```clojure
(gen/tuple [gen/int gen/boolean])
```
to
```clojure
(gen/tuple gen/int gen/boolean)
```
Tuples will now retain their size when shrunk.
* add a `ratio` generator
* correctly shrink empty lists
* switch to `codox-md` for documentation
## simple-check 0.3.0
* add strictly-positive and strictly-negative integer generators
* allow scientific notation in number of tests parameter
* allow specification of number of elements in vector generator
## simple-check 0.2.1
* remove unused `diff` function
* eliminate reflection warnings
## simple-check 0.2.0
* added `gen/byte` and `gen/bytes`
* swapped order of args in `gen/such-that`
* swapped order of args in `simple-check.clojure-test/defspec`
* add implicit `do` to `defspec` body
## simple-check 0.1.0
* First release
clojure-test.check-b2ec872/CONTRIBUTING.md 0000664 0000000 0000000 00000001164 14151735222 0020061 0 ustar 00root root 0000000 0000000 This is a [Clojure contrib] project.
__We cannot accept pull requests.__
* Issues can be filed in [JIRA]. No [Clojure CA] is required for this.
* Patches/contributions can be attached to issues in [Jira]. You can [read more
about that process](https://clojure.org/community/contributing).
Contributions need a [signed CA (Contributor
Agreement)](https://clojure.org/community/contributing).
Don't hesitate to ask if you have questions about this.
[JIRA]: https://clojure.atlassian.net/browse/TCHECK
[Clojure CA]: https://clojure.org/community/contributing
[Clojure contrib]: https://clojure.org/community/contrib_libs
clojure-test.check-b2ec872/LICENSE 0000664 0000000 0000000 00000026374 14151735222 0016647 0 ustar 00root root 0000000 0000000 Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and documentation
distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and are
distributed by that particular Contributor. A Contribution 'originates'
from a Contributor if it was added to the Program by such Contributor
itself or anyone acting on such Contributor's behalf. Contributions do not
include additions to the Program which: (i) are separate modules of
software distributed in conjunction with the Program under their own
license agreement, and (ii) are not derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.
"Program" means the Contributions distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free copyright license to
reproduce, prepare derivative works of, publicly display, publicly
perform, distribute and sublicense the Contribution of such Contributor,
if any, and such derivative works, in source code and object code form.
b) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free patent license under
Licensed Patents to make, use, sell, offer to sell, import and otherwise
transfer the Contribution of such Contributor, if any, in source code and
object code form. This patent license shall apply to the combination of
the Contribution and the Program if, at the time the Contribution is
added by the Contributor, such addition of the Contribution causes such
combination to be covered by the Licensed Patents. The patent license
shall not apply to any other combinations which include the Contribution.
No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the licenses
to its Contributions set forth herein, no assurances are provided by any
Contributor that the Program does not infringe the patent or other
intellectual property rights of any other entity. Each Contributor
disclaims any liability to Recipient for claims brought by any other
entity based on infringement of intellectual property rights or
otherwise. As a condition to exercising the rights and licenses granted
hereunder, each Recipient hereby assumes sole responsibility to secure
any other intellectual property rights needed, if any. For example, if a
third party patent license is required to allow Recipient to distribute
the Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright
license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under
its own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties
and conditions, express and implied, including warranties or
conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for
damages, including direct, indirect, special, incidental and
consequential damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are
offered by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable
manner on or through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its Contribution,
if
any, in a manner that reasonably allows subsequent Recipients to identify the
originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with
respect to end users, business partners and the like. While this license is
intended to facilitate the commercial use of the Program, the Contributor who
includes the Program in a commercial product offering should do so in a manner
which does not create potential liability for other Contributors. Therefore,
if a Contributor includes the Program in a commercial product offering, such
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
every other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits and
other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such Commercial
Contributor in connection with its distribution of the Program in a commercial
product offering. The obligations in this section do not apply to any claims
or Losses relating to any actual or alleged intellectual property
infringement. In order to qualify, an Indemnified Contributor must:
a) promptly notify the Commercial Contributor in writing of such claim, and
b) allow the Commercial Contributor to control, and cooperate with the
Commercial Contributor in, the defense and any related settlement
negotiations. The Indemnified Contributor may participate in any such claim at
its own expense.
For example, a Contributor might include the Program in a commercial product
offering, Product X. That Contributor is then a Commercial Contributor. If
that Commercial Contributor then makes performance claims, or offers
warranties related to Product X, those performance claims and warranties are
such Commercial Contributor's responsibility alone. Under this section, the
Commercial Contributor would have to defend claims against the other
Contributors related to those performance claims and warranties, and if a
court requires any other Contributor to pay any damages as a result, the
Commercial Contributor must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
Recipient is solely responsible for determining the appropriateness of using
and distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to the
risks and costs of program errors, compliance with applicable laws, damage to
or loss of data, programs or equipment, and unavailability or interruption of
operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of the
remainder of the terms of this Agreement, and without further action by the
parties hereto, such provision shall be reformed to the minimum extent
necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
(excluding combinations of the Program with other software or hardware)
infringes such Recipient's patent(s), then such Recipient's rights granted
under Section 2(b) shall terminate as of the date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipient's rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipient's obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue
and survive.
Everyone is permitted to copy and distribute copies of this Agreement, but in
order to avoid inconsistency the Agreement is copyrighted and may only be
modified in the following manner. The Agreement Steward reserves the right to
publish new versions (including revisions) of this Agreement from time to
time. No one other than the Agreement Steward has the right to modify this
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
Eclipse Foundation may assign the responsibility to serve as the Agreement
Steward to a suitable separate entity. Each new version of the Agreement will
be given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version of the
Agreement is published, Contributor may elect to distribute the Program
(including its Contributions) under the new version. Except as expressly
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
licenses to the intellectual property of any Contributor under this Agreement,
whether expressly, by implication, estoppel or otherwise. All rights in the
Program not expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to this
Agreement will bring a legal action under this Agreement more than one year
after the cause of action arose. Each party waives its rights to a jury trial in
any resulting litigation.
clojure-test.check-b2ec872/Makefile 0000664 0000000 0000000 00000000535 14151735222 0017271 0 ustar 00root root 0000000 0000000 .PHONY: pages docs
##
## Doc targets
##
docs:
lein doc
pages: docs
rm -rf /tmp/org.clojure-test.check-docs
mkdir -p /tmp/org.clojure-test.check-docs
cp -R doc/ /tmp/org.clojure-test.check-docs
git checkout gh-pages
cp -R /tmp/org.clojure-test.check-docs/* .
git add .
git add -u
git commit
git push origin gh-pages
git checkout master
clojure-test.check-b2ec872/README.md 0000664 0000000 0000000 00000011143 14151735222 0017105 0 ustar 00root root 0000000 0000000 # test.check
_test.check_ is a Clojure property-based testing tool inspired by QuickCheck.
The core idea of _test.check_ is that instead of enumerating expected input
and output for unit tests, you write properties about your function that should
hold true for all inputs. This lets you write concise, powerful tests.
* Release Info
* [Latest Releases](#latest-releases)
* [Changelog](CHANGELOG.markdown)
* [Introduction](doc/intro.md)
* Basic Docs
* [API Docs](http://clojure.github.io/test.check/)
* [Cheatsheet](doc/cheatsheet.md)
* [Generator Examples](doc/generator-examples.md)
* [Migrating from SimpleCheck](doc/migrating-from-simple-check.md)
* Useful Libraries
* [test.chuck](https://github.com/gfredericks/test.chuck)
* [collection-check](https://github.com/ztellman/collection-check)
* [herbert](https://github.com/miner/herbert)
* Examples (some of these may refer to simple-check):
* [core.matrix](https://github.com/mikera/core.matrix/blob/c45ee6b551a50a509e668f46a1ae52ade2c52a82/src/test/clojure/clojure/core/matrix/properties.clj)
* [byte-streams](https://github.com/ztellman/byte-streams/blob/b5f50a20c6237ae4e45046f72367ad658090c591/test/byte_streams_simple_check.clj)
* [byte-transforms](https://github.com/ztellman/byte-transforms/blob/c5b9613eebac722447593530531b9aa7976a0592/test/byte_transforms_simple_check.clj)
* [collection-check](https://github.com/ztellman/collection-check)
* Blog posts and videos (some of these may refer to simple-check):
* [Powerful Testing with test.check - Clojure/West 2014](https://www.youtube.com/watch?v=JMhNINPo__g) -- [Slides](https://speakerdeck.com/reiddraper/powerful-testing-with-test-dot-check)
* [Purely Random - Clojure/west 2015](https://www.youtube.com/watch?v=u0t-6lUvXHo)
* [Building test.check Generators - Clojure/Conj 2017](https://www.youtube.com/watch?v=F4VZPxLZUdA) - [Slides](https://gfredericks.com/speaking/2017-10-12-generators.pdf)
* [Check your work - 8th Light](http://blog.8thlight.com/connor-mendenhall/2013/10/31/check-your-work.html)
* [Writing simple-check - Reid Draper](http://reiddraper.com/writing-simple-check/)
* [Generative testing in Clojure - Youtube](https://www.youtube.com/watch?v=u0TkAw8QqrQ)
* Advanced Docs
* [Growth and Shrinking](doc/growth-and-shrinking.md)
* Other Implementations
* [QC for Haskell](http://hackage.haskell.org/package/QuickCheck)
* [The significantly more advanced QC for Erlang](http://www.quviq.com/index.html)
* Papers
* [QuickCheck: A Lightweight Tool for Random Testing of Haskell
Programs](http://www.eecs.northwestern.edu/~robby/courses/395-495-2009-fall/quick.pdf)
* Developer Docs
* [Contributing](CONTRIBUTING.md)
* [Developer Information](doc/development.md)
* [Miscellaneous](#miscellaneous)
## Latest Releases
* Release notes for each version are available in [`CHANGELOG.markdown`](CHANGELOG.markdown)
* Remember that prior to version 0.5.7, _test.check_ was called _simple-check_
* As of version `0.9.0`, test.check requires Clojure >= `1.7.0`
* Please note a [breaking change for ClojureScript](https://github.com/clojure/test.check/blob/master/CHANGELOG.markdown#080)
in the `0.8.*` releases.
### Latest Version
#### [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) dependency information:
```clojure
org.clojure/test.check {:mvn/version "1.1.0"}
```
#### Leiningen
```clojure
[org.clojure/test.check "1.1.0"]
```
#### Maven
```xml
org.clojure
test.check
1.1.0
```
### Stable Version
#### Leiningen
```clojure
[org.clojure/test.check "1.1.0"]
```
#### Maven
```xml
org.clojure
test.check
1.1.0
```
If you'd like to try a SNAPSHOT version, [add the sonatype repository to your
project](https://clojure.org/community/downloads#_using_clojure_and_contrib_snapshot_releases).
## Miscellaneous
### YourKit

YourKit is kindly supporting test.check and other open source projects with its
full-featured Java Profiler. YourKit, LLC is the creator of innovative and
intelligent tools for profiling Java and .NET applications. Take a look at
YourKit's leading software products:
* YourKit Java Profiler and
* YourKit .NET Profiler
### License
Copyright © 2014-2020 Rich Hickey, Reid Draper and contributors
Distributed under the Eclipse Public License, the same as Clojure.
clojure-test.check-b2ec872/deps.edn 0000664 0000000 0000000 00000000050 14151735222 0017244 0 ustar 00root root 0000000 0000000 {:paths ["src/main/clojure"]
:deps {}}
clojure-test.check-b2ec872/doc/ 0000775 0000000 0000000 00000000000 14151735222 0016373 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/doc/api-docs-for-older-versions.md 0000664 0000000 0000000 00000000301 14151735222 0024143 0 ustar 00root root 0000000 0000000 # API Docs for Older Versions
- [0.9.0](https://clojure.github.io/test.check/0.9.0)
- [0.8.2](https://clojure.github.io/test.check/0.8.2)
- [0.6.2](https://clojure.github.io/test.check/0.6.2)
clojure-test.check-b2ec872/doc/cheatsheet.md 0000664 0000000 0000000 00000013663 14151735222 0021043 0 ustar 00root root 0000000 0000000 # test.check cheatsheet
So far this only documents functions in the generators namespace.
## Dev utilities, misc
- `(gen/sample g)` — returns 10 smallish samples from `g`
- `(gen/sample g n)` — generates `n` samples from `g`
- `(gen/generate g)` — generates a single moderately sized value from `g`
- `(gen/generate g size)` — generates a value from `g` with the given
`size` (normally sizes range from 0 to 200)
- `(gen/generator? g)` — checks if `g` is a generator
## Simple Generators
- `(gen/return x)` — A constant generator that always generates `x`
- `gen/boolean` — generates booleans (`true` and `false`)
- `gen/uuid` — generates uniformly random UUIDs, does not shrink
- `(gen/elements coll)` — generates elements from `coll` (which must be non-empty)
- `(gen/shuffle coll)` — generates vectors with the elements of `coll`
in random orders
- `gen/any` — generates any clojure value
- `gen/any-printable` — generates any printable clojure value
- `gen/any-equatable` — generates any clojure value that can be equal to another
- `gen/any-printable-equatable` — generates any printable clojure value that can be equal to another
- `gen/simple-type` — like `gen/any` but does not generate collections
- `gen/simple-type-printable` — like `gen/any-printable` but does not
- `gen/simple-type-equatable` — like `gen/any-equatable` but does not generate collections
- `gen/simple-type-printable-equatable` — like `gen/any-printable-equatable` but does not
generate collections
### Numbers
- `gen/nat` — generates small non-negative integers (useful for generating sizes of things)
- `gen/small-integer` — generates small integers, like `gen/nat` but also negative
- `gen/large-integer` — generates a large range of integers
- variant with options: `(gen/large-integer* {:min x, :max y})`
- `gen/size-bounded-bigint` — generates bigints, up to `2^(6*size)`
- `gen/double` — generates a large range of doubles (w/ infinities & `NaN`)
- variant with options: `(gen/double* {:min x, :max y, :infinite? true, :NaN? true})`
- `gen/ratio` — generates ratios (sometimes integers) using gen/small-integer
- `gen/big-ratio` — generates ratios (sometimes integers) using gen/size-bounded-bigint
- `gen/byte` — generates a `Byte`
- `gen/choose` — generates *uniformly distributed* integers between two (inclusive) values
### Characters & Strings & Things
- `gen/char` — generates characters
- `gen/char-ascii` — generates printable ASCII characters
- `gen/char-alphanumeric` — generates alphanumeric ASCII characters
- `gen/char-alpha` — generates alphabetic ASCII characters
- `gen/string` — generates a string
- `gen/string-ascii` — generates a string using `gen/char-ascii`
- `gen/string-alphanumeric` — generates a string using `gen/char-alphanumeric`
- `gen/keyword` — generates keywords
- `gen/keyword-ns` — generates namespaced keywords
- `gen/symbol` — generates symbols
- `gen/symbol-ns` — generates namespaced symbols
## Heterogeneous Collections
- `(gen/tuple g1 g2 ...)` — generates vectors `[x1 x2 ...]` where `x1`
is drawn from `g1`, `x2` from `g2`, etc.
- `(gen/hash-map k1 g1, k2 g2, ...)` — generates maps `{k1 v1, k2 v2, ...}`
where `v1` is drawn from `g1`, `v2` from `g2`, etc.
## Homogeneous Collections
- `(gen/vector g)` — generates vectors of elements from `g`
- Variants:
- `(gen/vector g num-elements)`
- `(gen/vector g min-elements max-elements)`
- `(gen/list g)` — generates lists of elements from `g`
- `(gen/set g)` — generates sets of elements from `g`
- Variants:
- `(gen/set g {:num-elements x, :max-tries 20})`
- `(gen/set g {:min-elements x, :max-elements y, :max-tries 20})`
- `(gen/map key-gen val-gen)` — generates a map with keys from `key-gen`
and vals from `val-gen`
- same opts as `gen/set`u
- `(gen/sorted-set g)` — just like `gen/set`, but generates sorted-sets
- `(gen/vector-distinct g)` — same signature as `gen/set`, but generates
vectors of distinct elements
- `(gen/list-distinct g)` — same signature as `gen/set`, but generates
lists of distinct elements
- `(gen/vector-distinct-by key-fn g)` — generates vectors of elements
where `(apply distinct? (map key-fn the-vector))`
- same opts as `gen/set`
- `(gen/list-distinct-by key-fn g)` — generates list of elements
where `(apply distinct? (map key-fn the-list))`
- same opts as `gen/set`
- `gen/bytes` — generates a byte array
## Combinators
- `(gen/let [x g] y)` — **macro**, like `clojure.core/let`, where
the right-hand bindings are generators and the left-hand are
generated values; creates a generator
- same functionality as `gen/fmap` and `gen/bind`
- `(gen/fmap f g)` — creates a generator that generates `(f x)` for
`x` generated from `g`
- `(gen/bind g f)` — similar to `gen/fmap`, but where `(f x)` is itself
a generator and `(gen/bind g f)` generates values from `(f x)`
- `(gen/such-that pred g)` — returns a new generator that generates
only elements from `g` that match `pred`
- Variants: `(gen/such-that pred g max-tries)`
- `(gen/one-of [g1 g2 ...])` — generates elements from the given
generators, picking generators at random
- `(gen/frequency [[2 g1] [7 g2] ...])` — generates elements from the
given generators, using the given weights to determine the
probability of picking any particular generator
- `(gen/not-empty g)` — given a generator that generates collections,
returns a modified generator that never generates empty collections
- `(gen/recursive-gen container-gen scalar-gen)` — generates a tree of
values, using `container-gen` (which is a function like `gen/list`
which takes and returns a generator) and `scalar-gen` (a generator
for the leaf values)
## Sizing & shrinking control
- `(gen/resize n g)` — creates a variant of `g` whose `size` parameter
is always `n`
- `(gen/scale f g)` — creates a variant of `g` whose `size` parameter
is `(f size)`
- `(gen/no-shrink g)` — creates a variant of `g` that does not shrink
clojure-test.check-b2ec872/doc/development.md 0000664 0000000 0000000 00000001707 14151735222 0021244 0 ustar 00root root 0000000 0000000 # Development
_(i.e., working on test.check itself)_
## Links
* [GitHub project](https://github.com/clojure/test.check)
* [Bug Tracker](https://clojure.atlassian.net/browse/TCHECK)
* [Continuous Integration](https://build.clojure.org/job/test.check/)
* [Compatibility Test Matrix](https://build.clojure.org/job/test.check-test-matrix/)
## Tests
test.check runs in both jvm-clojure and clojurescript, so testing
comprehensively requires several steps:
* Run `lein test` to run the JVM tests (requires [Leiningen](https://leiningen.org))
* Run `lein cljsbuild once` to run the ClojureScript tests (also requires [node.js](https://nodejs.org))
* To run the same tests in a web browser, open (after running the above command)
`test-runners/run_tests_dev.html` and `test-runners/run_tests_adv.html` and watch the
javascript console for output
* Run `script/test-self-host` to run the self-hosted ClojureScript tests (also requires [node.js](https://nodejs.org))
clojure-test.check-b2ec872/doc/generator-examples.md 0000664 0000000 0000000 00000007176 14151735222 0022532 0 ustar 00root root 0000000 0000000 # Generator Examples
The following examples assume you have the following namespace alias:
```clojure
(require '[clojure.test.check.generators :as gen])
```
For the most part, these are in order of simplest to most complex. They also
skip over some of the built-in, basic generators.
## Integers 5 through 9, inclusive
```clojure
(def five-through-nine (gen/choose 5 9))
(gen/sample five-through-nine)
;; => (6 5 9 5 7 7 6 9 7 9)
```
## A random element from a vector
```clojure
(def languages (gen/elements ["clojure" "haskell" "erlang" "scala" "python"]))
(gen/sample languages)
;; => ("clojure" "scala" "clojure" "haskell" "clojure" "erlang" "erlang"
;; => "erlang" "haskell" "python")
```
## An integer or nil
```clojure
(def int-or-nil (gen/one-of [gen/small-integer (gen/return nil)]))
(gen/sample int-or-nil)
;; => (nil 0 -2 nil nil 3 nil nil 4 2)
```
## An integer 90% of the time, nil 10%
```clojure
(def mostly-ints (gen/frequency [[9 gen/small-integer] [1 (gen/return nil)]]))
(gen/sample mostly-ints)
;; => (0 -1 nil 0 -2 0 6 -6 8 7)
```
## Even, positive integers
```clojure
(def even-and-positive (gen/fmap #(* 2 %) gen/nat))
(gen/sample even-and-positive 20)
;; => (0 0 2 0 8 6 4 12 4 18 10 0 8 2 16 16 6 4 10 4)
```
## Powers of two
```clojure
;; generate exponents with gen/nat,
;; and then apply the function to them
(def powers-of-two (gen/fmap #(int (Math/pow 2 %)) gen/nat))
(gen/sample powers-of-two)
;; => (1 1 4 8 8 32 8 1 2 2)
```
## Sorted seq of integers
```clojure
;; apply the sort function to each generated vector
(def sorted-vec (gen/fmap sort (gen/vector gen/small-integer)))
(gen/sample sorted-vec)
;; => (() (-1) (-2 -2) (-1 2 3) (-1 2 4) (-3 2 3 3 4) (1)
;; => (-4 0 1 3 4 6) (-5 -4 -1 0 2 8) (1))
```
## An integer and a boolean
```clojure
(def int-and-boolean (gen/tuple gen/small-integer gen/boolean))
(gen/sample int-and-boolean)
;; => ([0 false] [0 true] [0 true] [3 true] [-3 false]
;; => [0 true] [4 true] [0 true] [-2 true] [-9 false])
```
## Any number but 5
```clojure
(def anything-but-five (gen/such-that #(not= % 5) gen/small-integer))
(gen/sample anything-but-five)
;; => (0 0 -2 1 -3 1 -4 7 -1 6)
```
It's important to note that `such-that` should only be used for predicates that
are _very_ likely to match. For example, you should _not_ use `such-that` to
filter out random vectors that are not sorted, as is this is exceedingly
unlikely to happen randomly. If you want sorted vectors, just sort them using
`gen/fmap` and `sort`.
## A vector and a random element from it
```clojure
(def vector-and-elem (gen/bind (gen/not-empty (gen/vector gen/small-integer))
#(gen/tuple (gen/return %) (gen/elements %))))
(gen/sample vector-and-elem)
;; =>([[-1] -1]
;; => [[0] 0]
;; => [[-1 -1] -1]
;; => [[2 0 -2] 2]
;; => [[0 1 1] 0]
;; => [[-2 -3 -1 1] -1]
;; => [[-1 2 -5] -5]
;; => [[5 -7 -3 7] 5]
;; => [[-1 2 2] 2]
;; => [[-8 7 -3 -2 -6] -3])
```
`gen/bind` and `gen/fmap` are similar: they're both binary functions that take
a generator and a function as arguments (though their argument order is
reversed). They differ in what the provided function's return value should be.
The function provided to `gen/fmap` should return a _value_. We saw that
earlier when we used `gen/fmap` to sort a vector. `sort` returns a normal
value. The function provided to `gen/bind` should return a _generator_. Notice
how above we're providing a function that returns a `gen/tuple` generator? The
decision of which to use depends on whether you want to simply transform the
_value_ of a generator (sort it, multiply it by two, etc.), or create an
entirely new generator out of it.
---
Go [back](intro.md) to the intro.
clojure-test.check-b2ec872/doc/growth-and-shrinking.md 0000664 0000000 0000000 00000040213 14151735222 0022761 0 ustar 00root root 0000000 0000000 # Growth and Shrinking
Sizing in test.check seems simple at first glance, but there are
subtleties that are important to understand to ensure that your tests
are covering what you expect them to, and that failing cases can
shrink in an effective way.
It's useful to keep in mind that the way test.check controls the
"size" of the data it generates is entirely different from how it
shrinks failing examples, and we'll cover both of these processes
below.
## Growth
### The `size` Parameter
Internally, a generator cannot produce a value without specifying what
`size` the value should be. This is what allows test.check to start a
test run by trying very simple values first, and gradually trying
larger and larger values. The meaning of `size` depends on the
generator; some generators ignore it altogether.
You can see how `size` affects different generators by experimenting
with the `gen/generate` function, which takes an optional `size`
argument:
``` clojure
(defn sizing-sample
[g]
(into {}
(for [size [0 5 25 200]]
[size
(repeatedly 5 #(gen/generate g size))])))
;; with gen/nat, the integer is roughly proportional to the `size`
(sizing-sample gen/nat)
;; => {0 (0 0 0 0 0),
;; 5 (4 1 3 3 5),
;; 25 (12 8 24 25 22),
;; 200 (63 143 31 199 7)}
;; with gen/large-integer, the integer can be much larger
(sizing-sample gen/large-integer)
;; => {0 (-1 0 -1 -1 -1),
;; 5 (1 6 7 4 2),
;; 25 (-1 55798 23198 -11 6124159),
;; 200 (8371567737
;; -393642130983883
;; -56826587109
;; 114071285698586153
;; 5723723802814291)}
;; a collection generator grows the collection size and the
;; size of its elements
(dissoc (sizing-sample (gen/vector gen/nat)) 200)
;; => {0 ([] [] [] [] []),
;; 5 ([1 0 4 3 4] [3] [] [2 0] [2 2 4]),
;; 25 ([8 10 0 16 7 7 2 19 16 10]
;; [16 8 0 18 11 23 11 7 1 19 10 4 0 23 17 2 17 12 1 20]
;; [15 19 7 6 4]
;; [6 5 14 12 19 12 7 13 17 10 16 6 9 1]
;; [19 16 5 22 2 5 10 3 6 7 22 19 21 10 4 22 23 5 9 21 19 16 2])}
;; unless we fix the size of the collection
(sizing-sample (gen/vector gen/large-integer 3))
;; => {0 ([-1 -1 -1] [-1 0 -1] [-1 0 -1] [0 0 -1] [0 0 -1]),
;; 5 ([-10 -1 -1] [4 -14 15] [2 2 -1] [-3 -7 2] [-2 0 0]),
;; 25 ([-4417 32 189]
;; [12886 576 -2]
;; [0 -2 -89799]
;; [108 -250318 -1218212]
;; [-10 -27 -5]),
;; 200 ([-8526639064861 -44 2311]
;; [819670069072907 -4481451104804003250 -81]
;; [-1985273 781374 -480118376]
;; [-2038 2593 -5355]
;; [143974988 4 209260326382094708])}
;; gen/uuid completely ignores `size`
(sizing-sample gen/uuid)
;; => {0 (#uuid "29ec3e6f-e35c-466f-b9d5-fa27e043743d"
;; #uuid "7bb1c53d-0b12-4be0-a2c5-b7d6a406f64f"
;; #uuid "8f07cab1-4e3d-4bd1-a699-6d653b353588"
;; #uuid "b2e65dcb-fad1-4f1e-afe6-5645d1759a9d"
;; #uuid "83d9ca17-cc07-4515-bf22-625e1e537943"),
;; 5 (#uuid "f1a35527-128a-4cda-b9ba-d0fec4255674"
;; #uuid "f7a7f621-5e84-4d09-b3e3-849bebfea048"
;; #uuid "d92aa9f9-7be6-4e02-80a7-ab89d9074b48"
;; #uuid "c5f24f29-1472-454a-9171-34a2d74074b1"
;; #uuid "c14ac2f6-a31a-4c0b-8e50-273feac6dbda"),
;; 25 (#uuid "008a8dcb-11b1-41cc-87b7-5b7b1b704e8e"
;; #uuid "f89c245a-7667-4d2f-9a88-b33c92ad09ca"
;; #uuid "dc209d21-cfbc-4e3e-a338-7a8b146084b4"
;; #uuid "c9585173-a4f5-4f3b-9a98-7eecc4801007"
;; #uuid "b10196fb-9cdb-4148-8d99-dea80be6d7fa"),
;; 200 (#uuid "b9a70100-4b02-404b-9de4-611ad5a2cefe"
;; #uuid "32ca9f24-3248-476a-ab9f-d58c9aa5df9c"
;; #uuid "9de09d09-0121-4ffe-b7a7-37cb95191bcb"
;; #uuid "bcaeeaf8-4225-40d9-a2b4-7ba2c417a3f0"
;; #uuid "6153d4e4-038c-4e73-be0c-2394b3078e25")}
```
### How `size` changes over a test run
`clojure.test.check/quick-check` generates the input for the first
trial using `size=0`, for the second trial it uses `size=1`, and
continues incrementing until the 200th trial with `size=199`, after
which it starts over. In general it uses `(cycle (range 200))`.
Test.check starts with small sizes so that it will catch easy bugs
quickly without needing to generate very large input and then shrink
it, and so that edge cases produced by small sizes have a good chance
of being caught. Custom generators that ignore the `size` parameter
are thwarting this feature.
Also see the warning about small test counts below.
### Controlling `size`
Custom generators can use and modify `size` in several different ways.
#### `gen/sized`
`gen/sized` is essentially a facility for "reading" the size as you
create a generator.
``` clojure
(def g
(gen/sized
(fn [size]
(gen/let [x gen/large-integer]
(format "I generated %d using size=%d!" x size)))))
(gen/sample g)
;; => ("I generated -1 using size=0!"
;; "I generated 0 using size=1!"
;; "I generated -1 using size=2!"
;; "I generated 0 using size=3!"
;; "I generated -1 using size=4!"
;; "I generated 2 using size=5!"
;; "I generated 0 using size=6!"
;; "I generated -2 using size=7!"
;; "I generated 12 using size=8!"
;; "I generated -2 using size=9!")
```
#### `gen/resize`
`gen/resize` lets you pin a generator to a particular `size`
``` clojure
(def g
(gen/sized
(fn [size]
(gen/let [x (gen/resize 100 gen/large-integer)]
(format "I generated %d, even though size=%d!" x size)))))
(gen/sample g)
;; => ("I generated 2047953455, even though size=0!"
;; "I generated -126726750629, even though size=1!"
;; "I generated 50066179923, even though size=2!"
;; "I generated 2078170872141134, even though size=3!"
;; "I generated 678227175, even though size=4!"
;; "I generated 3858768648, even though size=5!"
;; "I generated -23231577, even though size=6!"
;; "I generated 4, even though size=7!"
;; "I generated 3503438568408, even though size=8!"
;; "I generated 186422559275, even though size=9!")
```
#### `gen/scale`
`gen/scale` is a convenient way to modify the `size` that a generator
sees (which you could do more tediously by combining `gen/sized` and
`gen/resize`).
``` clojure
(def gen-small-vectors-of-large-numbers
(gen/scale #(max 0 (Math/log %))
(gen/vector (gen/scale #(* % 100) gen/large-integer))))
(gen/sample gen-small-vectors-of-large-numbers 20)
;; => ([]
;; []
;; []
;; [234236101]
;; [34663197938259]
;; [-15]
;; [87]
;; []
;; []
;; [-5310368659078251]
;; [-8403929563691 126041240]
;; []
;; []
;; []
;; []
;; [-84306261785]
;; [35060841580649472 45255404980]
;; []
;; [61658595345 277549824780866555]
;; [])
```
### Gotchas
#### Integer generators
Test.check originally contained six integer generators which are
all variants of the same thing:
``` clojure
(gen/sample (gen/tuple gen/nat gen/int
gen/pos-int gen/neg-int
gen/s-pos-int gen/s-neg-int))
;; => ([0 0 0 0 1 -1]
;; [1 1 0 0 1 -2]
;; [0 2 0 -1 2 -2]
;; [0 0 2 -2 2 -3]
;; [3 -2 2 -2 5 -2]
;; [3 -4 3 -2 5 -2]
;; [0 0 1 -2 5 -4]
;; [6 5 3 -6 2 -4]
;; [1 -2 8 -5 4 -5]
;; [4 -6 2 -9 8 -2])
```
Besides the confusing names, the big gotcha is that the range of these
generators is is more or less strictly bounded by `size`, and so any
use of them will by default not test numbers bigger than `200`, which
is unacceptable coverage for a lot of
applications. `gen/large-integer` should avoid this issue. Most of the
small integer generators have been deprecated, with the exception of
`gen/nat` and the new-and-less-confusingly-named `gen/small-integer`.
#### Small Test Count
Due to the use of `(cycle (range 200))` as the `size` progression
during a test run (described above), tests that use less than 200
trials will not be exposed to the normal range of sizes, and in
particular tests that run less than ~10 trials will be getting very
poor coverage.
If you don't want to run very many trials for some reason, you can
mitigate this with `gen/scale`; e.g.:
``` clojure
;; uses sizes 0,20,40,60,80,100,120,140,160,180
(tc/quick-check 10
(prop/for-all [x (gen/scale #(* 20 %) g)]
(f x)))
```
#### `gen/sample`
`gen/sample` starts with very small sizes in the same way that the
`quick-check` function does. This can be misleading to users who don't
expect that and take the first ten results from `gen/sample` to be
representative of the distribution of a generator. Using `gen/generate`
with an explicit `size` argument can be a better way of learning about
the distribution of a generator.
#### Collection composition
_See [TCHECK-106](https://clojure.atlassian.net/browse/TCHECK-106)_
test.check's collection generators by default select a size for the
generated collection that is proportional to the `size` parameter.
This generally works well enough, but when creating generators of
nested collections it can lead to Very Large output, in the worst
case exhausting available memory.
``` clojure
(defn max-size
[colls]
(->> colls (map flatten) (map count) (apply max)))
(-> gen/nat
(gen/vector)
(gen/vector)
(gen/sample 200)
(max-size))
;; => 4747
(-> gen/nat
(gen/vector)
(gen/vector)
(gen/vector)
(gen/sample 200)
(max-size))
;; => 195635
```
This can be mitigated with strategic resizing.
## Shrinking
Despite the conceptual similarity, the shrinking algorithm has nothing
to do with the `size` parameter. `size` affects the distribution of a
random process, while shrinking is entirely deterministic and based on
the properties of the basic generators and the combinators.
### Gotchas
#### Unnecessary `bind`
_See [TCHECK-112](http://dev.clojure.org/jira/browse/TCHECK-112)_
`gen/bind` (and multi-clause uses of `gen/let`) is a powerful
combinator that allows you to combine generators in "phases", where
the later generators can make use of values generated in earlier
generators. This can be very useful, but it also is difficult to
shrink in a general way (see the details in the jira ticket linked
above for an example of this).
This means that if you care about the effectiveness of shrinking, it
can be worth taking care not to use `gen/bind` where you don't have
to.
For example, say you wanted to generate a collection of an even number
of integers. You might think to do this by first generating an even
number for the length, and then using that with `gen/vector`:
``` clojure
(def gen-an-even-number-of-integers
(gen/let [even-number (gen/fmap #(* 2 %) gen/nat)]
(gen/vector gen/large-integer even-number))
;; or, rewritten without gen/let:
#_
(gen/bind (gen/fmap #(* 2 %) gen/nat)
(fn [even-number]
(gen/vector gen/large-integer even-number))))
(def gen-strictly-increasing-integers
(gen/bind gen/nat #(gen-strictly-increasing-integers* % 0)))
(gen/sample gen-an-even-number-of-integers)
;; => ([]
;; [-1 -1]
;; []
;; []
;; [-1 -4 0 -1 -2 2]
;; [0 2]
;; [6 -1 1 4 8 30 -2 0 21 -2 -1 0]
;; [2 -2]
;; [0 -1 7 -33 9 -49 14 14 1 1 -1 0 1 -2]
;; [0 2 -1 -9])
```
It looks okay, but we can see a problem when shrinking
``` clojure
(tc/quick-check
10000
(prop/for-all [xs gen-an-even-number-of-integers]
(not-any? #{42} xs)))
;; => {:result false,
;; :seed 1482063539636,
;; :failing-size 176,
;; :num-tests 177,
;; :fail [[-92431438766962 63530 -164135493216497125 -3270829858185774102
;; -260351529 -59352395648111 -4 -17469 -31636041044035
;; 7336711261875630 -1636343167264 -20912505735276 -23753842660
;; 13368897139830488 -1 -250220724 24370059524 -8266208340
;; 949778971431 -2233935110 -10 -226980 -166150097914784515
;; 1446375390291034 17977873032 -306481593932634684 2 321887
;; 1535082621176844 24757631603 -15034747392805020 -248163661633
;; -2272021814312959965 -1045247795284 177163345 13467
;; -355036687887336641 -4098005768175 -8055 -6317647 133903089
;; 3881 42630713210694061 -2673915744452669 421802903098966
;; -34741965 1630280301 231213827 858102836152006 5282
;; -269037059 -4985695680423 -187884359879 -68958514179
;; 1356369075861 -1 5701573467 -9 3993 -66360585914444
;; 1796329244719094 -9139976096708138 -11216 908965 17156900
;; 5559124946 13403 -2345413999 42 1 -76248253307297 222887742816784
;; 1274360 -68929 1 -213900 -122103507959521 2767011893757957
;; -3626024977 84758031 461767131016 -122390014709033 -1052250928741535
;; 1 383 -575550 -8793837628976134 -540423902910181208
;; 7896218 -49725987 -68869268253 -470133169 -7407245227931
;; -2266127667584039 -60700760 7759 14242030181 -565807123157122480
;; -21599378358624 1000368132 -1 109045164 23447410579428773
;; 1966123182 949341425 16444393 60598 340542 -187842543295
;; 3676708478 -236529145197024202 -791408585920527 -3452127625272781
;; -132208027103 25 -17500698053396417 -3375613232 -88206409961854
;; -3368 7 -179071081209 16894761949763 -132946664 -30990191248478947
;; 402283570687771 29732288327985 -6211 -885340544041821
;; -2134764587 -16103 518432298883356507 -30801 -311015444053486
;; -52408941698 -2282761018048237612 438556242]],
;; :shrunk {:total-nodes-visited 97,
;; :depth 72,
;; :result false,
;; :smallest [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
;; 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
;; 0 0 0 0 0 0 0 0 0 0 0 0 42 0]]}}
```
Test.check wasn't able to shrink the collection to the optimal size (2
elements) because of the structure of the generators (though it did
manage to shrink from 136 to 70 elements). `gen/vector` is not able to
remove elements from the vector when shrinking because it was called
with a fixed size. So the only way to shrink the size of the vector is
to shrink the value from `(gen/fmap #(* 2 %) gen/nat)`. This is one of
the things that `gen/bind` tries, but when it shrinks the
`even-number`, it has no choice but to create an entirely new
generator from `gen-vector` and generate a fresh value that's
unrelated to the original failing collection. This fresh value is
highly unlikely to also fail, and so that part of the shrinking
algorithm is unlikely to be very fruitful (though sometimes it works,
like in the example above where we were lucky to reduce the size from
136 to 70).
Sometimes there are natural ways to write a generator that do not use
`gen/bind`. For example, in this case we could use `gen/vector`
without a fixed size, and modify it with `gen/fmap` to ensure it has
an even number of elements:
``` clojure
(def gen-an-even-number-of-integers
(gen/let [xs (gen/vector gen/large-integer)]
(cond-> xs (odd? (count xs)) pop))
;; or, rewritten without gen/let:
#_
(gen/fmap (fn [xs] (cond-> xs (odd? (count xs)) pop))
(gen/vector gen/large-integer)))
(gen/sample gen-an-even-number-of-integers)
;; => ([]
;; []
;; []
;; [0 0]
;; [-6 0]
;; [0 6]
;; [22 -2 -2 0]
;; []
;; [0 -2 15 95]
;; [0 -7 -205 -9])
;; => {:result false,
;; :seed 1482064393801,
;; :failing-size 160,
;; :num-tests 161,
;; :fail [[-20 3758 -174 2908907278 7767028 6628657334113049 -7409556399
;; -3379156667294 -473722 760549137 -7137938397056 124401939
;; 1590227088 174 482329 4972338 -53955167617312 -237816
;; 1 -13159175 2 1911311087865 -2675112025 -391133804902
;; -1444282617174675 1477509406066 138075 -3555024567808
;; 0 -26579022516 0 5182 -82958251 -1287 -35417824257454314
;; -129794819488 42 1642761942897 975833887255494324 701657767868417
;; 3940940 2 458 -1337864380187855428 -6716451 23621121
;; 1 -826778832808 -2 -137892 6996928807632 -1 -506146269826334582
;; -23783 -419873644169 3928808977969 0 -3595621317791
;; -66706208260298 -13099314 10721686280827793 -50904466
;; -6134528453735 24779423757 -43 1042490490 134213823314 -29]],
;; :shrunk {:total-nodes-visited 115, :depth 68, :result false, :smallest [[42 0]]}}
```
clojure-test.check-b2ec872/doc/intro.md 0000664 0000000 0000000 00000032143 14151735222 0020053 0 ustar 00root root 0000000 0000000 # Introduction to test.check
test.check is a tool for writing property-based tests. This differs from
traditional unit-testing, where you write individual test-cases. With
test.check you write universal quantifications, properties that should hold
true for all input. For example, for all vectors, reversing the vector should
preserve the count. Reversing it twice should equal the input. In this guide,
we'll cover the thought process for coming up with properties, as well as the
practice of writing the tests themselves.
## A simple example
First, let's start with an example, suppose we want to test a sort function.
It's easy to come up with some trivial properties for our function, namely that
the output should be in ascending order. We also might want to make sure that
the count of the input is preserved. Our test might look like:
```clojure
(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop #?@(:cljs [:include-macros true])])
(def property
(prop/for-all [v (gen/vector gen/small-integer)]
(let [s (sort v)]
(and (= (count v) (count s))
(or (empty? s)
(apply <= s))))))
;; test our property
(tc/quick-check 100 property)
;; => {:result true,
;; => :pass? true,
;; => :num-tests 100,
;; => :time-elapsed-ms 90,
;; => :seed 1528578896309}
```
What if we were to forget to actually sort our vector? The test will fail, and
then test.check will try and find 'smaller' inputs that still cause the test
to fail. For example, the function might originally fail with input:
`[5 4 2 2 2]`, but test.check will shrink this down to `[0 -1]` (or `[1 0]`).
```clojure
(def bad-property
(prop/for-all [v (gen/vector gen/small-integer)]
(or (empty? v) (apply <= v))))
(tc/quick-check 100 bad-property)
;; => {:num-tests 6,
;; => :seed 1528579035247,
;; => :fail [[-2 -4 -4 -3]],
;; => :failed-after-ms 1,
;; => :result false,
;; => :result-data nil,
;; => :failing-size 5,
;; => :pass? false,
;; => :shrunk
;; => {:total-nodes-visited 16,
;; => :depth 4,
;; => :pass? false,
;; => :result false,
;; => :result-data nil,
;; => :time-shrinking-ms 1,
;; => :smallest [[0 -1]]}}
```
This process of shrinking is done automatically, even for our more complex
generators that we write ourselves.
## Generators
In order to write our property, we'll use generators. A generator knows how to
generate random values for a specific type. The `test.check.generators`
namespace has many built-in generators, as well as combinators for creating
your own new generators. You can write sophisticated generators just by
combining the existing generators with the given combinators. As we write
generators, we can see them in practice with the `sample` function:
```clojure
(require '[clojure.test.check.generators :as gen])
(gen/sample gen/small-integer)
;; => (0 1 -1 0 -1 4 4 2 7 1)
```
we can ask for more samples:
```clojure
(gen/sample gen/small-integer 20)
;; => (0 1 1 0 2 -4 0 5 -7 -8 4 5 3 11 -9 -4 6 -5 -3 0)
```
or get a lazy-seq of values:
```clojure
(take 1 (gen/sample-seq gen/small-integer))
;; => (0)
```
You may notice that as you ask for more values, the 'size' of the generated
values increases. As test.check generates more values, it increases the
'size' of the generated values. This allows tests to fail early, for simple
values, and only increase the size as the test continues to pass.
### Compound generators
Some generators take other generators as arguments. For example the `vector`
and `list` generator:
```clojure
(gen/sample (gen/vector gen/nat))
;; => ([] [] [1] [1] [] [] [5 6 6 2 0 1] [3 7 5] [2 0 0 6 2 5 8] [9 1 9 3 8 3 5])
(gen/sample (gen/list gen/boolean))
;; => (() () (false) (false true false) (false true) (false true true true) (true) (false false true true) () (true))
(gen/sample (gen/map gen/keyword gen/boolean) 5)
;; => ({} {:z false} {:k true} {:v8Z false} {:9E false, :3uww false, :2s true})
```
Sometimes we'll want to create heterogeneous collections. The `tuple` generator
allows us to do this:
```clojure
(gen/sample (gen/tuple gen/nat gen/boolean gen/ratio))
;; => ([0 false 0] [1 false 0] [0 false 2] [0 false -1/3] [1 true 2] [1 false 0] [2 false 3/5] [3 true -1] [3 true -5/3] [6 false 9/5])
```
### Generator combinators
There are several generator combinators, we'll take a look at `fmap`,
`such-that` and `bind`.
#### fmap
`fmap` allows us to create a new generator by applying a function to the
values generated by another generator. Let's say we want to to create a set of
natural numbers. We can create a set by calling `set` on a vector. So let's
create a vector of natural numbers (using the `nat` generator), and then use
`fmap` to call `set` on the values:
```clojure
(gen/sample (gen/fmap set (gen/vector gen/nat)))
;; => (#{} #{1} #{1} #{3} #{0 4} #{1 3 4 5} #{0 6} #{3 4 5 7} #{0 3 4 5 7} #{1 5})
```
Imagine you have a record, that has a convenience creation function, `foo`. You
can create random `foo`s by generating the types of the arguments to `foo` with
`tuple`, and then using `(fmap foo (tuple ...))`.
#### such-that
`such-that` allows us to create a generator that passes a predicate. Imagine we
wanted to generate non-empty lists, we can use `such-that` to filter out empty
lists:
```clojure
(gen/sample (gen/such-that not-empty (gen/list gen/boolean)))
;; => ((true) (true) (false) (true false) (false) (true) (false false true true) (false) (true) (false))
```
#### bind
`bind` allows us to create a new generator based on the _value_ of a previously
created generator. For example, say we wanted to generate a vector of keywords,
and then choose a random element from it, and return both the vector and the
random element. `bind` takes a generator, and a function that takes a value
from that generator, and creates a new generator.
```clojure
(def keyword-vector (gen/such-that not-empty (gen/vector gen/keyword)))
(def vec-and-elem
(gen/bind keyword-vector
(fn [v] (gen/tuple (gen/elements v) (gen/return v)))))
(gen/sample vec-and-elem 4)
;; => ([:va [:va :b4]] [:Zu1 [:w :Zu1]] [:2 [:2]] [:27X [:27X :KW]])
```
This allows us to build quite sophisticated generators.
### Record generators
Let's go through an example of generating random values of our own
`defrecord`s. Let's create a simple user record:
```clojure
(defrecord User [user-name user-id email active?])
;; recall that a helper function is automatically generated
;; for us
(->User "reiddraper" 15 "reid@example.com" true)
;; #user.User{:user-name "reiddraper",
;; :user-id 15,
;; :email "reid@example.com",
;; :active? true}
```
We can use the `->User` helper function to construct our user. First, let's
look at the generators we'll use for the arguments. For the user-name, we can
just use an alphanumeric string, user IDs will be natural numbers, we'll
construct our own simple email generator, and we'll use booleans to denote
whether the user account is active. Let's write a simple email address
generator:
```clojure
(def domain (gen/elements ["gmail.com" "hotmail.com" "computer.org"]))
(def email-gen
(gen/fmap (fn [[name domain-name]]
(str name "@" domain-name))
(gen/tuple (gen/not-empty gen/string-alphanumeric) domain)))
(last (gen/sample email-gen))
;; => "CW6161Q6@hotmail.com"
```
To put it all together, we'll use `fmap` to call our record constructor, and
`tuple` to create a vector of the arguments:
```clojure
(def user-gen
(gen/fmap (partial apply ->User)
(gen/tuple (gen/not-empty gen/string-alphanumeric)
gen/nat
email-gen
gen/boolean)))
(last (gen/sample user-gen))
;; => #user.User{:user-name "kWodcsE2",
;; :user-id 1,
;; :email "r2ed3VE@computer.org",
;; :active? true}
```
### Recursive generators
---
NOTE: Writing recursive generators was significantly simplified in version
0.5.9. For the old way, see the [0.5.8
documentation](https://github.com/clojure/test.check/blob/v0.5.8/doc/intro.md#recursive-generators).
---
Writing recursive, or tree-shaped generators is easy using `gen/recursive-gen`.
`recursive-gen` takes two arguments, a compound generator, and a scalar
generator. We'll start with a simple example, and then move into something more
complex. First, let's generate a nested vector of booleans. So our compound
generator will be `gen/vector` and our scalar will be `gen/boolean`:
```clojure
(def nested-vector-of-boolean (gen/recursive-gen gen/vector gen/boolean))
(last (gen/sample nested-vector-of-boolean 20))
;; => [[[true] true] [[] []]]
```
Now, let's make our own, JSON-like generator. We'll allow `gen/list` and
`gen/map` as our compound types and `gen/small-integer` and `gen/boolean` as our scalar
types. Since `recursive-gen` only accepts one of each type of generator, we'll
combine our compound types with a simple function, and the two scalars with
`gen/one-of`.
```clojure
(def compound (fn [inner-gen]
(gen/one-of [(gen/list inner-gen)
(gen/map inner-gen inner-gen)])))
(def scalars (gen/one-of [gen/small-integer gen/boolean]))
(def my-json-like-thing (gen/recursive-gen compound scalars))
(last (gen/sample my-json-like-thing 20))
;; =>
;; (()
;; {(false false) {true -3, false false, -7 1},
;; {4 -11, 1 -19} (false),
;; {} {1 6}})
```
And we see we got a list whose first element is the empty list. The second
element is a map with int keys and values. Etc.
## More Examples
Let's say we're testing a sort function. We want to check that that our sort
function is idempotent, that is, applying sort twice should be equivalent to
applying it once: `(= (sort a) (sort (sort a)))`. Let's write a quick test to
make sure this is the case:
```clojure
(require '[clojure.test.check :as tc])
(require '[clojure.test.check.generators :as gen])
(require '[clojure.test.check.properties :as prop #?@(:cljs [:include-macros true])])
(def sort-idempotent-prop
(prop/for-all [v (gen/vector gen/small-integer)]
(= (sort v) (sort (sort v)))))
(tc/quick-check 100 sort-idempotent-prop)
;; => {:result true,
;; => :pass? true,
;; => :num-tests 100,
;; => :time-elapsed-ms 28,
;; => :seed 1528580707376}
```
In prose, this test reads: for all vectors of integers, `v`, sorting `v` is
equal to sorting `v` twice.
What happens if our test fails? _test.check_ will try and find 'smaller'
inputs that still fail. This process is called shrinking. Let's see it in
action:
```clojure
(def prop-sorted-first-less-than-last
(prop/for-all [v (gen/not-empty (gen/vector gen/small-integer))]
(let [s (sort v)]
(< (first s) (last s)))))
(tc/quick-check 100 prop-sorted-first-less-than-last)
;; => {:num-tests 5,
;; => :seed 1528580863556,
;; => :fail [[-3]],
;; => :failed-after-ms 1,
;; => :result false,
;; => :result-data nil,
;; => :failing-size 4,
;; => :pass? false,
;; => :shrunk
;; => {:total-nodes-visited 5,
;; => :depth 2,
;; => :pass? false,
;; => :result false,
;; => :result-data nil,
;; => :time-shrinking-ms 1,
;; => :smallest [[0]]}}
```
This test claims that the first element of a sorted vector should be less-than
the last. Of course, this isn't true: the test fails with input `[-3]`, which
gets shrunk down to `[0]`, as seen in the output above. As your test functions
require more sophisticated input, shrinking becomes critical to being able
to understand exactly why a random test failed. To see how powerful shrinking
is, let's come up with a contrived example: a function that fails if it's
passed a sequence that contains the number 42:
```clojure
(def prop-no-42
(prop/for-all [v (gen/vector gen/small-integer)]
(not (some #{42} v))))
(tc/quick-check 100 prop-no-42)
;; => {:num-tests 45,
;; => :seed 1528580964834,
;; => :fail
;; => [[-35 -9 -31 12 -30 -40 36 36 25 -2 -31 42 8 31 17 -19 3 -15 44 -1 -8 27 16]],
;; => :failed-after-ms 11,
;; => :result false,
;; => :result-data nil,
;; => :failing-size 44,
;; => :pass? false,
;; => :shrunk
;; => {:total-nodes-visited 16,
;; => :depth 5,
;; => :pass? false,
;; => :result false,
;; => :result-data nil,
;; => :time-shrinking-ms 1,
;; => :smallest [[42]]}}
```
We see that the test failed on a rather large vector, as seen in the `:fail`
key. But then _test.check_ was able to shrink the input down to `[42]`, as
seen in the keys `[:shrunk :smallest]`.
To learn more, check out the [documentation](#documentation) links.
### `clojure.test` Integration
The macro `clojure.test.check.clojure-test/defspec` allows you to succinctly
write properties that run under the `clojure.test` runner, for example:
```clojure
(defspec first-element-is-min-after-sorting ;; the name of the test
100 ;; the number of iterations for test.check to test
(prop/for-all [v (gen/not-empty (gen/vector gen/small-integer))]
(= (apply min v)
(first (sort v)))))
```
### ClojureScript
ClojureScript support was added in version `0.7.0`.
Integrating with `cljs.test` is via the
`clojure.test.check.clojure-test/defspec` macro, in the same fashion
as integration with `clojure.test` on the jvm.
---
Check out [page two](generator-examples.md) for more examples of using
generators in practice.
clojure-test.check-b2ec872/doc/migrating-from-simple-check.md 0000664 0000000 0000000 00000001531 14151735222 0024201 0 ustar 00root root 0000000 0000000 # Migrating from simple-check
_test.check_ used to be called
[_simple-check_](https://github.com/reiddraper/simple-check).
In order to migrate from _simple-check_ to _test.check_, you'll need to do two
things:
* Update project.clj
In your `project.clj` replace `[reiddraper/simple-check "0.5.6"]` with
`[org.clojure/test.check "0.6.2"]` (note: your version numbers may be
different).
* Update namespace declarations
Update your namespaces: `simple-check.core` becomes `clojure.test.check` (note
the dropping of 'core'). For everything else you can simply replace `simple-check`
with `clojure.test.check`. Let's make it easy:
```shell
find test -name '*.clj' -print0 | xargs -0 sed -i.bak \
-e 's/simple-check.core/clojure.test.check/' \
-e 's/simple-check/clojure.test.check/'
```
Review the updates.
clojure-test.check-b2ec872/doc/old-confluence-notes.md 0000664 0000000 0000000 00000043121 14151735222 0022741 0 ustar 00root root 0000000 0000000 # Old Confluence Notes
These notes were hastily exported from dev.clojure.org on 2019-04-24.
Formatting was mostly preserved, except that bulleted lists ended up
as preformatted text.
## Main Page
Some old and largely outdated design ideas for test.check.
### API Overhaul Ideas
Responding to TCHECK-99 got me thinking about trying to pull off a
complete redesign of the (mostly generators) API, while maintaining as
much backwards compatibility as possible, at least for a few versions.
I'm going to try to list here the sorts of problems that could be
fixed with such an overhaul that would be hard to fix otherwise.
Generators
Convert all generators to be functions; if a default generator is available, it would be obtained by calling the function with 0 args
Removes confusion such as with the current pair of large-integer and large-integer*
Get rid of nat, pos-int, s-pos-int, and similar. Replace with a generator called small-integer that takes various options
rename large-integer to integer, consider whether to allow bigints
change gen/vector and gen/list APIs to match gen/set
Maybe allow passing options when calling a generator, so that generators like strings and doubles can be customizable in a different way
e.g., this way a generator like gen/any could support being customized to never generate NaNs anywhere without having to explicitly code for that
clojure-test
change defspec to use clojure.test/is
can maybe do this w/o breaking changes by putting an alternative to prop/for-all in the clojure-test namespace???
Properties
### Numerics
The situation here used to be much worse, when all of the integer
generators would only generate numbers up to ~200 by default, and no
double generator existed at all. Now we have gen/large-integer which
does well for large ranges of integers, and gen/double which is also
pretty good. The remaining holes are relatively minor:
numbers from infinite sets
a generator for bigints; unclear if this would require bounds or not
a good generator for ratios; again unclear about bounds, both on abs value and on denominator size
bigdecimals
some lower-level utility generators
a generator for doubles between 0 and 1
a raw generator of evenly-distributed longs
#### What is a solid approach to generating infinite-domain types?
In particular bigints, ratios, bigdecimals, and maybe even collections.
One idea I had was a generator that always has a non-zero probability of generating any given value, and the size parameter determines the average size of the thing generated (e.g., in bits). I think this means that the size of the output, at least to start, would have a geometric distribution.
#### How should NaNs be handled?
##### Background
gen/double and gen/double* generate NaN, at least by default. This is intentional, since NaN is a useful edge case to test for any code that deals with doubles. However NaN != NaN, which means that any data structure containing a NaN is not equal to itself, which screws up all sorts of things.
This isn't such a problem for gen/double, since users can opt-out of NaNs with gen/double* when appropriate. However, gen/simple-type, gen/simple-type-printable, gen/any, and gen/any-printable are all affected by this in 0.9.0.
##### Possible Approaches
Modify generators in-place
Just the four composite generators (gen/simple-type{-printable} and gen/any{-printable})
gen/double doesn't generate NaN, and gen/double* allows it as opt-in
Add new generators
Analogues of the four composite, or some subset (gen/simple-type-no-NaN, gen/any-no-NaN, etc.)
Something else?
arguably users can manage this on their own, although excluding NaN from gen/any is not so simple, since a naïve wrapping in gen/such-that won't work
### Test Runner Topics
#### Test Failure Feedback
Currently a test error (i.e., exception caught) gives ample feedback
because the user gets the exception object to inspect, but a mere
failure (falsy return value) doesn't communicate anything other than
whether the result was false or nil. clojure.test goes farther for
this, with a probably-overly-macrocentric approach.
Probably the best approach is to evaluate the result of a property
expression with a protocol, so the current behavior can be maintained
but new implementations can provide more information.
I think reid has already started in this direction on the "feature/result-map" branch
Related: https://github.com/yeller/matcha and this alternate clojure.test integration.
#### Parallel Tests
Requires the immutable RNG for full determinism.
Do we actually need complex coordination of threads or can we do a naive thing?
#### Rerunning a single test easily
#### Running tests for a specific amount of time
Related: test.chuck/times, and a branch that Tom Crayford worked on
### Some ideas from Hypothesis
From this (http://www.drmaciver.com/2015/01/using-multi-armed-bandits-to-satisfy-property-based-tests/) blog post primarily. The library itself is here.
It essentially uses a rather more sophisticated approach to getting more interesting distributions.
Idea for trying this out: have each generator keep track of how many
size parameters it can accept. E.g., gen/int takes one size
parameter. (gen/list gen/int) takes two (one for the size of the list,
one for the size given to gen/int). (gen/tuple g1 g2 g3) takes the sum
of the size-parameter-counts for its args. This breaks down when using
bind and recursive generators, so we might have to fall back on just
passing an infinite number of size parameters, or using the RNG to
determine them, or something like that.
On the other hand, I spoke to David directly about this feature and he
says he regrets including it in hypothesis due to the high complexity
and low value. I didn't ask why he thought it was low value.
### Orchestrating Concurrent (and distributed?) Programs
The point being to test them deterministically, and to test different
possible linearizations to look for concurrency bugs.
### Testing Generators, Regression Tests
Several related topics:
How do we validate that a generator is likely to generate certain kinds of things?
How could we collect stats about the sorts of things generated?
When we find a rare failing test, is there an easy way to ensure that such cases are generated more often?
Need to research other QC impls.
### Generator Syntax Sugar
We ended up adding gen/let for this, but test.chuck/for is still fancier and especially useful for tuples.
### Shrinking Topics
Check other QC impls for all of this.
#### Can we use more specialized generators to minimize the need for bind?
There was an example somewhere of trying to generate a 2d vector,
where doing it without bind seemed impossible but bind caused it to
shrink poorly.
Also see "Stateful Generators" elsewhere on this page.
#### Should shrink-resumption be part of the API?
#### Custom shrinking algorithms?
### Advanced Generators
#### "Stateful" Generators
There are common use cases for being able to generate sequences of
events, where each event can affect what future events are
possible. Perhaps the most natural is modeling a collection of records
with create/update/delete events, where you can only have an update or
delete for a record that already exists (and no updates on a deleted
record).
This can be done pretty easily with a recursive generator that uses
gen/bind at each step, but this comes at the huge expense of almost
entirely thwarting the shrinking process. It would be great to devise
some general way of constructing these sorts of generators that also
shrinks well. This seems easier for special cases like the record-set
case described above, but difficult in the general case.
##### Single Set of Independent Records
A specialized generator that takes as input args similar to
gen/hash-map (i.e., a description of what keys a record has and
generators for each of the keys) and shrinks reasonably well should be
possible to write, especially if there are no constraints beyond the
ordering of the create/update/delete for individual records.
There would be custom low-level generator code that avoids using
gen/bind, paired with custom shrinking code. The shrinking part might
only have to ensure that if a shrink removes a "create" event that it
deletes all subsequent events for that record. There might also have
to be a reified "id" that doesn't shrink or else shrinks all
references to an id in unison.
##### Full Relational Dataset
This would be like the previous example, but would involve multiple
datasets ("tables") and relationships between them ("foreign
keys"). The tricky part would be intelligent handling of foreign
keys. If a record's relative disappears, the shrinking code would
manually do cascading deletes when necessary (or perhaps divert
foreign keys to other records when that makes sense).
##### General Problem
The only natural framing of the general problem that I can think of is a generator such as:
(defn gen-events
"Given an init-state and a reduce function for determining the
current state from a sequence of events, together with a function
that takes a state and returns a generator of a new event, returns
a generator of sequences of events."
[reduce-func init-state state->event-gen]
...)
It seems difficult to find a general way to layer smart shrinking on
top of this sort of API, since any change to earlier events could
potentially require arbitrary changes to future events, and I'm not
sure how to build in a way of allowing the user to specify those
changes.
One idea is to take an additional argument, a function that receives
an event that was previously generated and a new state that is the
precursor to that event. The function can choose to leave the event
as-is (if it's still valid in the new state), apply return a function
suitable for use with fmap (if the event can be easily adjusted to the
new state), or signal that the event should be removed altogether. I
think this should be sufficient for implementing the two dataset
examples above, but I'm not sure whether it works for more complex
uses.
### Breaking Changes Fantasy
If we decided to make a large breaking-changes release, what would we include?
Rename the pos-int/s-pos-int style functions
Change the arg order for bind? It's awkward that it's opposite fmap
Change the multi-clause behavior of prop/for-all?
Currently can't integrate test.chuck/for w/ prop/for-all support without breaking this
### Old/Obsolete Notes
Things that are done or decided against.
#### Immutable RNG
I think we need to do some more performance checks. Ideas for things to look into:
How does linear generation with java.util.SplittableRandom compare to JUR and IJUSR? Knowing this should give us an idea if the slowdown with IJUSR is more about the algorithm or immutability
Seems to be entirely about immutability. E.g., JUSR is actually slightly faster with JUR, even after removing concurrency from JUR.
Can we squeeze any extra performance by using a more specialized API anywhere?
Like split-n
This is looking like the most promising
Can we generate batches of numbers faster than the more general splitting method? If so, can we take advantage of that cleanly in the generators?
E.g., you could linearly generate 256 longs, put them in an immutable array, and have the RNG reference a range of that array. rand-long returns the first number in the range, splitting returns two objects with the range cut in half. Splitting a singleton range triggers another array creation. I think there are still some subtleties to work out though.
It's also worth noting that the splittable API involves twice as many allocation as a linear-immutable API (see split-n idea above)
Is the macro usage subverting inlining?
Is the long->double->int process taking a while?
Try using an unrolled pair (ztellman style) for the return value from split
Some code for messing with this stuff is on this branch.
## Generators Reboot
This is a list of problems that can be addressed to some extent by
creating a new generators-2 namespace.
For each problem, there is a proposed solution with and without adding
a new namespace.
Under every "WITHOUT Reboot" section, there is an implicit alternative
to do nothing.
Many generators have confusing names
E.g., monadic names (return, fmap, bind), and others (elements, hash-map, one-of)
WITH Reboot
Pick better names
WITHOUT Reboot
Add aliases and mark the old names as deprecated
Difference between a var referring to a generator vs a function that returns a generator can be error-prone for users, and leads to a duplication of names like gen/double and gen/double* to allow for options to be passed
WITH Reboot
Make every var a function that returns a generator
WITHOUT Reboot
Add an IGenerator protocol so that vars like gen/double can be backwards-compatibly changed to functions that can act as generators in a deprecated manner (with optional warnings?)
Options API is inconsistent
Some functions take an options map, others have similar options as positional args
WITH Reboot
every generator should take an options map, as an optional argument after all of the required args
WITHOUT Reboot
Modify signatures with backwards compatible support for the deprecated signatures
Built-in composite generators like gen/any do not allow customizing the underlying base generators – most notably, you cannot ask gen/any to avoid generating NaNs (but with more options on other base generators this would be a more general problem)
WITH Reboot
These generators would be functions and could therefore pass options down to their constituent generators
WITHOUT Reboot
Do the "WITHOUT Reboot" steps in the previous two points, and then do the same thing we would do in a reboot
Integer generators need overhauling
There are a lot and some are misleadingly named. They could all be collapsed into a single generator with rich options for specifying the distribution
WITH Reboot
Make a single gen2/integer
WITHOUT Reboot
Make gen2/integer and deprecate all the others
gen/bind has confusing argument order
Every other combinator takes the generator as its last argument
WITHOUT Reboot
Accept args in either order? This could be ambiguous if we also convert everything to functions but allow the functions to be used as generators
WITH Reboot
Change the arg order in the new function
String generators are pretty basic
the non-ascii generators use a fixed small range of non-ASCII unicode characters
WITHOUT Reboot
Add a new string generator that takes options for distribution, deprecate all the old ones
WITH Reboot
Add a new string generator that takes options
Collection sizing is bad
Collection generations naïvely result in the expected size of the generated value being N times larger than the expected size from the element generator
A better situation would perhaps involve
Collection generators that reduce the `size` used for their elements according to some rule, perhaps based on the expected size of the collection
Some sort of in-place accounting for how much stuff is being generated as it goes, so that the size can be suppressed further if the value is getting too big, mitigating the risk of the largest 0.001% of values being OOMly large
Standard options for users to have more control of the above behavior
WITHOUT Reboot
Change built-in generators in place to effect the above solution (should this be considered a breaking change, if old calls still work but perhaps generate different distributions?)
Create new collection generators according to the above solution and deprecate the old ones
WITH Reboot
New collection generators according to the above solution
## quickcheck refactoring
This is a hopefully temporary page, tracking evaluation of [Nico's
proposed refactoring](https://clojure.atlassian.net/browse/TCHECK-126)
and any alternatives and related issues.
Organized as (nested) list of questions.
What problem are we trying to solve?
A general change to allow an implementation of a variety of useful features (in test.check directly or by users/libraries):
async tests
statistics
time-bounded runs, time-bounded shrinks
more speculatively, pausing and resuming shrinks
Note that we shouldn't assume we have to solve all these problems with the same changes, but if a change simultaneously enables all of them, that could be considered evidence in its favor
How do other quickchecks do any of those things?
At least
Hedgehog
Proper
Optionally
Quickcheck
Hypothesis
Nico's solution
My general concern is that it changes the public API, so we have to get it right, and it seems to allow a lot of undefined/strange things, since the user's supplied function could return whatever it wants; e.g., it could return a structure that's inconsistent or sends the algorithm to an unexpected state
I'm also wondering how it composes; e.g., if the stats library provides one implementation of the state transition function, and the user provides another for limiting shrink time, how do you combine those functions together? does `comp` work? is it commutative?
clojure-test.check-b2ec872/pom.xml 0000664 0000000 0000000 00000003143 14151735222 0017144 0 ustar 00root root 0000000 0000000
4.0.0
test.check
1.1.1
test.check
A QuickCheck inspired property-based testing library
https://github.com/clojure/build.poms
org.clojure
pom.contrib
1.1.0
Eclipse Public License 1.0
http://opensource.org/licenses/eclipse-1.0.php
repo
reiddraper
Reid Draper
https://github.com/reiddraper
1.9.0
true
org.clojure
clojurescript
1.10.520
test
scm:git:git://github.com/clojure/test.check.git
scm:git:git://github.com/clojure/test.check.git
http://github.com/clojure/test.check
v1.1.1
clojure-test.check-b2ec872/project.clj 0000664 0000000 0000000 00000010676 14151735222 0020000 0 ustar 00root root 0000000 0000000 (defproject org.clojure/test.check "0.11.0-SNAPSHOT"
:description "A QuickCheck inspired property-based testing library."
:url "https://github.com/clojure/test.check"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies []
:source-paths ["src/main/clojure"]
:test-paths ["src/test/clojure"]
:jvm-opts ^:replace ["-Xmx512m" "-server"]
:profiles {:dev {:dependencies [[org.clojure/clojure "1.10.1"]
[org.clojure/clojurescript "1.10.520"]]}
:self-host {:dependencies [[org.clojure/clojure "1.10.1"]
[org.clojure/clojurescript "1.10.520"]]
:main clojure.main
:global-vars {*warn-on-reflection* false}}}
:global-vars {*warn-on-reflection* true}
:plugins [[lein-codox "0.10.7"]
[lein-cljsbuild "1.1.5"]]
;; To generate codox files (which are hosted on the gh-pages branch)
;; for a release:
;;
;; 1) if the current content should be preserved as documentation
;; for an old release, first copy it to a subdirectory and
;; add a link in doc/api-docs-for-older-versions.md (on the
;; master branch, I guess, though that won't help when building
;; from an older tag, so you may also manually add that change
;; to the api-docs-for-older-versions.html file on the gh-pages
;; branch as well, which is no fun)
;; 2) checkout the tagged git commit
;; 3) tweak the project.clj version to match, since
;; jenkins only updates the pom.xml
;; 4) tweak the :source-uri entry below, replacing "master"
;; with the appropriate tag
;; 5) run `lein codox`
;; 6) copy target/doc into the gh-pages branch source tree, commit
;; and push; e.g., if you have the gh-pages branch open on a
;; worktree at gitignored/gh-pages, then this might work:
;; D1=target/doc
;; D2=gitignored/gh-pages
;; for file in $(ls $D1); do
;; if [[ -e $D2/$file ]]; then
;; rm -rf $D2/$file
;; fi
;; cp -r {$D1,$D2}/$file
;; done
;; 7) check the result at http://clojure.github.io/test.check/ ;
;; the source links should go to the correct tag on github
:codox {:namespaces [clojure.test.check
clojure.test.check.clojure-test
clojure.test.check.generators
clojure.test.check.properties
clojure.test.check.results]
:source-uri {#".*" "https://github.com/clojure/test.check/blob/master/{filepath}#L{line}"}
:doc-files ["doc/api-docs-for-older-versions.md"
"doc/cheatsheet.md"
"doc/generator-examples.md"
"doc/growth-and-shrinking.md"
"doc/intro.md"]}
:cljsbuild
{:builds
[{:id "node-dev"
:source-paths ["src/main/clojure" "src/test/clojure"
"src/target/cljs/node"]
:notify-command ["node" "test-runners/run.js"]
:compiler {:optimizations :none
:main clojure.test.check.test.runner
:static-fns true
:target :nodejs
:output-to "target/cljs/node_dev/tests.js"
:output-dir "target/cljs/node_dev/out"
:source-map true}}
{:id "browser-dev"
:source-paths ["src/main/clojure" "src/test/clojure"
"src/target/cljs/browser"]
:compiler {:optimizations :none
:static-fns true
:output-to "target/cljs/browser_dev/tests.js"
:output-dir "target/cljs/browser_dev/out"
:source-map true}}
{:id "node-adv"
:source-paths ["src/main/clojure" "src/test/clojure"
"src/target/cljs/node"]
:notify-command ["node" "target/cljs/node_adv/tests.js"]
:compiler {:optimizations :advanced
:main clojure.test.check.test.runner
:target :nodejs
:pretty-print false
:output-to "target/cljs/node_adv/tests.js"
:output-dir "target/cljs/node_adv/out"}}
{:id "browser-adv"
:source-paths ["src/main/clojure" "src/test/clojure"
"src/target/cljs/browser"]
:compiler {:optimizations :advanced
:pretty-print false
:output-to "target/cljs/browser_adv/tests.js"
:output-dir "target/cljs/browser_adv/out"}}]})
clojure-test.check-b2ec872/script/ 0000775 0000000 0000000 00000000000 14151735222 0017132 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/script/test-self-host 0000775 0000000 0000000 00000000261 14151735222 0021740 0 ustar 00root root 0000000 0000000 #!/bin/bash
rm -rf target/out-self-host
mkdir -p target/out-self-host/cljs/analyzer
lein with-profile self-host run script/test-self-host.clj
node target/out-self-host/main.js
clojure-test.check-b2ec872/script/test-self-host.clj 0000664 0000000 0000000 00000001131 14151735222 0022501 0 ustar 00root root 0000000 0000000 (require '[cljs.build.api]
'[clojure.java.io :as io])
(cljs.build.api/build "src/target/cljs/self-host"
{:main 'clojure.test.check.test.runner
:output-to "target/out-self-host/main.js"
:output-dir "target/out-self-host"
:target :nodejs
:cache-analysis-format :edn})
(defn copy-source
[filename]
(spit (str "target/out-self-host/" filename)
(slurp (io/resource filename))))
(copy-source "cljs/test.cljc")
(copy-source "cljs/analyzer/api.cljc")
(copy-source "cljs/reader.clj")
(copy-source "clojure/template.clj")
clojure-test.check-b2ec872/src/ 0000775 0000000 0000000 00000000000 14151735222 0016415 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/ 0000775 0000000 0000000 00000000000 14151735222 0017341 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0021004 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0022447 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/ 0000775 0000000 0000000 00000000000 14151735222 0023426 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/check.cljc 0000664 0000000 0000000 00000030056 14151735222 0025344 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check
(:require [clojure.test.check.generators :as gen]
[clojure.test.check.random :as random]
[clojure.test.check.results :as results]
[clojure.test.check.rose-tree :as rose]
[clojure.test.check.impl :refer [get-current-time-millis]]))
(declare shrink-loop failure)
(defn- make-rng
[seed]
(if seed
[seed (random/make-random seed)]
(let [non-nil-seed (get-current-time-millis)]
[non-nil-seed (random/make-random non-nil-seed)])))
(defn- complete
[property num-trials seed start-time reporter-fn]
(let [time-elapsed-ms (- (get-current-time-millis) start-time)]
(reporter-fn {:type :complete
:property property
:result true
:pass? true
:num-tests num-trials
:time-elapsed-ms time-elapsed-ms
:seed seed})
{:result true
:pass? true
:num-tests num-trials
:time-elapsed-ms time-elapsed-ms
:seed seed}))
(defn ^:private legacy-result
"Returns a value for the legacy :result key, which has the peculiar
property of conflating returned exceptions with thrown exceptions."
[result]
(if (satisfies? results/Result result)
(let [d (results/result-data result)]
(if-let [[_ e] (find d :clojure.test.check.properties/error)]
#?(:clj e
:cljs (if (instance? js/Error e)
e
(ex-info "Non-Error object thrown in test"
{}
e)))
(results/pass? result)))
result))
(defn quick-check
"Tests `property` `num-tests` times.
Takes several optional keys:
`:seed`
Can be used to re-run previous tests, as the seed used is returned
after a test is run.
`:max-size`.
can be used to control the 'size' of generated values. The size will
start at 0, and grow up to max-size, as the number of tests increases.
Generators will use the size parameter to bound their growth. This
prevents, for example, generating a five-thousand element vector on
the very first test.
`:reporter-fn`
A callback function that will be called at various points in the test
run, with a map like:
;; called after a passing trial
{:type :trial
:args [...]
:num-tests
:num-tests-total
:seed 42
:pass? true
:property #<...>
:result true
:result-data {...}}
;; called after the first failing trial
{:type :failure
:fail [...failing args...]
:failing-size 13
:num-tests
:pass? false
:property #<...>
:result false/exception
:result-data {...}
:seed 42}
It will also be called on :complete, :shrink-step and :shrunk. Many
of the keys also appear in the quick-check return value, and are
documented below.
If the test passes, the return value will be something like:
{:num-tests 100,
:pass? true,
:result true,
:seed 1561826505982,
:time-elapsed-ms 24}
If the test fails, the return value will be something like:
{:fail [0],
:failed-after-ms 0,
:failing-size 0,
:num-tests 1,
:pass? false,
:result false,
:result-data nil,
:seed 1561826506080,
:shrunk
{:depth 0,
:pass? false,
:result false,
:result-data nil,
:smallest [0],
:time-shrinking-ms 0,
:total-nodes-visited 0}}
The meaning of the individual entries is:
:num-tests
The total number of trials that was were run, not including
shrinking (if applicable)
:pass?
A boolean indicating whether the test passed or failed
:result
A legacy entry that is similar to :pass?
:seed
The seed used for the entire test run; can be used to reproduce
a test run by passing it as the :seed option to quick-check
:time-elapsed-ms
The total time, in milliseconds, of a successful test run
:fail
The generated values for the first failure; note that this is
always a vector, since prop/for-all can have multiple clauses
:failed-after-ms
The total time, in milliseconds, spent finding the first failing
trial
:failing-size
The value of the size parameter used to generate the first
failure
:result-data
The result data, if any, of the first failing trial (to take
advantage of this a property must return an object satisfying
the clojure.test.check.results/Result protocol)
:shrunk
A map of data about the shrinking process; nested keys that
appear at the top level have the same meaning; other keys are
documented next
:shrunk / :depth
The depth in the shrink tree that the smallest failing instance
was found; this is essentially the idea of how many times the
original failure was successfully shrunk
:smallest
The smallest values found in the shrinking process that still
fail the test; this is a vector of the same type as :fail
:time-shrinking-ms
The total time, in milliseconds, spent shrinking
:total-nodes-visited
The total number of steps in the shrinking process
Examples:
(def p (for-all [a gen/nat] (> (* a a) a)))
(quick-check 100 p)
(quick-check 200 p
:seed 42
:max-size 50
:reporter-fn (fn [m]
(when (= :failure (:type m))
(println \"Uh oh...\"))))"
[num-tests property & {:keys [seed max-size reporter-fn]
:or {max-size 200, reporter-fn (constantly nil)}}]
(let [[created-seed rng] (make-rng seed)
size-seq (gen/make-size-range-seq max-size)
start-time (get-current-time-millis)]
(loop [so-far 0
size-seq size-seq
rstate rng]
(if (== so-far num-tests)
(complete property num-tests created-seed start-time reporter-fn)
(let [[size & rest-size-seq] size-seq
[r1 r2] (random/split rstate)
result-map-rose (gen/call-gen property r1 size)
result-map (rose/root result-map-rose)
result (:result result-map)
args (:args result-map)
so-far (inc so-far)]
(if (results/pass? result)
(do
(reporter-fn {:type :trial
:args args
:num-tests so-far
:num-tests-total num-tests
:pass? true
:property property
:result result
:result-data (results/result-data result)
:seed seed})
(recur so-far rest-size-seq r2))
(failure property result-map-rose so-far size
created-seed start-time reporter-fn)))))))
(defn- smallest-shrink
[total-nodes-visited depth smallest start-time]
(let [{:keys [result]} smallest]
{:total-nodes-visited total-nodes-visited
:depth depth
:pass? false
:result (legacy-result result)
:result-data (results/result-data result)
:time-shrinking-ms (- (get-current-time-millis) start-time)
:smallest (:args smallest)}))
(defn- shrink-loop
"Shrinking a value produces a sequence of smaller values of the same type.
Each of these values can then be shrunk. Think of this as a tree. We do a
modified depth-first search of the tree:
Do a non-exhaustive search for a deeper (than the root) failing example.
Additional rules added to depth-first search:
* If a node passes the property, you may continue searching at this depth,
but not backtrack
* If a node fails the property, search its children
The value returned is the left-most failing example at the depth where a
passing example was found.
Calls reporter-fn on every shrink step."
[rose-tree reporter-fn]
(let [start-time (get-current-time-millis)
shrinks-this-depth (rose/children rose-tree)]
(loop [nodes shrinks-this-depth
current-smallest (rose/root rose-tree)
total-nodes-visited 0
depth 0]
(if (empty? nodes)
(smallest-shrink total-nodes-visited depth current-smallest start-time)
(let [;; can't destructure here because that could force
;; evaluation of (second nodes)
head (first nodes)
tail (rest nodes)
result (:result (rose/root head))
args (:args (rose/root head))
pass? (results/pass? result)
reporter-fn-arg {:type :shrink-step
:shrinking {:args args
:depth depth
:pass? (boolean pass?)
:result result
:result-data (results/result-data result)
:smallest (:args current-smallest)
:total-nodes-visited total-nodes-visited}}]
(if pass?
;; this node passed the test, so now try testing its right-siblings
(do
(reporter-fn reporter-fn-arg)
(recur tail current-smallest (inc total-nodes-visited) depth))
;; this node failed the test, so check if it has children,
;; if so, traverse down them. If not, save this as the best example
;; seen now and then look at the right-siblings
;; children
(let [new-smallest (rose/root head)]
(reporter-fn (assoc-in reporter-fn-arg
[:shrinking :smallest]
(:args new-smallest)))
(if-let [children (seq (rose/children head))]
(recur children new-smallest (inc total-nodes-visited) (inc depth))
(recur tail new-smallest (inc total-nodes-visited) depth)))))))))
(defn- failure
[property failing-rose-tree trial-number size seed start-time reporter-fn]
(let [failed-after-ms (- (get-current-time-millis) start-time)
root (rose/root failing-rose-tree)
result (:result root)
failure-data {:fail (:args root)
:failing-size size
:num-tests trial-number
:pass? false
:property property
:result (legacy-result result)
:result-data (results/result-data result)
:failed-after-ms failed-after-ms
:seed seed}]
(reporter-fn (assoc failure-data :type :failure))
(let [shrunk (shrink-loop failing-rose-tree
#(reporter-fn (merge failure-data %)))]
(reporter-fn (assoc failure-data
:type :shrunk
:shrunk shrunk))
(-> failure-data
(dissoc :property)
(assoc :shrunk shrunk)))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/ 0000775 0000000 0000000 00000000000 14151735222 0024503 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/clojure_test.cljc 0000664 0000000 0000000 00000021472 14151735222 0030050 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.clojure-test
(:require #?(:clj [clojure.test :as ct]
:cljs [cljs.test :as ct :include-macros true])
[clojure.test.check :as tc]
[clojure.test.check.clojure-test.assertions]
[clojure.test.check.impl :refer [get-current-time-millis]])
#?(:cljs (:require-macros [clojure.test.check.clojure-test :refer [defspec]])))
(defn assert-check
[{:keys [result result-data] :as m}]
(if-let [error (:clojure.test.check.properties/error result-data)]
(throw error)
(ct/is (clojure.test.check.clojure-test/check? m))))
(def ^:dynamic *default-test-count* 100)
(defn default-reporter-fn
"Default function passed as the :reporter-fn to clojure.test.check/quick-check.
Delegates to clojure.test/report."
[{:keys [type] :as args}]
(case type
:complete
(let [testing-vars #?(:clj ct/*testing-vars*
:cljs (:testing-vars ct/*current-env*))
params (merge (select-keys args [:result :num-tests :seed
:time-elapsed-ms])
(when (seq testing-vars)
{:test-var (-> testing-vars first meta :name name)}))]
(ct/report {:type :clojure.test.check.clojure-test/complete
:clojure.test.check.clojure-test/property (:property args)
:clojure.test.check.clojure-test/complete params}))
:trial
(ct/report {:type :clojure.test.check.clojure-test/trial
:clojure.test.check.clojure-test/property (:property args)
:clojure.test.check.clojure-test/trial [(:num-tests args)
(:num-tests-total args)]})
:failure
(ct/report {:type :clojure.test.check.clojure-test/shrinking
:clojure.test.check.clojure-test/property (:property args)
:clojure.test.check.clojure-test/params (vec (:fail args))})
:shrunk
(ct/report {:type :clojure.test.check.clojure-test/shrunk
:clojure.test.check.clojure-test/property (:property args)
:clojure.test.check.clojure-test/params (-> args :shrunk :smallest vec)})
nil))
(def ^:dynamic *default-opts*
"The default options passed to clojure.test.check/quick-check
by defspec."
{:reporter-fn default-reporter-fn})
(defn process-options
{:no-doc true}
[options]
(cond (nil? options) (merge {:num-tests *default-test-count*} *default-opts*)
(number? options) (assoc *default-opts* :num-tests options)
(map? options) (merge {:num-tests *default-test-count*}
*default-opts*
options)
:else (throw (ex-info (str "Invalid defspec options: " (pr-str options))
{:bad-options options}))))
(defmacro defspec
"Defines a new clojure.test test var that uses `quick-check` to verify the
property, running num-times trials by default. You can call the function defined as `name`
with no arguments to trigger this test directly (i.e., without starting a
wider clojure.test run). If called with arguments, the first argument is the number of
trials, optionally followed by keyword arguments as defined for `quick-check`."
{:arglists '([name property] [name num-tests? property] [name options? property])}
([name property] `(defspec ~name nil ~property))
([name options property]
`(defn ~(vary-meta name assoc
::defspec true
:test `(fn []
(clojure.test.check.clojure-test/assert-check
(assoc (~name) :test-var (str '~name)))))
{:arglists '([] ~'[num-tests & {:keys [seed max-size reporter-fn]}])}
([] (let [options# (process-options ~options)]
(apply ~name (:num-tests options#) (apply concat options#))))
([times# & {:as quick-check-opts#}]
(let [options# (merge (process-options ~options) quick-check-opts#)]
(apply
tc/quick-check
times#
(vary-meta ~property assoc :name '~name)
(apply concat options#)))))))
(def ^:dynamic *report-trials*
"Controls whether property trials should be reported via clojure.test/report.
Valid values include:
* false - no reporting of trials (default)
* a function - will be passed a clojure.test/report-style map containing
:clojure.test.check/property and :clojure.test.check/trial slots
* true - provides quickcheck-style trial reporting (dots) via
`trial-report-dots`
(Note that all reporting requires running `quick-check` within the scope of a
clojure.test run (via `test-ns`, `test-all-vars`, etc.))
Reporting functions offered by clojure.test.check include `trial-report-dots` and
`trial-report-periodic` (which prints more verbose trial progress information
every `*trial-report-period*` milliseconds)."
false)
(def ^:dynamic *report-shrinking*
"If true, a verbose report of the property being tested, the
failing return value, and the arguments provoking that failure is emitted
prior to the start of the shrinking search."
false)
(def ^:dynamic *trial-report-period*
"Milliseconds between reports emitted by `trial-report-periodic`."
10000)
(def ^:private last-trial-report (atom 0))
(defn- get-property-name
[{property-fun ::property :as report-map}]
(or (-> property-fun meta :name) (ct/testing-vars-str report-map)))
(defn with-test-out* [f]
#?(:clj (ct/with-test-out (f))
:cljs (f)))
(defn trial-report-periodic
"Intended to be bound as the value of `*report-trials*`; will emit a verbose
status every `*trial-report-period*` milliseconds, like this one:
Passing trial 3286 / 5000 for (your-test-var-name-here) (:)"
[m]
(let [t (get-current-time-millis)]
(when (> (- t *trial-report-period*) @last-trial-report)
(with-test-out*
(fn []
(println "Passing trial"
(-> m ::trial first) "/" (-> m ::trial second)
"for" (get-property-name m))))
(reset! last-trial-report t))))
(defn trial-report-dots
"Intended to be bound as the value of `*report-trials*`; will emit a single
dot every 1000 trials reported."
[{[so-far total] ::trial}]
(when (pos? so-far)
(when (zero? (mod so-far 1000))
(print ".")
(flush))
(when (== so-far total) (println))))
(def ^:dynamic *report-completion*
"If true, completed tests report test-var, num-tests and seed. Failed tests
report shrunk results. Defaults to true."
true)
(when #?(:clj true :cljs (not (and *ns* (re-matches #".*\$macros" (name (ns-name *ns*))))))
;; This check accomodates a number of tools that rebind ct/report
;; to be a regular function instead of a multimethod, and may do
;; so before this code is loaded (see TCHECK-125)
(if-not (instance? #?(:clj clojure.lang.MultiFn :cljs MultiFn) ct/report)
(binding [*out* #?(:clj *err* :cljs *out*)]
(println "clojure.test/report is not a multimethod, some reporting functions have been disabled."))
(let [begin-test-var-method (get-method ct/report #?(:clj :begin-test-var
:cljs [::ct/default :begin-test-var]))]
(defmethod ct/report #?(:clj :begin-test-var
:cljs [::ct/default :begin-test-var]) [m]
(reset! last-trial-report (get-current-time-millis))
(when begin-test-var-method (begin-test-var-method m)))
(defmethod ct/report #?(:clj ::trial :cljs [::ct/default ::trial]) [m]
(when-let [trial-report-fn (and *report-trials*
(if (true? *report-trials*)
trial-report-dots
*report-trials*))]
(trial-report-fn m)))
(defmethod ct/report #?(:clj ::shrinking :cljs [::ct/default ::shrinking]) [m]
(when *report-shrinking*
(with-test-out*
(fn []
(println "Shrinking" (get-property-name m)
"starting with parameters" (pr-str (::params m)))))))
(defmethod ct/report #?(:clj ::complete :cljs [::ct/default ::complete]) [m]
(when *report-completion*
(prn (::complete m))))
(defmethod ct/report #?(:clj ::shrunk :cljs [::ct/default ::shrunk]) [m]
(when *report-completion*
(with-test-out*
(fn [] (prn m))))))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/clojure_test/ 0000775 0000000 0000000 00000000000 14151735222 0027205 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/clojure_test/assertions.cljc 0000664 0000000 0000000 00000004143 14151735222 0032236 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey and contributors. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.clojure-test.assertions
#?(:cljs (:require-macros [clojure.test.check.clojure-test.assertions.cljs]))
(:require #?(:clj [clojure.test :as t]
:cljs [cljs.test :as t])))
#?(:clj
(defn test-context-stacktrace [st]
(drop-while
#(let [class-name (.getClassName ^StackTraceElement %)]
(or (.startsWith class-name "java.lang")
(.startsWith class-name "clojure.test$")
(.startsWith class-name "clojure.test.check.clojure_test$")
(.startsWith class-name "clojure.test.check.clojure_test.assertions")))
st)))
#?(:clj
(defn file-and-line*
[stacktrace]
(if (seq stacktrace)
(let [^StackTraceElement s (first stacktrace)]
{:file (.getFileName s) :line (.getLineNumber s)})
{:file nil :line nil})))
(defn check-results [m]
(if (:pass? m)
(t/do-report
{:type :pass
:message (dissoc m :result)})
(t/do-report
(merge {:type :fail
:expected {:result true}
:actual m}
#?(:clj (file-and-line*
(test-context-stacktrace (.getStackTrace (Thread/currentThread))))
:cljs (t/file-and-line (js/Error.) 4))))))
(defn check?
[_ form]
`(let [m# ~(nth form 1)]
(check-results m#)))
#?(:clj
(defmethod t/assert-expr 'clojure.test.check.clojure-test/check?
[_ form]
(check? _ form))
:cljs
(when (exists? js/cljs.test$macros)
(defmethod js/cljs.test$macros.assert_expr 'clojure.test.check.clojure-test/check?
[_ msg form]
(clojure.test.check.clojure-test.assertions/check? msg form))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/clojure_test/assertions/ 0000775 0000000 0000000 00000000000 14151735222 0031377 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/clojure_test/assertions/cljs.cljc 0000664 0000000 0000000 00000001521 14151735222 0033166 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey and contributors. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.clojure-test.assertions.cljs)
#?(:clj
(try
(require 'cljs.test
'[clojure.test.check.clojure-test.assertions :as assertions])
(eval
'(defmethod cljs.test/assert-expr 'clojure.test.check.clojure-test/check?
[_ msg form]
(assertions/check? msg form)))
(catch java.io.FileNotFoundException e)))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/generators.cljc 0000664 0000000 0000000 00000200742 14151735222 0027516 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.generators
(:refer-clojure :exclude [abs int vector list hash-map map keyword
char boolean byte bytes sequence
shuffle not-empty symbol namespace
set sorted-set uuid double let])
(:require [#?(:clj clojure.core :cljs cljs.core) :as core
#?@(:cljs [:include-macros true])]
[clojure.string :as string]
[clojure.test.check.random :as random]
[clojure.test.check.rose-tree :as rose]
#?@(:cljs [[goog.string :as gstring]
[clojure.string]]))
#?(:cljs (:require-macros [clojure.test.check.generators :refer [let]])))
;; Gen
;; (internal functions)
;; ---------------------------------------------------------------------------
(defrecord Generator [gen])
(defn generator?
"Test if `x` is a generator. Generators should be treated as opaque values."
[x]
(instance? Generator x))
(defn- make-gen
[generator-fn]
(Generator. generator-fn))
(defn call-gen
"Internal function."
{:no-doc true}
[{generator-fn :gen} rnd size]
(generator-fn rnd size))
(defn gen-pure
"Internal function."
{:no-doc true}
[value]
(make-gen
(fn [rnd size]
value)))
(defn gen-fmap
"Internal function."
{:no-doc true}
[k {h :gen}]
(make-gen
(fn [rnd size]
(k (h rnd size)))))
(defn gen-bind
"Internal function."
{:no-doc true}
[{h :gen} k]
(make-gen
(fn [rnd size]
(core/let [[r1 r2] (random/split rnd)
inner (h r1 size)
{result :gen} (k inner)]
(result r2 size)))))
(defn lazy-random-states
"Internal function.
Given a random number generator, returns an infinite lazy sequence
of random number generators."
{:no-doc true}
[rr]
(lazy-seq
(core/let [[r1 r2] (random/split rr)]
(cons r1
(lazy-random-states r2)))))
(defn- gen-tuple
"Takes a collection of generators and returns a generator of vectors."
[gens]
(make-gen
(fn [rnd size]
(mapv #(call-gen % %2 size) gens (random/split-n rnd (count gens))))))
;; Exported generator functions
;; ---------------------------------------------------------------------------
(defn fmap
"Returns a generator like `gen` but with values transformed by `f`.
E.g.:
(gen/sample (gen/fmap str gen/nat))
=> (\"0\" \"1\" \"0\" \"1\" \"4\" \"3\" \"6\" \"6\" \"4\" \"2\")
Also see gen/let for a macro with similar functionality."
[f gen]
(assert (generator? gen) "Second arg to fmap must be a generator")
(gen-fmap #(rose/fmap f %) gen))
(defn return
"Creates a generator that always returns `value`,
and never shrinks. You can think of this as
the `constantly` of generators. E.g.:
(gen/sample (gen/return 42))
=> (42 42 42 42 42 42 42 42 42 42)"
[value]
(gen-pure (rose/pure value)))
(defn- bind-helper
[f]
(fn [rose]
(gen-fmap rose/join
(make-gen
(fn [rnd size]
(rose/fmap #(call-gen (f %) rnd size)
rose))))))
(defn bind
"Creates a new generator that passes the result of `gen` into function
`f`. `f` should return a new generator. This allows you to create new
generators that depend on the value of other generators. For example,
to create a generator of permutations which first generates a
`num-elements` and then generates a shuffling of `(range num-elements)`:
(gen/bind gen/nat
;; this function takes a value generated by
;; the generator above and returns a new generator
;; which shuffles the collection returned by `range`
(fn [num-elements]
(gen/shuffle (range num-elements))))
Also see gen/let for a macro with similar functionality."
[generator f]
(assert (generator? generator) "First arg to bind must be a generator")
(gen-bind generator (bind-helper f)))
;; Helpers
;; ---------------------------------------------------------------------------
(defn make-size-range-seq
"Internal function."
{:no-doc true}
[max-size]
(cycle (range 0 max-size)))
(defn sample-seq
"Returns an infinite sequence of realized values from `generator`.
Note that this function is a dev helper and is not meant to be used
to build other generators."
([generator] (sample-seq generator 200))
([generator max-size]
(core/let [r (random/make-random)
size-seq (make-size-range-seq max-size)]
(core/map #(rose/root (call-gen generator %1 %2))
(lazy-random-states r)
size-seq))))
(defn sample
"Return a sequence of `num-samples` (default 10)
realized values from `generator`.
The sequence starts with small values from the generator, which
probably do not reflect the variety of values that will be generated
during a longer test run.
Note that this function is a dev helper and is not meant to be used
to build other generators."
([generator]
(sample generator 10))
([generator num-samples]
(assert (generator? generator) "First arg to sample must be a generator")
(take num-samples (sample-seq generator))))
(defn generate
"Returns a single sample value from the generator.
Note that this function is a dev helper and is not meant to be used
to build other generators.
Optional args:
- size: the abstract size parameter, defaults to 30
- seed: the seed for the random number generator, an integer"
{:added "0.8.0"}
([generator]
(generate generator 30))
([generator size]
(core/let [rng (random/make-random)]
(rose/root (call-gen generator rng size))))
([generator size seed]
(core/let [rng (random/make-random seed)]
(rose/root (call-gen generator rng size)))))
;; Internal Helpers
;; ---------------------------------------------------------------------------
(defn- halfs
[n]
(take-while #(not= 0 %) (iterate #(quot % 2) n)))
(defn- shrink-int
[integer]
(core/map #(- integer %) (halfs integer)))
(defn- int-rose-tree
[value]
(rose/make-rose value (core/map int-rose-tree (shrink-int value))))
;; calc-long is factored out to support testing the surprisingly tricky double math. Note:
;; An extreme long value does not have a precision-preserving representation as a double.
;; Be careful about changing this code unless you understand what's happening in these
;; examples:
;;
;; (= (long (- Integer/MAX_VALUE (double (- Integer/MAX_VALUE 10)))) 10)
;; (= (long (- Long/MAX_VALUE (double (- Long/MAX_VALUE 10)))) 0)
(defn- calc-long
[factor lower upper]
;; these pre- and post-conditions are disabled for deployment
#_ {:pre [(float? factor) (>= factor 0.0) (< factor 1.0)
(integer? lower) (integer? upper) (<= lower upper)]
:post [(integer? %)]}
;; Use -' on width to maintain accuracy with overflow protection.
#?(:clj
(core/let [width (-' upper lower -1)]
;; Preserve long precision if the width is in the long range. Otherwise, we must accept
;; less precision because doubles don't have enough bits to preserve long equivalence at
;; extreme values.
(if (< width Long/MAX_VALUE)
(+ lower (long (Math/floor (* factor width))))
;; Clamp down to upper because double math.
(min upper (long (Math/floor (+ lower (* factor width)))))))
:cljs
(long (Math/floor (+ lower (- (* factor (+ 1.0 upper))
(* factor lower)))))))
(defn- rand-range
[rnd lower upper]
{:pre [(<= lower upper)]}
(calc-long (random/rand-double rnd) lower upper))
(defn sized
"Creates a generator that depends on the size parameter.
`sized-gen` is a function that takes an integer and returns
a generator.
Examples:
;; generates vectors of booleans where the length always exactly
;; matches the `size` parameter
(gen/sample (gen/sized (fn [size] (gen/vector gen/boolean size))))
=> ([]
[false]
[true true]
[false true false]
[false true true true]
[false false true true false]
[false true false true true false]
[true false true true true false false]
[true true false false false true false false]
[false false false true true false true false true])"
[sized-gen]
(make-gen
(fn [rnd size]
(core/let [sized-gen (sized-gen size)]
(call-gen sized-gen rnd size)))))
;; Combinators and helpers
;; ---------------------------------------------------------------------------
(defn resize
"Creates a new generator with `size` always bound to `n`.
(gen/sample (gen/set (gen/resize 200 gen/double)))
=> (#{}
#{-4.994772362980037E147}
#{-4.234418056487335E-146}
#{}
#{}
#{}
#{NaN}
#{8.142414100982609E-63}
#{-3.58429955903876E-159 2.8563794617604296E-154
4.1021360195776005E-100 1.9084564045332549E-38}
#{-2.1582818131881376E83 -5.8460065493236117E48 9.729260993803226E166})"
[n generator]
(assert (generator? generator) "Second arg to resize must be a generator")
(core/let [{:keys [gen]} generator]
(make-gen
(fn [rnd _size]
(gen rnd n)))))
(defn scale
"Creates a new generator that modifies the size parameter by the
given function. Intended to support generators with sizes that need
to grow at different rates compared to the normal linear scaling.
(gen/sample (gen/tuple (gen/scale #(/ % 10) gen/nat)
gen/nat
(gen/scale #(* % 10) gen/nat)))
=> ([0 0 0] [0 1 2] [0 2 13] [0 1 6] [0 1 23]
[0 2 42] [0 1 26] [0 1 12] [0 1 12] [0 0 3])"
{:added "0.8.0"}
([f generator]
(sized (fn [n] (resize (f n) generator)))))
(defn choose
#?(:clj
"Creates a generator that generates integers uniformly in the range
`lower` to `upper`, inclusive.
(gen/sample (gen/choose 200 800))
=> (331 241 593 339 643 718 688 473 247 694)"
:cljs
"Creates a generator that generates integer numbers uniformly in
the range `lower` to `upper`, inclusive.
(gen/sample (gen/choose 200 800))
=> (331 241 593 339 643 718 688 473 247 694)")
[lower upper]
;; cast to long to support doubles as arguments per TCHECK-73
(core/let #?(:clj
[lower (long lower)
upper (long upper)]
:cljs ;; does nothing, no long in cljs
[])
(make-gen
(fn [rnd _size]
(core/let [value (rand-range rnd lower upper)]
(rose/filter
#(and (>= % lower) (<= % upper))
(int-rose-tree value)))))))
(defn one-of
"Creates a generator that randomly chooses a value from the list of
provided generators. Shrinks toward choosing an earlier generator,
as well as shrinking the value generated by the chosen generator.
(gen/sample (gen/one-of [gen/small-integer gen/boolean (gen/vector gen/small-integer)]))
=> (true [] -1 [0] [1 -4 -4 1] true 4 [] 6 true)"
[generators]
(assert (every? generator? generators)
"Arg to one-of must be a collection of generators")
(assert (seq generators)
"one-of cannot be called with an empty collection")
(bind (choose 0 (dec (count generators)))
#(nth generators %)))
(defn- pick
"Returns an index into the `likelihoods` sequence."
[likelihoods n]
(->> likelihoods
(reductions + 0)
(rest)
(take-while #(<= % n))
(count)))
(defn frequency
"Creates a generator that chooses a generator from `pairs` based on the
provided likelihoods. The likelihood of a given generator being chosen is
its likelihood divided by the sum of all likelihoods. Shrinks toward
choosing an earlier generator, as well as shrinking the value generated
by the chosen generator.
Examples:
(gen/sample (gen/frequency [[5 gen/small-integer] [3 (gen/vector gen/small-integer)] [2 gen/boolean]]))
=> (true [] -1 [0] [1 -4 -4 1] true 4 [] 6 true)"
[pairs]
(assert (every? (fn [[x g]] (and (number? x) (generator? g)))
pairs)
"Arg to frequency must be a list of [num generator] pairs")
(core/let [pairs (filter (comp pos? first) pairs)
total (apply + (core/map first pairs))]
(assert (seq pairs)
"frequency must be called with at least one non-zero weight")
;; low-level impl for shrinking control
(make-gen
(fn [rnd size]
(call-gen
(gen-bind (choose 0 (dec total))
(fn [x]
(core/let [idx (pick (core/map first pairs) (rose/root x))]
(gen-fmap (fn [rose]
(rose/make-rose (rose/root rose)
(lazy-seq
(concat
;; try to shrink to earlier generators first
(for [idx (range idx)]
(call-gen (second (nth pairs idx))
rnd
size))
(rose/children rose)))))
(second (nth pairs idx))))))
rnd size)))))
(defn elements
"Creates a generator that randomly chooses an element from `coll`.
(gen/sample (gen/elements [:foo :bar :baz]))
=> (:foo :baz :baz :bar :foo :foo :bar :bar :foo :bar)"
[coll]
(assert (seq coll) "elements cannot be called with an empty collection")
(core/let [v (vec coll)]
(gen-fmap #(rose/fmap v %)
(choose 0 (dec (count v))))))
(defn- such-that-helper
[pred gen {:keys [ex-fn max-tries]} rng size]
(loop [tries-left max-tries
rng rng
size size]
(if (zero? tries-left)
(throw (ex-fn {:pred pred, :gen, gen :max-tries max-tries}))
(core/let [[r1 r2] (random/split rng)
value (call-gen gen r1 size)]
(if (pred (rose/root value))
(rose/filter pred value)
(recur (dec tries-left) r2 (inc size)))))))
(def ^:private
default-such-that-opts
{:ex-fn (fn [{:keys [max-tries] :as arg}]
(ex-info (str "Couldn't satisfy such-that predicate after "
max-tries " tries.")
arg))
:max-tries 10})
(defn such-that
"Creates a generator that generates values from `gen` that satisfy predicate
`pred`. Care is needed to ensure there is a high chance `gen` will satisfy
`pred`. By default, `such-that` will try 10 times to generate a value that
satisfies the predicate. If no value passes this predicate after this number
of iterations, a runtime exception will be thrown. Note also that each
time such-that retries, it will increase the size parameter.
Examples:
;; generate non-empty vectors of integers
;; (note, gen/not-empty does exactly this)
(gen/such-that not-empty (gen/vector gen/small-integer))
You can customize `such-that` by passing an optional third argument, which can
either be an integer representing the maximum number of times test.check
will try to generate a value matching the predicate, or a map:
:max-tries positive integer, the maximum number of tries (default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate a matching value; it will be passed a map with `:gen`,
`:pred`, and `:max-tries` and should return an exception"
([pred gen]
(such-that pred gen 10))
([pred gen max-tries-or-opts]
(core/let [opts (cond (integer? max-tries-or-opts)
{:max-tries max-tries-or-opts}
(map? max-tries-or-opts)
max-tries-or-opts
:else
(throw (ex-info "Bad argument to such-that!" {:max-tries-or-opts
max-tries-or-opts})))
opts (merge default-such-that-opts opts)]
(assert (generator? gen) "Second arg to such-that must be a generator")
(make-gen
(fn [rand-seed size]
(such-that-helper pred gen opts rand-seed size))))))
(defn not-empty
"Modifies a generator so that it doesn't generate empty collections.
Examples:
;; generate a vector of booleans, but never the empty vector
(gen/sample (gen/not-empty (gen/vector gen/boolean)))
=> ([false]
[false false]
[false false]
[false false false]
[false false false false]
[false true true]
[true false false false]
[true]
[true true true false false true false]
[false true true true false true true true false])"
[gen]
(assert (generator? gen) "Arg to not-empty must be a generator")
(such-that core/not-empty gen))
(defn no-shrink
"Creates a new generator that is just like `gen`, except does not shrink
at all. This can be useful when shrinking is taking a long time or is not
applicable to the domain."
[gen]
(assert (generator? gen) "Arg to no-shrink must be a generator")
(gen-fmap (fn [rose]
(rose/make-rose (rose/root rose) []))
gen))
(defn shrink-2
"Creates a new generator like `gen`, but will consider nodes for shrinking
even if their parent passes the test (up to one additional level)."
[gen]
(assert (generator? gen) "Arg to shrink-2 must be a generator")
(gen-fmap rose/collapse gen))
(def boolean
"Generates one of `true` or `false`. Shrinks to `false`."
(elements [false true]))
(defn tuple
"Creates a generator that returns a vector, whose elements are chosen
from the generators in the same position. The individual elements shrink
according to their generator, but the vector will never shrink in count.
Examples:
(def t (gen/tuple gen/small-integer gen/boolean))
(sample t)
;; => ([1 true] [2 true] [2 false] [1 false] [0 true] [-2 false] [-6 false]
;; => [3 true] [-4 false] [9 true]))"
[& generators]
(assert (every? generator? generators)
"Args to tuple must be generators")
(gen-fmap (fn [roses]
(rose/zip core/vector roses))
(gen-tuple generators)))
(def nat
"Generates non-negative integers bounded by the generator's `size`
parameter. Shrinks to zero."
(sized (fn [size] (choose 0 size)))
#_
(fmap #(Math/abs (long %)) int))
(def ^{:added "0.10.0"} small-integer
"Generates a positive or negative integer bounded by the generator's
`size` parameter. Shrinks to zero."
(sized (fn [size] (choose (- size) size))))
;; The following five are deprecated due to being confusingly named,
;; and in some cases not being very useful.
(def ^{:deprecated "0.10.0"} int
"Deprecated - use gen/small-integer instead.
Generates a positive or negative integer bounded by the generator's
`size` parameter."
small-integer)
(def ^{:deprecated "0.10.0"} pos-int
"Deprecated - use gen/nat instead (see also gen/large-integer).
(this generator, despite its name, can generate 0)
Generates nonnegative integers bounded by the generator's `size` parameter."
nat)
(def ^{:deprecated "0.10.0"} neg-int
"Deprecated - use (gen/fmap - gen/nat) instead (see also gen/large-integer).
(this generator, despite its name, can generate 0)
Generates nonpositive integers bounded by the generator's `size` parameter."
(fmap #(* -1 %) nat))
(def ^{:deprecated "0.10.0"} s-pos-int
"Deprecated - use (gen/fmap inc gen/nat) instead (see also gen/large-integer).
Generates positive integers bounded by the generator's `size` + 1"
(fmap inc nat))
(def ^{:deprecated "0.10.0"} s-neg-int
"Deprecated - use (gen/fmap (comp dec -) gen/nat) instead (see also gen/large-integer).
Generates negative integers bounded by the generator's `size` + 1"
(fmap dec neg-int))
(defn vector
"Creates a generator of vectors whose elements are chosen from
`generator`. The count of the vector will be bounded by the `size`
generator parameter."
([generator]
(assert (generator? generator) "Arg to vector must be a generator")
(gen-bind
(sized #(choose 0 %))
(fn [num-elements-rose]
(gen-fmap (fn [roses]
(rose/shrink-vector core/vector
roses))
(gen-tuple (repeat (rose/root num-elements-rose)
generator))))))
([generator num-elements]
(assert (generator? generator) "First arg to vector must be a generator")
(apply tuple (repeat num-elements generator)))
([generator min-elements max-elements]
(assert (generator? generator) "First arg to vector must be a generator")
(gen-bind
(choose min-elements max-elements)
(fn [num-elements-rose]
(gen-fmap (fn [roses]
(rose/filter
(fn [v] (and (>= (count v) min-elements)
(<= (count v) max-elements)))
(rose/shrink-vector core/vector
roses)))
(gen-tuple (repeat (rose/root num-elements-rose)
generator)))))))
(defn list
"Like `vector`, but generates lists."
[generator]
(assert (generator? generator) "First arg to list must be a generator")
(gen-bind (sized #(choose 0 %))
(fn [num-elements-rose]
(gen-fmap (fn [roses]
(rose/shrink-vector core/list
roses))
(gen-tuple (repeat (rose/root num-elements-rose)
generator))))))
(defn- swap
[coll [i1 i2]]
(assoc coll i2 (coll i1) i1 (coll i2)))
(defn
^{:added "0.6.0"}
shuffle
"Creates a generator that generates random permutations of
`coll`. Shrinks toward the original collection: `coll`. `coll` will
be coerced to a vector."
[coll]
(core/let [coll (if (vector? coll) coll (vec coll))
index-gen (choose 0 (dec (count coll)))]
(fmap #(reduce swap coll %)
;; a vector of swap instructions, with count between
;; zero and 2 * count. This means that the average number
;; of instructions is count, which should provide sufficient
;; (though perhaps not 'perfect') shuffling. This still gives us
;; nice, relatively quick shrinks.
(vector (tuple index-gen index-gen) 0 (* 2 (count coll))))))
;; NOTE cljs: Comment out for now - David
#?(:clj
(def byte
"Generates `java.lang.Byte`s, using the full byte-range."
(fmap core/byte (choose Byte/MIN_VALUE Byte/MAX_VALUE))))
#?(:clj
(def bytes
"Generates byte-arrays."
(fmap core/byte-array (vector byte))))
(defn hash-map
"Like clojure.core/hash-map, except the values are generators.
Returns a generator that makes maps with the supplied keys and
values generated using the supplied generators.
(gen/sample (gen/hash-map :a gen/boolean :b gen/nat))
=> ({:a false, :b 0}
{:a true, :b 1}
{:a false, :b 2}
{:a true, :b 2}
{:a false, :b 4}
{:a false, :b 2}
{:a true, :b 3}
{:a true, :b 4}
{:a false, :b 1}
{:a false, :b 0})"
[& kvs]
(assert (even? (count kvs)))
(core/let [ks (take-nth 2 kvs)
vs (take-nth 2 (rest kvs))]
(assert (every? generator? vs)
"Value args to hash-map must be generators")
(fmap #(zipmap ks %)
(apply tuple vs))))
;; Collections of distinct elements
;; (has to be done in a low-level way (instead of with combinators)
;; and is subject to the same kind of failure as such-that)
;; ---------------------------------------------------------------------------
(defn ^:private transient-set-contains?
[s k]
#? (:clj
(.contains ^clojure.lang.ITransientSet s k)
:cljs
(some? (-lookup s k))))
(defn ^:private coll-distinct-by*
"Returns a rose tree."
[empty-coll key-fn shuffle-fn gen rng size num-elements min-elements max-tries ex-fn]
{:pre [gen (:gen gen)]}
(loop [rose-trees (transient [])
s (transient #{})
rng rng
size size
tries 0]
(cond (and (= max-tries tries)
(< (count rose-trees) min-elements))
(throw (ex-fn {:gen gen
:max-tries max-tries
:num-elements num-elements}))
(or (= max-tries tries)
(= (count rose-trees) num-elements))
(->> (persistent! rose-trees)
;; we shuffle the rose trees so that we aren't biased
;; toward generating "smaller" elements earlier in the
;; collection (only applies to ordered collections)
;;
;; shuffling the rose trees is more efficient than
;; (bind ... shuffle) because we only perform the
;; shuffling once and we have no need to shrink the
;; shufling.
(shuffle-fn rng)
(rose/shrink-vector #(into empty-coll %&)))
:else
(core/let [[rng1 rng2] (random/split rng)
rose (call-gen gen rng1 size)
root (rose/root rose)
k (key-fn root)]
(if (transient-set-contains? s k)
(recur rose-trees s rng2 (inc size) (inc tries))
(recur (conj! rose-trees rose)
(conj! s k)
rng2
size
0))))))
(defn ^:private distinct-by?
"Like clojure.core/distinct? but takes a collection instead of varargs,
and returns true for empty collections."
[f coll]
(or (empty? coll)
(apply distinct? (core/map f coll))))
(defn ^:private the-shuffle-fn
"Returns a shuffled version of coll according to the rng.
Note that this is not a generator, it is just a utility function."
[rng coll]
(core/let [empty-coll (empty coll)
v (vec coll)
card (count coll)
dec-card (dec card)]
(into empty-coll
(first
(reduce (fn [[v rng] idx]
(core/let [[rng1 rng2] (random/split rng)
swap-idx (rand-range rng1 idx dec-card)]
[(swap v [idx swap-idx]) rng2]))
[v rng]
(range card))))))
(defn ^:private coll-distinct-by
[empty-coll key-fn allows-dupes? ordered? gen
{:keys [num-elements min-elements max-elements max-tries ex-fn]
:or {max-tries 10
ex-fn #(ex-info "Couldn't generate enough distinct elements!" %)}}]
(core/let [shuffle-fn (if ordered?
the-shuffle-fn
(fn [_rng coll] coll))
hard-min-elements (or num-elements min-elements 1)]
(if num-elements
(core/let [size-pred #(= num-elements (count %))]
(assert (and (nil? min-elements) (nil? max-elements)))
(make-gen
(fn [rng gen-size]
(rose/filter
(if allows-dupes?
;; is there a smarter way to do the shrinking than checking
;; the distinctness of the entire collection at each
;; step?
(every-pred size-pred #(distinct-by? key-fn %))
size-pred)
(coll-distinct-by* empty-coll key-fn shuffle-fn gen rng gen-size
num-elements hard-min-elements max-tries ex-fn)))))
(core/let [min-elements (or min-elements 0)
size-pred (if max-elements
#(<= min-elements (count %) max-elements)
#(<= min-elements (count %)))]
(gen-bind
(if max-elements
(choose min-elements max-elements)
(sized #(choose min-elements (+ min-elements %))))
(fn [num-elements-rose]
(core/let [num-elements (rose/root num-elements-rose)]
(make-gen
(fn [rng gen-size]
(rose/filter
(if allows-dupes?
;; same comment as above
(every-pred size-pred #(distinct-by? key-fn %))
size-pred)
(coll-distinct-by* empty-coll key-fn shuffle-fn gen rng gen-size
num-elements hard-min-elements max-tries ex-fn)))))))))))
;; I tried to reduce the duplication in these docstrings with a macro,
;; but couldn't make it work in cljs.
(defn vector-distinct
"Generates a vector of elements from the given generator, with the
guarantee that the elements will be distinct.
If the generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated vectors
:min-elements the min size of generated vectors
:max-elements the max size of generated vectors
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct values; it will be passed a map with
`:gen`, `:num-elements`, and `:max-tries` and should return an
exception"
{:added "0.9.0"}
([gen] (vector-distinct gen {}))
([gen opts]
(assert (generator? gen) "First arg to vector-distinct must be a generator!")
(coll-distinct-by [] identity true true gen opts)))
(defn list-distinct
"Generates a list of elements from the given generator, with the
guarantee that the elements will be distinct.
If the generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated list
:min-elements the min size of generated list
:max-elements the max size of generated list
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct values; it will be passed a map with
`:gen`, `:num-elements`, and `:max-tries` and should return an
exception"
{:added "0.9.0"}
([gen] (list-distinct gen {}))
([gen opts]
(assert (generator? gen) "First arg to list-distinct must be a generator!")
(coll-distinct-by () identity true true gen opts)))
(defn vector-distinct-by
"Generates a vector of elements from the given generator, with the
guarantee that (map key-fn the-vector) will be distinct.
If the generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated vectors
:min-elements the min size of generated vectors
:max-elements the max size of generated vectors
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct values; it will be passed a map with
`:gen`, `:num-elements`, and `:max-tries` and should return an
exception"
{:added "0.9.0"}
([key-fn gen] (vector-distinct-by key-fn gen {}))
([key-fn gen opts]
(assert (generator? gen) "Second arg to vector-distinct-by must be a generator!")
(coll-distinct-by [] key-fn true true gen opts)))
(defn list-distinct-by
"Generates a list of elements from the given generator, with the
guarantee that (map key-fn the-list) will be distinct.
If the generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated list
:min-elements the min size of generated list
:max-elements the max size of generated list
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct values; it will be passed a map with
`:gen`, `:num-elements`, and `:max-tries` and should return an
exception"
{:added "0.9.0"}
([key-fn gen] (list-distinct-by key-fn gen {}))
([key-fn gen opts]
(assert (generator? gen) "Second arg to list-distinct-by must be a generator!")
(coll-distinct-by () key-fn true true gen opts)))
(defn set
"Generates a set of elements from the given generator.
If the generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated set
:min-elements the min size of generated set
:max-elements the max size of generated set
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct values; it will be passed a map with
`:gen`, `:num-elements`, and `:max-tries` and should return an
exception"
{:added "0.9.0"}
([gen] (set gen {}))
([gen opts]
(assert (generator? gen) "First arg to set must be a generator!")
(coll-distinct-by #{} identity false false gen opts)))
(defn sorted-set
"Generates a sorted set of elements from the given generator.
If the generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated set
:min-elements the min size of generated set
:max-elements the max size of generated set
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct values; it will be passed a map with
`:gen`, `:num-elements`, and `:max-tries` and should return an
exception"
{:added "0.9.0"}
([gen] (sorted-set gen {}))
([gen opts]
(assert (generator? gen) "First arg to sorted-set must be a generator!")
(coll-distinct-by (core/sorted-set) identity false false gen opts)))
(defn map
"Creates a generator that generates maps, with keys chosen from
`key-gen` and values chosen from `val-gen`.
If the key generator cannot or is unlikely to produce enough distinct
elements, this generator will fail in the same way as `such-that`.
Available options:
:num-elements the fixed size of generated maps
:min-elements the min size of generated maps
:max-elements the max size of generated maps
:max-tries the number of times the generator will be tried before
failing when it does not produce distinct elements
(default 10)
:ex-fn a function of one arg that will be called if test.check cannot
generate enough distinct keys; it will be passed a map with
`:gen` (the key-gen), `:num-elements`, and `:max-tries` and
should return an exception"
([key-gen val-gen] (map key-gen val-gen {}))
([key-gen val-gen opts]
(coll-distinct-by {} first false false (tuple key-gen val-gen) opts)))
;; large integers
;; ---------------------------------------------------------------------------
;; This approach has a few distribution edge cases, but is pretty good
;; for expected uses and is way better than nothing.
(def ^:private gen-raw-long
"Generates a single uniformly random long, does not shrink."
(make-gen (fn [rnd _size]
(rose/pure (random/rand-long rnd)))))
(def ^:private MAX_INTEGER
#?(:clj Long/MAX_VALUE :cljs (dec (apply * (repeat 53 2)))))
(def ^:private MIN_INTEGER
#?(:clj Long/MIN_VALUE :cljs (- MAX_INTEGER)))
(defn ^:private abs
[x]
#?(:clj (Math/abs (long x)) :cljs (Math/abs x)))
(defn ^:private long->large-integer
[bit-count x min max]
(loop [res (-> x
(#?(:clj bit-shift-right :cljs .shiftRight)
(- 64 bit-count))
#?(:cljs .toNumber)
;; so we don't get into an infinite loop bit-shifting
;; -1
(cond-> (zero? min) (abs)))]
(if (<= min res max)
res
(core/let [res' (- res)]
(if (<= min res' max)
res'
(recur #?(:clj (bit-shift-right res 1)
;; emulating bit-shift-right
:cljs (-> res
(cond-> (odd? res)
((if (neg? res) inc dec)))
(/ 2)))))))))
(defn ^:private large-integer**
"Like large-integer*, but assumes range includes zero."
[min max]
(sized (fn [size]
(core/let [size (core/max size 1) ;; no need to worry about size=0
max-bit-count (core/min size #?(:clj 64 :cljs 54))]
(gen-fmap (fn [rose]
(core/let [[bit-count x] (rose/root rose)]
(int-rose-tree (long->large-integer bit-count x min max))))
(tuple (choose 1 max-bit-count)
gen-raw-long))))))
(defn large-integer*
"Like large-integer, but accepts options:
:min the minimum integer (inclusive)
:max the maximum integer (inclusive)
Both :min and :max are optional.
(gen/sample (gen/large-integer* {:min 9000 :max 10000}))
=> (9000 9001 9001 9002 9000 9003 9006 9030 9005 9044)"
{:added "0.9.0"}
[{:keys [min max]}]
(core/let [min (or min MIN_INTEGER)
max (or max MAX_INTEGER)]
(assert (<= min max))
(such-that #(<= min % max)
(if (<= min 0 max)
(large-integer** min max)
(if (< max 0)
(fmap #(+ max %) (large-integer** (- min max) 0))
(fmap #(+ min %) (large-integer** 0 (- max min))))))))
(def ^{:added "0.9.0"} large-integer
"Generates a platform-native integer from the full available range
(in clj, 64-bit Longs, and in cljs, numbers between -(2^53 - 1) and
(2^53 - 1)).
Use large-integer* for more control."
(large-integer* {}))
;; doubles
;; ---------------------------------------------------------------------------
;; This code is a lot more complex than any reasonable person would
;; expect, for two reasons:
;;
;; 1) I wanted the generator to start with simple values and grow with
;; the size parameter, as well as shrink back to simple values. I
;; decided to define "simple" as numbers with simpler (closer to 0)
;; exponents, with simpler fractional parts (fewer lower-level bits
;; set), and with positive being simpler than negative. I also wanted
;; to take optional min/max parameters, which complicates the hell out
;; of things.
;;
;; 2) It works in CLJS as well, which has fewer utility functions for
;; doubles, and I wanted it to work exactly the same way in CLJS just
;; to validate the whole cross-platform situation. It should generate
;; the exact same numbers on both platforms.
;;
;; Some of the lower level stuff could probably be less messy and
;; faster, especially for CLJS.
(def ^:private POS_INFINITY #?(:clj Double/POSITIVE_INFINITY, :cljs (.-POSITIVE_INFINITY js/Number)))
(def ^:private NEG_INFINITY #?(:clj Double/NEGATIVE_INFINITY, :cljs (.-NEGATIVE_INFINITY js/Number)))
(def ^:private MAX_POS_VALUE #?(:clj Double/MAX_VALUE, :cljs (.-MAX_VALUE js/Number)))
(def ^:private MIN_NEG_VALUE (- MAX_POS_VALUE))
(def ^:private NAN #?(:clj Double/NaN, :cljs (.-NaN js/Number)))
(defn ^:private uniform-integer
"Generates an integer uniformly in the range 0..(2^bit-count-1)."
[bit-count]
{:assert [(<= 0 bit-count 52)]}
(if (<= bit-count 32)
;; the case here is just for cljs
(choose 0 (case (long bit-count)
32 0xffffffff
31 0x7fffffff
(-> 1 (bit-shift-left bit-count) dec)))
(fmap (fn [[upper lower]]
#? (:clj
(-> upper (bit-shift-left 32) (+ lower))
:cljs
(-> upper (* 0x100000000) (+ lower))))
(tuple (uniform-integer (- bit-count 32))
(uniform-integer 32)))))
(defn ^:private scalb
[x exp]
#?(:clj (Math/scalb ^double x ^int exp)
:cljs (* x (.pow js/Math 2 exp))))
(defn ^:private fifty-two-bit-reverse
"Bit-reverses an integer in the range [0, 2^52)."
[n]
#? (:clj
(-> n (Long/reverse) (unsigned-bit-shift-right 12))
:cljs
(loop [out 0
n n
out-shifter (Math/pow 2 52)]
(if (< n 1)
(* out out-shifter)
(recur (-> out (* 2) (+ (bit-and n 1)))
(/ n 2)
(/ out-shifter 2))))))
(def ^:private backwards-shrinking-significand
"Generates a 52-bit non-negative integer that shrinks toward having
fewer lower-order bits (and shrinks to 0 if possible)."
(fmap fifty-two-bit-reverse
(sized (fn [size]
(gen-bind (choose 0 (min size 52))
(fn [rose]
(uniform-integer (rose/root rose))))))))
(defn ^:private get-exponent
[x]
#? (:clj
(Math/getExponent ^Double x)
:cljs
(if (zero? x)
-1023
(core/let [x (Math/abs x)
res
(Math/floor (* (Math/log x) (.-LOG2E js/Math)))
t (scalb x (- res))]
(cond (< t 1) (dec res)
(<= 2 t) (inc res)
:else res)))))
(defn ^:private double-exp-and-sign
"Generates [exp sign], where exp is in [-1023, 1023] and sign is 1
or -1. Only generates values for exp and sign for which there are
doubles within the given bounds."
[lower-bound upper-bound]
(letfn [(gen-exp [lb ub]
(sized (fn [size]
(core/let [qs8 (bit-shift-left 1 (quot (min 200 size) 8))]
(cond (<= lb 0 ub)
(choose (max lb (- qs8)) (min ub qs8))
(< ub 0)
(choose (max lb (- ub qs8)) ub)
:else
(choose lb (min ub (+ lb qs8))))))))]
(if (and (nil? lower-bound)
(nil? upper-bound))
(tuple (gen-exp -1023 1023)
(elements [1.0 -1.0]))
(core/let [lower-bound (or lower-bound MIN_NEG_VALUE)
upper-bound (or upper-bound MAX_POS_VALUE)
lbexp (max -1023 (get-exponent lower-bound))
ubexp (max -1023 (get-exponent upper-bound))]
(cond (<= 0.0 lower-bound)
(tuple (gen-exp lbexp ubexp)
(return 1.0))
(<= upper-bound 0.0)
(tuple (gen-exp ubexp lbexp)
(return -1.0))
:else
(fmap (fn [[exp sign :as pair]]
(if (or (and (neg? sign) (< lbexp exp))
(and (pos? sign) (< ubexp exp)))
[exp (- sign)]
pair))
(tuple
(gen-exp -1023 (max ubexp lbexp))
(elements [1.0 -1.0]))))))))
(defn ^:private block-bounds
"Returns [low high], the smallest and largest numbers in the given
range."
[exp sign]
(if (neg? sign)
(core/let [[low high] (block-bounds exp (- sign))]
[(- high) (- low)])
(if (= -1023 exp)
[0.0 (-> 1.0 (scalb 52) dec (scalb -1074))]
[(scalb 1.0 exp)
(-> 1.0 (scalb 52) dec (scalb (- exp 51)))])))
(defn ^:private double-finite
[lower-bound upper-bound]
{:pre [(or (nil? lower-bound)
(nil? upper-bound)
(<= lower-bound upper-bound))]}
(core/let [pred (if lower-bound
(if upper-bound
#(<= lower-bound % upper-bound)
#(<= lower-bound %))
(if upper-bound
#(<= % upper-bound)))
gen
(fmap (fn [[[exp sign] significand]]
(core/let [;; 1.0 <= base < 2.0
base (inc (/ significand (Math/pow 2 52)))
x (-> base (scalb exp) (* sign))]
(if (or (nil? pred) (pred x))
x
;; Scale things a bit when we have a partial range
;; to deal with. It won't be great for generating
;; simple numbers, but oh well.
(core/let [[low high] (block-bounds exp sign)
block-lb (cond-> low lower-bound (max lower-bound))
block-ub (cond-> high upper-bound (min upper-bound))
x (+ block-lb (* (- block-ub block-lb) (- base 1)))]
(-> x (min block-ub) (max block-lb))))))
(tuple (double-exp-and-sign lower-bound upper-bound)
backwards-shrinking-significand))]
;; wrapping in the such-that is necessary for staying in bounds
;; during shrinking
(cond->> gen pred (such-that pred))))
(defn double*
"Generates a 64-bit floating point number. Options:
:infinite? - whether +/- infinity can be generated (default true)
:NaN? - whether NaN can be generated (default true)
:min - minimum value (inclusive, default none)
:max - maximum value (inclusive, default none)
Note that the min/max options must be finite numbers. Supplying a
min precludes -Infinity, and supplying a max precludes +Infinity."
{:added "0.9.0"}
[{:keys [infinite? NaN? min max]
:or {infinite? true, NaN? true}}]
(core/let [frequency-arg (cond-> [[95 (double-finite min max)]]
(if (nil? min)
(or (nil? max) (<= 0.0 max))
(if (nil? max)
(<= min 0.0)
(<= min 0.0 max)))
(conj
;; Add zeros here as a special case, since
;; the `finite` code considers zeros rather
;; complex (as they have a -1023 exponent)
;;
;; I think most uses can't distinguish 0.0
;; from -0.0, but seems worth throwing both
;; in just in case.
[1 (return 0.0)]
[1 (return -0.0)])
(and infinite? (nil? max))
(conj [1 (return POS_INFINITY)])
(and infinite? (nil? min))
(conj [1 (return NEG_INFINITY)])
NaN? (conj [1 (return NAN)]))]
(if (= 1 (count frequency-arg))
(-> frequency-arg first second)
(frequency frequency-arg))))
(def ^{:added "0.9.0"} double
"Generates 64-bit floating point numbers from the entire range,
including +/- infinity and NaN. Use double* for more control."
(double* {}))
;; bigints
;; ---------------------------------------------------------------------------
#?(:clj
(defn ^:private two-pow
[exp]
(bigint (.shiftLeft (biginteger 1) exp))))
#?(:clj (def ^:private dec-2-32 0xFFFFFFFF))
#?(:clj (def ^:private just-2-32 0x100000000))
;; could potentially make this public
#?(:clj
(defn ^:private bounded-bigint
"Generates bigints having the given max-bit-length, i.e. exclusively
between -(2^(max-bit-length)) and 2^(max-bit-length)."
[max-bit-length]
(fmap
(fn [xs]
;; is there a better way to avoid the auto-boxing warnings
;; than these Long constructor calls? I'd rather not start out
;; with bigints since that seems unnecessarily wasteful
(loop [multiple (Long. 1)
bits-left max-bit-length
xs xs
res (Long. 0)]
(cond (<= 32 bits-left)
(recur (*' just-2-32 multiple)
(- bits-left 32)
(rest xs)
(+' res (*' multiple (first xs))))
(pos? bits-left)
(core/let [x (-> xs
first
(bit-shift-right (- 32 bits-left)))]
(+' res (*' multiple x)))
:else
res)))
(vector (choose 0 dec-2-32)
(Math/ceil (/ (core/double max-bit-length) 32))))))
#?(:clj
(def ^:private size-bounded-bignat
(core/let [poor-shrinking-gen
(sized
(fn [size]
(bind (choose 0 (max 0 (* size 6)))
(fn [bit-count]
(if (zero? bit-count)
(return 0)
(fmap #(+' % (two-pow (dec bit-count)))
(bounded-bigint (dec bit-count))))))))]
(gen-fmap (fn [rose] (int-rose-tree (rose/root rose)))
poor-shrinking-gen))))
;; I avoided supporting min/max parameters because they could
;; contradict the size-boundedness
;;
;; I suppose a size-bounded-bigint* could be added with min/max that
;; throws if the resulting intersection is empty, but maybe that's
;; weird.
#?(:clj
(def ^{:added "0.10.0"} size-bounded-bigint
;; 2^(6*size) was chosen so that with size=200 the generator could
;; generate values larger than Double/MAX_VALUE
"Generates an integer (long or bigint) bounded exclusively by ±2^(6*size)."
(fmap (fn [[n negate? force-bigint?]]
(cond-> n
negate? -'
;; adds some exciting variety
force-bigint? bigint))
(tuple size-bounded-bignat
boolean
boolean))))
;; Characters & Strings
;; ---------------------------------------------------------------------------
(def char
"Generates character from 0-255."
(fmap core/char (choose 0 255)))
(def char-ascii
"Generates only ascii characters."
(fmap core/char (choose 32 126)))
(def char-alphanumeric
"Generates alphanumeric characters."
(fmap core/char
(one-of [(choose 48 57)
(choose 65 90)
(choose 97 122)])))
(def ^{:deprecated "0.6.0"}
char-alpha-numeric
"Deprecated - use char-alphanumeric instead.
Generates alphanumeric characters."
char-alphanumeric)
(def char-alpha
"Generates alpha characters."
(fmap core/char
(one-of [(choose 65 90)
(choose 97 122)])))
(def ^:private char-symbol-special
"Generates non-alphanumeric characters that can be in a symbol."
(elements [\* \+ \! \- \_ \? \.]))
(def ^:private char-symbol-noninitial
"Generates characters that can be the char following first of a keyword or symbol."
(frequency [[14 char-alphanumeric]
[7 char-symbol-special]
[1 (return \:)]]))
(def ^:private char-symbol-initial
"Generates characters that can be the first char of a keyword or symbol."
(frequency [[2 char-alpha]
[1 char-symbol-special]]))
(def string
"Generates strings. May generate unprintable characters."
(fmap clojure.string/join (vector char)))
(def string-ascii
"Generates ascii strings."
(fmap clojure.string/join (vector char-ascii)))
(def string-alphanumeric
"Generates alphanumeric strings."
(fmap clojure.string/join (vector char-alphanumeric)))
(def ^{:deprecated "0.6.0"}
string-alpha-numeric
"Deprecated - use string-alphanumeric instead.
Generates alphanumeric strings."
string-alphanumeric)
(defn- digit?
[d]
#?(:clj (Character/isDigit ^Character d)
:cljs (gstring/isNumeric d)))
(defn- +-or---digit?
"Returns true if c is \\+ or \\- and d is non-nil and a digit.
Symbols that start with +3 or -2 are not readable because they look
like numbers."
[c d]
(core/boolean (and d
(or (#?(:clj = :cljs identical?) \+ c)
(#?(:clj = :cljs identical?) \- c))
(digit? d))))
(def ^:private symbol-name-or-namespace
"Generates a namespace string for a symbol/keyword."
(->> (tuple char-symbol-initial (vector char-symbol-noninitial))
(such-that (fn [[c [d]]] (not (+-or---digit? c d))))
(fmap (fn [[c cs]]
(core/let [s (clojure.string/join (cons c cs))]
(-> s
(string/replace #":{2,}" ":")
(string/replace #":$" "")))))))
(defn ^:private resize-symbolish-generator
"Scales the sizing down on a keyword or symbol generator so as to
make it reasonable."
[g]
;; function chosen by ad-hoc experimentation
(scale #(long (Math/pow % 0.60)) g))
(def keyword
"Generates keywords without namespaces."
(->> symbol-name-or-namespace
(fmap core/keyword)
(resize-symbolish-generator)))
(def
^{:added "0.5.9"}
keyword-ns
"Generates keywords with namespaces."
(->> (tuple symbol-name-or-namespace symbol-name-or-namespace)
(fmap (fn [[ns name]] (core/keyword ns name)))
(resize-symbolish-generator)))
(def symbol
"Generates symbols without namespaces."
(frequency [[100
(->> symbol-name-or-namespace
(fmap core/symbol)
(resize-symbolish-generator))]
[1 (return '/)]]))
(def
^{:added "0.5.9"}
symbol-ns
"Generates symbols with namespaces."
(->> (tuple symbol-name-or-namespace symbol-name-or-namespace)
(fmap (fn [[ns name]] (core/symbol ns name)))
(resize-symbolish-generator)))
(def ratio
"Generates a small ratio (or integer) using gen/small-integer. Shrinks
toward simpler ratios, which may be larger or smaller."
(fmap
(fn [[a b]] (/ a b))
(tuple small-integer (fmap inc nat))))
#?(:clj
(def ^{:added "0.10.0"} big-ratio
"Generates a ratio (or integer) using gen/size-bounded-bigint. Shrinks
toward simpler ratios, which may be larger or smaller."
(fmap
(fn [[a b]] (/ a b))
(tuple size-bounded-bignat
(such-that (complement zero?) size-bounded-bignat)))))
(def ^{:added "0.9.0"} uuid
"Generates a random type-4 UUID. Does not shrink."
(no-shrink
#?(:clj
;; this could be done with combinators, but doing it low-level
;; seems to be 10x faster
(make-gen
(fn [rng _size]
(core/let [[r1 r2] (random/split rng)
x1 (-> (random/rand-long r1)
(bit-and -45057)
(bit-or 0x4000))
x2 (-> (random/rand-long r2)
(bit-or -9223372036854775808)
(bit-and -4611686018427387905))]
(rose/make-rose
(java.util.UUID. x1 x2)
[]))))
:cljs
;; this could definitely be optimized so that it doesn't require
;; generating 31 numbers
(fmap (fn [nibbles]
(letfn [(hex [idx] (.toString (nibbles idx) 16))]
(core/let [rhex (-> (nibbles 15) (bit-and 3) (+ 8) (.toString 16))]
(core/uuid (str (hex 0) (hex 1) (hex 2) (hex 3)
(hex 4) (hex 5) (hex 6) (hex 7) "-"
(hex 8) (hex 9) (hex 10) (hex 11) "-"
"4" (hex 12) (hex 13) (hex 14) "-"
rhex (hex 16) (hex 17) (hex 18) "-"
(hex 19) (hex 20) (hex 21) (hex 22)
(hex 23) (hex 24) (hex 25) (hex 26)
(hex 27) (hex 28) (hex 29) (hex 30))))))
(vector (choose 0 15) 31)))))
(defn ^:private base-simple-type
[double-gen char-gen string-gen]
(one-of [int #?(:clj size-bounded-bigint :cljs large-integer) double-gen char-gen
string-gen ratio boolean keyword keyword-ns symbol symbol-ns uuid]))
(def simple-type
"Generates a variety of scalar types."
(base-simple-type double char string))
(def simple-type-printable
"Generates a variety of scalar types, with printable strings."
(base-simple-type double char-ascii string-ascii))
(def ^{:added "0.10.0"} simple-type-equatable
"Like gen/simple-type, but only generates objects that can be
equal to other objects (e.g., not a NaN)."
(base-simple-type (double* {:NaN? false}) char string))
(def ^{:added "0.10.0"} simple-type-printable-equatable
"Like gen/simple-type-printable, but only generates objects that
can be equal to other objects (e.g., not a NaN)."
(base-simple-type (double* {:NaN? false}) char-ascii string-ascii))
#?(:cljs
;; https://clojure.atlassian.net/browse/CLJS-1594
(defn ^:private hashable?
[x]
(if (number? x)
(not (or (js/isNaN x)
(= NEG_INFINITY x)
(= POS_INFINITY x)))
true)))
(defn container-type
[inner-type]
(one-of [(vector inner-type)
(list inner-type)
(set #?(:clj inner-type
:cljs (such-that hashable? inner-type)))
;; scaling this by half since it naturally generates twice
;; as many elements
(scale #(quot % 2)
(map #?(:clj inner-type
:cljs (such-that hashable? inner-type))
inner-type))]))
;; A few helpers for recursive-gen
(defn ^:private size->max-leaf-count
[size]
;; chosen so that recursive-gen (with the assumptions mentioned in
;; the comment below) will generate structures with leaf-node-counts
;; not greater than the `size` ~99% of the time.
(long (Math/pow size 1.1)))
(core/let [log2 (Math/log 2)]
(defn ^:private random-pseudofactoring
"Returns (not generates) a random collection of integers `xs`
greater than 1 such that (<= (apply * xs) n)."
[n rng]
(if (<= n 2)
[n]
(core/let [log (Math/log n)
[r1 r2] (random/split rng)
n1 (-> (random/rand-double r1)
(* (- log log2))
(+ log2)
(Math/exp)
(long))
n2 (quot n n1)]
(if (and (< 1 n1) (< 1 n2))
(cons n1 (random-pseudofactoring n2 r2))
[n])))))
(defn ^:private randomized
"Like sized, but passes an rng instead of a size."
[func]
(make-gen (fn [rng size]
(core/let [[r1 r2] (random/split rng)]
(call-gen
(func r1)
r2
size)))))
(defn
^{:added "0.5.9"}
recursive-gen
"This is a helper for writing recursive (tree-shaped) generators. The first
argument should be a function that takes a generator as an argument, and
produces another generator that 'contains' that generator. The vector function
in this namespace is a simple example. The second argument is a scalar
generator, like boolean. For example, to produce a tree of booleans:
(gen/recursive-gen gen/vector gen/boolean)
Vectors or maps either recurring or containing booleans or integers:
(gen/recursive-gen (fn [inner] (gen/one-of [(gen/vector inner)
(gen/map inner inner)]))
(gen/one-of [gen/boolean gen/small-integer]))
Note that raw scalar values will be generated as well. To prevent this, you
can wrap the returned generator with the function passed as the first arg,
e.g.:
(gen/vector (gen/recursive-gen gen/vector gen/boolean))"
[container-gen-fn scalar-gen]
(assert (generator? scalar-gen)
"Second arg to recursive-gen must be a generator")
;; The trickiest part about this is sizing. The strategy here is to
;; assume that the container generators will (like the normal
;; collection generators in this namespace) have a size bounded by
;; the `size` parameter, and with that assumption we can give an
;; upper bound to the total number of leaf nodes in the generated
;; structure.
;;
;; So we first pick an upper bound, and pick it to be somewhat
;; larger than the real `size` since on average they will be rather
;; smaller. Then we factor that upper bound into integers to give us
;; the size to use at each depth, assuming that the total size
;; should sort of be the product of the factored sizes.
;;
;; This is all a bit weird and hard to explain precisely but I think
;; it works reasonably and definitely better than the old code.
(sized (fn [size]
(bind (choose 0 (size->max-leaf-count size))
(fn [max-leaf-count]
(randomized
(fn [rng]
(core/let [sizes (random-pseudofactoring max-leaf-count rng)
sized-scalar-gen (resize size scalar-gen)]
(reduce (fn [g size]
(bind (choose 0 10)
(fn [x]
(if (zero? x)
sized-scalar-gen
(resize size
(container-gen-fn g))))))
sized-scalar-gen
sizes)))))))))
(def any
"A recursive generator that will generate many different, often nested, values"
(recursive-gen container-type simple-type))
(def any-printable
"Like any, but avoids characters that the shell will interpret as actions,
like 7 and 14 (bell and alternate character set command)"
(recursive-gen container-type simple-type-printable))
(def ^{:added "0.10.0"} any-equatable
"Like any, but only generates objects that can be equal to other objects (e.g., do
not contain a NaN)"
(recursive-gen container-type simple-type-equatable))
(def ^{:added "0.10.0"} any-printable-equatable
"Like any, but avoids characters that the shell will interpret as actions,
like 7 and 14 (bell and alternate character set command), and only generates
objects that can be equal to other objects (e.g., do not contain a NaN)"
(recursive-gen container-type simple-type-printable-equatable))
;; Macros
;; ---------------------------------------------------------------------------
(defmacro let
"Macro for building generators using values from other generators.
Uses a binding vector with the same syntax as clojure.core/let,
where the right-hand side of the binding pairs are generators, and
the left-hand side are names (or destructuring forms) for generated
values.
Subsequent generator expressions can refer to the previously bound
values, in the same way as clojure.core/let.
The body of the let can be either a value or a generator, and does
the expected thing in either case. In this way let provides the
functionality of both `bind` and `fmap`.
Examples:
(gen/let [strs (gen/not-empty (gen/list gen/string))
s (gen/elements strs)]
{:some-strings strs
:one-of-those-strings s})
;; generates collections of \"users\" that have integer IDs
;; from 0...N-1, but are in a random order
(gen/let [users (gen/list (gen/hash-map :name gen/string-ascii
:age gen/nat))]
(->> users
(map #(assoc %2 :id %1) (range))
(gen/shuffle)))"
{:added "0.9.0"}
[bindings & body]
(assert (vector? bindings)
"First arg to gen/let must be a vector of bindings.")
(assert (even? (count bindings))
"gen/let requires an even number of forms in binding vector")
(if (empty? bindings)
`(core/let [val# (do ~@body)]
(if (clojure.test.check.generators/generator? val#)
val#
(return val#)))
(core/let [[binding gen & more] bindings]
`(clojure.test.check.generators/bind ~gen (fn [~binding] (let [~@more] ~@body))))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/impl.cljc 0000664 0000000 0000000 00000001173 14151735222 0026303 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.impl)
(defn get-current-time-millis []
#?(:clj (System/currentTimeMillis)
:cljs (.valueOf (js/Date.))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/properties.cljc 0000664 0000000 0000000 00000006341 14151735222 0027540 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.properties
(:require [clojure.test.check.generators :as gen]
[clojure.test.check.results :as results])
#?(:cljs (:require-macros [clojure.test.check.properties :refer [for-all]])))
(defrecord ErrorResult [error]
results/Result
(pass? [_] false)
(result-data [_]
;; spelling out the whole keyword here since `::error` is
;; different in self-hosted cljs.
{:clojure.test.check.properties/error error}))
(defn ^:private exception?
[x]
(instance? #?(:clj Throwable :cljs js/Error) x))
(defn ^:private apply-gen
[function]
(fn [args]
(let [result (try
(let [ret (apply function args)]
;; TCHECK-131: for backwards compatibility (mainly
;; for spec), treat returned exceptions like thrown
;; exceptions
(if (exception? ret)
(throw ret)
ret))
#?(:clj (catch java.lang.ThreadDeath t (throw t)))
(catch #?(:clj Throwable :cljs :default) ex
(->ErrorResult ex)))]
{:result result
:function function
:args args})))
(defn for-all*
"A function version of `for-all`. Takes a sequence of N generators
and a function of N args, and returns a property that calls the
function with generated values and tests the return value for
truthiness, like with `for-all`.
Example:
(for-all* [gen/large-integer gen/large-integer]
(fn [a b] (>= (+ a b) a)))"
[args function]
(gen/fmap
(apply-gen function)
(apply gen/tuple args)))
(defn- binding-vars
[bindings]
(map first (partition 2 bindings)))
(defn- binding-gens
[bindings]
(map second (partition 2 bindings)))
(defmacro for-all
"Returns a property, which is the combination of some generators and
an assertion that should be true for all generated values. Properties
can be used with `quick-check` or `defspec`.
`for-all` takes a `let`-style bindings vector, where the right-hand
side of each binding is a generator.
The body should be an expression of the generated values that will
be tested for truthiness, unless it is a special implementation of
the clojure.test.check.results/Result protocol. Exceptions in the
body will be caught and treated as failures.
When there are multiple binding pairs, the earlier pairs are not
visible to the later pairs.
If there are multiple body expressions, all but the last one are
executed for side effects, as with `do`.
Example:
(for-all [a gen/large-integer
b gen/large-integer]
(>= (+ a b) a))"
[bindings & body]
`(for-all* ~(vec (binding-gens bindings))
(fn [~@(binding-vars bindings)]
~@body)))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random.clj 0000664 0000000 0000000 00000014223 14151735222 0026457 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:author "Gary Fredericks"
:doc "Purely functional and splittable pseudo-random number generators."}
clojure.test.check.random
(:refer-clojure :exclude [unsigned-bit-shift-right]))
(defprotocol IRandom
(rand-long [rng]
"Returns a random long based on the given immutable RNG.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument")
(rand-double [rng]
"Returns a random double between 0.0 (inclusive) and 1.0 (exclusive)
based on the given immutable RNG.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument")
(split [rng]
"Returns two new RNGs [rng1 rng2], which should generate
sufficiently independent random data.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument")
(split-n [rng n]
"Returns a collection of `n` RNGs, which should generate
sufficiently independent random data.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument"))
;; Immutable version of Java 8's java.util.SplittableRandom
;;
;; Meant to give the same results as similar uses of
;; java.util.SplittableRandom, in particular:
;;
;; (= (-> (make-java-util-splittable-random 42)
;; (rand-long))
;; (.nextLong (SplittableRandom. 42)))
;;
;; (= (-> (make-java-util-splittable-random 42)
;; (split)
;; (first)
;; (rand-long))
;; (.nextLong (doto (SplittableRandom. 42)
;; (.split))))
;;
;; (= (-> (make-java-util-splittable-random 42)
;; (split)
;; (second)
;; (rand-long))
;; (.nextLong (.split (SplittableRandom. 42))))
;;
;; Also see the spec that checks this equivalency.
;; backwards compatibility for clojure 1.5
(def ^:private old-clojure?
(not (resolve 'clojure.core/unsigned-bit-shift-right)))
(defmacro ^:private unsigned-bit-shift-right
[x n]
{:pre [(<= 1 n 63)]}
(if old-clojure?
(let [mask (-> Long/MIN_VALUE
(bit-shift-right (dec n))
(bit-not))]
`(-> ~x
(bit-shift-right ~n)
(bit-and ~mask)))
`(clojure.core/unsigned-bit-shift-right ~x ~n)))
(defmacro ^:private longify
"Macro for writing arbitrary longs in the java 0x syntax. E.g.
0x9e3779b97f4a7c15 (which is read as a bigint because it's out
of range) becomes -7046029254386353131."
[num]
(if (> num Long/MAX_VALUE)
(-> num
(- 18446744073709551616N)
(long)
(bit-or -9223372036854775808))
num))
(set! *unchecked-math* :warn-on-boxed)
(defmacro ^:private bxoubsr
"Performs (-> x (unsigned-bit-shift-right n) (bit-xor x))."
[x n]
(vary-meta
`(let [x# ~x]
(-> x# (unsigned-bit-shift-right ~n) (bit-xor x#)))
assoc :tag 'long))
(defmacro ^:private mix-64
[n]
`(-> ~n
(bxoubsr 30)
(* (longify 0xbf58476d1ce4e5b9))
(bxoubsr 27)
(* (longify 0x94d049bb133111eb))
(bxoubsr 31)))
(defmacro ^:private mix-gamma
[n]
`(-> ~n
(bxoubsr 33)
(* (longify 0xff51afd7ed558ccd))
(bxoubsr 33)
(* (longify 0xc4ceb9fe1a85ec53))
(bxoubsr 33)
(bit-or 1)
(as-> z#
(cond-> z#
(> 24 (-> z#
(bxoubsr 1)
(Long/bitCount)))
(bit-xor (longify 0xaaaaaaaaaaaaaaaa))))))
(def ^:private ^:const double-unit (/ 1.0 (double (bit-set 0 53))))
;; Java: 0x1.0p-53 or (1.0 / (1L << 53))
(deftype JavaUtilSplittableRandom [^long gamma ^long state]
IRandom
(rand-long [_]
(-> state (+ gamma) (mix-64)))
(rand-double [this]
(* double-unit (unsigned-bit-shift-right (long (rand-long this)) 11)))
(split [this]
(let [state' (+ gamma state)
state'' (+ gamma state')
gamma' (mix-gamma state'')]
[(JavaUtilSplittableRandom. gamma state'')
(JavaUtilSplittableRandom. gamma' (mix-64 state'))]))
(split-n [this n]
;; immitates a particular series of 2-way splits, but avoids the
;; intermediate allocation. See the `split-n-spec` for a test of
;; the equivalence to 2-way splits.
(let [n (long n)]
(case n
0 []
1 [this]
(let [n-dec (dec n)]
(loop [state state
ret (transient [])]
(if (= n-dec (count ret))
(-> ret
(conj! (JavaUtilSplittableRandom. gamma state))
(persistent!))
(let [state' (+ gamma state)
state'' (+ gamma state')
gamma' (mix-gamma state'')
new-rng (JavaUtilSplittableRandom. gamma' (mix-64 state'))]
(recur state'' (conj! ret new-rng))))))))))
(def ^:private golden-gamma
(longify 0x9e3779b97f4a7c15))
(defn make-java-util-splittable-random
[^long seed]
(JavaUtilSplittableRandom. golden-gamma seed))
;; some global state to make sure that seedless calls to make-random
;; return independent results
(def ^:private next-rng
"Returns a random-number generator. Successive calls should return
independent results."
(let [a (atom (make-java-util-splittable-random (System/currentTimeMillis)))
thread-local
(proxy [ThreadLocal] []
(initialValue []
(first (split (swap! a #(second (split %)))))))]
(fn []
(let [rng (.get thread-local)
[rng1 rng2] (split rng)]
(.set thread-local rng2)
rng1))))
(defn make-random
"Given an optional Long seed, returns an object that satisfies the
IRandom protocol."
([] (next-rng))
([seed] (make-java-util-splittable-random seed)))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random.cljs 0000664 0000000 0000000 00000011506 14151735222 0026643 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:author "Gary Fredericks"
:doc "Purely functional and splittable pseudo-random number generators."}
clojure.test.check.random
(:refer-clojure :exclude [+ * bit-xor bit-or bit-count
unsigned-bit-shift-right])
(:require [clojure.test.check.random.doubles :as doubles]
[clojure.test.check.random.longs :as longs
:refer [+ * bit-xor bit-or bit-count unsigned-bit-shift-right]]))
(defprotocol IRandom
(rand-long [rng]
"Returns a random goog.math.Long based on the given immutable RNG.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument")
(rand-double [rng]
"Returns a random double between 0.0 (inclusive) and 1.0 (exclusive)
based on the given immutable RNG.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument")
(split [rng]
"Returns two new RNGs [rng1 rng2], which should generate
sufficiently independent random data.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument")
(split-n [rng n]
"Returns a collection of `n` RNGs, which should generate
sufficiently independent random data.
Note: to maintain independence you should not call more than one
function in the IRandom protocol with the same argument"))
;;
;; This is a port of the clojure-jvm port of
;; java.util.SplittableRandom, and should give identical results.
;;
(defn ^:private hex-long
"Helper for defining constants."
[s]
(longs/from-string s 16))
(defn ^:private bxoubsr
"Performs (-> x (unsigned-bit-shift-right n) (bit-xor x))."
[x n]
(-> x (unsigned-bit-shift-right n) (bit-xor x)))
(def ^:private mix-64-const-1 (hex-long "bf58476d1ce4e5b9"))
(def ^:private mix-64-const-2 (hex-long "94d049bb133111eb"))
(defn ^:private mix-64
[n]
(-> n
(bxoubsr 30)
(* mix-64-const-1)
(bxoubsr 27)
(* mix-64-const-2)
(bxoubsr 31)))
(def ^:private mix-gamma-const-1 (hex-long "ff51afd7ed558ccd"))
(def ^:private mix-gamma-const-2 (hex-long "c4ceb9fe1a85ec53"))
(def ^:private mix-gamma-const-3 (hex-long "aaaaaaaaaaaaaaaa"))
(defn ^:private mix-gamma
[n]
(-> n
(bxoubsr 33)
(* mix-gamma-const-1)
(bxoubsr 33)
(* mix-gamma-const-2)
(bxoubsr 33)
(bit-or longs/ONE)
(as-> z
(cond-> z
(> 24 (-> z
(bxoubsr 1)
(bit-count)))
(bit-xor mix-gamma-const-3)))))
(deftype JavaUtilSplittableRandom [gamma state]
IRandom
(rand-long [_]
(-> state (+ gamma) (mix-64)))
(rand-double [this]
(-> this rand-long doubles/rand-long->rand-double))
(split [this]
(let [state' (+ gamma state)
state'' (+ gamma state')
gamma' (mix-gamma state'')]
[(JavaUtilSplittableRandom. gamma state'')
(JavaUtilSplittableRandom. gamma' (mix-64 state'))]))
(split-n [this n]
(case n
0 []
1 [this]
(let [n-dec (dec n)]
(loop [state state
ret (transient [])]
(if (= n-dec (count ret))
(-> ret
(conj! (JavaUtilSplittableRandom. gamma state))
(persistent!))
(let [state' (+ gamma state)
state'' (+ gamma state')
gamma' (mix-gamma state'')
new-rng (JavaUtilSplittableRandom. gamma' (mix-64 state'))]
(recur state'' (conj! ret new-rng)))))))))
(def ^:private golden-gamma
(hex-long "9e3779b97f4a7c15"))
(defn make-java-util-splittable-random
[seed]
(JavaUtilSplittableRandom. golden-gamma
(or (longs/->long seed)
(throw (ex-info "Bad random seed!"
{:seed seed})))))
(def ^:private next-rng
(let [a (atom {:state
(make-java-util-splittable-random (.valueOf (js/Date.)))})]
(fn []
(:returned
(swap! a (fn [{:keys [state]}]
(let [[r1 r2] (split state)]
{:state r1 :returned r2})))))))
(defn make-random
"Given an optional integer (or goog.math.Long) seed, returns an
implementation of the IRandom protocol."
([] (next-rng))
([seed]
(make-java-util-splittable-random seed)))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random/ 0000775 0000000 0000000 00000000000 14151735222 0025763 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random/doubles.cljs 0000664 0000000 0000000 00000002256 14151735222 0030302 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:author "Gary Fredericks"}
clojure.test.check.random.doubles
(:require [clojure.test.check.random.longs :as longs]))
(def ^:private double-unit
(loop [i 53 x 1]
(if (zero? i)
x
(recur (dec i) (/ x 2)))))
(def ^:private big-double-unit
;; (* double-unit 0x100000000)
(* double-unit 4294967296))
(defn rand-long->rand-double
"Given a uniformly distributed random long, returns a uniformly
distributed double between 0.0 (inclusive) and 1.0 (exclusive)."
[long]
(let [x (longs/unsigned-bit-shift-right long 11)
low-bits (.getLowBitsUnsigned x)
high-bits (.getHighBits x)]
(+ (* double-unit low-bits)
(* big-double-unit high-bits))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random/longs.cljs 0000664 0000000 0000000 00000010132 14151735222 0027757 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:author "Gary Fredericks"
:doc "Internal namespace, wrapping some goog.math.Long functionality."}
clojure.test.check.random.longs
(:refer-clojure :exclude [+ * bit-xor bit-or bit-count
unsigned-bit-shift-right])
(:require [clojure.test.check.random.longs.bit-count-impl :as bit-count]
[goog.math.Long :as long]
[clojure.core :as core]))
(defn unsigned-bit-shift-right
[x n]
(.shiftRightUnsigned x n))
(defn +
[x y]
(.add x y))
(defn *
[x y]
(let [a48 (bit-shift-right-zero-fill (.-high_ x) 16)
a32 (bit-and (.-high_ x) 0xFFFF)
a16 (bit-shift-right-zero-fill (.-low_ x) 16)
a00 (bit-and (.-low_ x) 0xFFFF)
b48 (bit-shift-right-zero-fill (.-high_ y) 16)
b32 (bit-and (.-high_ y) 0xFFFF)
b16 (bit-shift-right-zero-fill (.-low_ y) 16)
b00 (bit-and (.-low_ y) 0xFFFF)
arr (array 0 0 0 0)] ;[c00 c16 c32 c48]
(aset arr 0 (core/* a00 b00)) ;c00 += a00 * b00;
(aset arr 1 (bit-shift-right-zero-fill (aget arr 0) 16)) ;c16 += c00 >>> 16
(aset arr 0 (bit-and (aget arr 0) 0xFFFF)) ;c00 &= 0xFFFF;
(aset arr 1 (core/+ (aget arr 1) (core/* a16 b00))) ;c16 += a16 * b00;
(aset arr 2 (bit-shift-right-zero-fill (aget arr 1) 16)) ;c32 += c16 >>> 16;
(aset arr 1 (bit-and (aget arr 1) 0xFFFF)) ;c16 &= 0xFFFF;
(aset arr 1 (core/+ (aget arr 1) (core/* a00 b16))) ;c16 += a00 * b16;
(aset arr 2 (core/+ (aget arr 2) (bit-shift-right-zero-fill (aget arr 1) 16))) ;c32 += c16 >>> 16;
(aset arr 1 (bit-and (aget arr 1) 0xFFFF)) ;c16 &= 0xFFFF;
(aset arr 2 (core/+ (aget arr 2) (core/* a32 b00))) ;c32 += a32 * b00;
(aset arr 3 (bit-shift-right-zero-fill (aget arr 2) 16)) ;c48 += c32 >>> 16;
(aset arr 2 (bit-and (aget arr 2) 0xFFFF)) ;c32 &= 0xFFFF;
(aset arr 2 (core/+ (aget arr 2) (core/* a16 b16))) ;c32 += a16 * b16;
(aset arr 3 (core/+ (aget arr 3) (bit-shift-right-zero-fill (aget arr 2) 16))) ;c48 += c32 >>> 16;
(aset arr 2 (bit-and (aget arr 2) 0xFFFF)) ;c32 &= 0xFFFF;
(aset arr 2 (core/+ (aget arr 2) (core/* a00 b32))) ;c32 += a00 * b32;
(aset arr 3 (core/+ (aget arr 3) (bit-shift-right-zero-fill (aget arr 2) 16))) ;c48 += c32 >>> 16;
(aset arr 2 (bit-and (aget arr 2) 0xFFFF)) ;c32 &= 0xFFFF;
(aset arr 3 (core/+ (aget arr 3) (core/* a48 b00) (core/* a32 b16) (core/* a16 b32) (core/* a00 b48)))
;c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
(aset arr 3 (bit-and (aget arr 3) 0xFFFF)) ;c48 &= 0xFFFF;
;(c16 << 16) | c00, (c48 << 16) | c32
(long/fromBits (core/bit-or (bit-shift-left (aget arr 1) 16) (aget arr 0))
(core/bit-or (bit-shift-left (aget arr 3) 16) (aget arr 2)))))
(defn bit-xor
[x y]
(.xor x y))
(defn bit-or
[x y]
(.or x y))
(defn from-string
[s radix]
(long/fromString s radix))
(defn from-number
[x]
(long/fromNumber x))
(defn ->long
"Coerces to long, or returns nil if not possible."
[x]
(cond (number? x)
(long/fromNumber x)
(instance? goog.math.Long x)
x))
(def ONE (long/getOne))
(def bit-count bit-count/bit-count)
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random/longs/ 0000775 0000000 0000000 00000000000 14151735222 0027105 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/random/longs/bit_count_impl.cljs 0000664 0000000 0000000 00000003120 14151735222 0032765 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:author "Gary Fredericks"}
clojure.test.check.random.longs.bit-count-impl
(:refer-clojure :exclude [bit-count]))
;; Had to put this in a separate namespace because the + doesn't get
;; inlined if it's called as (core/+ ...). This might change in future
;; CLJS versions?
(def ^:private lookup
(let [arr (make-array 256)]
(aset arr 0 0)
(dotimes [i 256]
(aset arr i (+ (aget arr (bit-shift-right i 1))
(bit-and i 1))))
arr))
(defn bit-count
"Returns a JS number (not a Long), the number of set bits in the
given Long."
[x]
(let [low (.-low_ x)
high (.-high_ x)]
(+ (aget lookup (-> low (bit-and 255)))
(aget lookup (-> low (bit-shift-right 8) (bit-and 255)))
(aget lookup (-> low (bit-shift-right 16) (bit-and 255)))
(aget lookup (-> low (bit-shift-right 24) (bit-and 255)))
(aget lookup (-> high (bit-and 255)))
(aget lookup (-> high (bit-shift-right 8) (bit-and 255)))
(aget lookup (-> high (bit-shift-right 16) (bit-and 255)))
(aget lookup (-> high (bit-shift-right 24) (bit-and 255))))))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/results.cljc 0000664 0000000 0000000 00000001662 14151735222 0027046 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:author "Gary Fredericks"
:doc "A protocol and helper functions for trial results."}
clojure.test.check.results)
(defprotocol Result
(pass? [result] "A boolean indicating if the result passed.")
(result-data [result] "A map of data about the trial."))
(extend-protocol Result
#?(:clj Object :cljs default)
(pass? [this] (boolean this))
(result-data [this] nil)
nil
(pass? [this] false)
(result-data [this] nil))
clojure-test.check-b2ec872/src/main/clojure/clojure/test/check/rose_tree.cljc 0000664 0000000 0000000 00000014444 14151735222 0027336 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.rose-tree
"A lazy tree data structure used for shrinking."
(:refer-clojure :exclude [filter remove seq])
(:require [#?(:clj clojure.core :cljs cljs.core) :as core]))
(deftype RoseTree [root children]
#?(:clj clojure.lang.Indexed
:cljs IIndexed)
(#?(:clj nth :cljs -nth) [this i]
(cond (= i 0) root
(= i 1) children
:else (throw #?(:clj (IndexOutOfBoundsException.)
:cljs (js/Error. "Index out of bounds in rose tree")))))
(#?(:clj nth :cljs -nth) [this i not-found]
(cond (= i 0) root
(= i 1) children
:else not-found)))
(defn root
"Returns the root of a Rose tree."
{:no-doc true}
[^RoseTree rose]
(.-root rose))
(defn children
"Returns the children of the root of the Rose tree."
{:no-doc true}
[^RoseTree rose]
(.-children rose))
(defn make-rose
[root children]
(RoseTree. root children))
(defn- exclude-nth
"Exclude the nth value in a collection."
[n coll]
(lazy-seq
(when-let [s (core/seq coll)]
(if (zero? n)
(rest coll)
(cons (first s)
(exclude-nth (dec n) (rest s)))))))
(defn join
"Turn a tree of trees into a single tree. Does this by concatenating
children of the inner and outer trees."
{:no-doc true}
[rose]
(let [outer-root (root rose)
outer-children (children rose)
inner-root (root outer-root)
inner-children (children outer-root)]
(make-rose inner-root (concat (map join outer-children)
inner-children))))
(defn pure
"Puts a value `x` into a Rose tree, with no children."
{:no-doc true}
[x]
(make-rose x []))
(defn fmap
"Applies functions `f` to all values in the tree."
{:no-doc true}
[f rose]
(make-rose (f (root rose)) (map #(fmap f %) (children rose))))
(defn bind
"Takes a Rose tree (m) and a function (k) from
values to Rose tree and returns a new Rose tree.
This is the monadic bind (>>=) for Rose trees."
{:no-doc true}
[m k]
(join (fmap k m)))
(defn filter
"Returns a new Rose tree whose values pass `pred`. Values who
do not pass `pred` have their children cut out as well."
{:no-doc true}
[pred rose]
(make-rose (root rose)
(map #(filter pred %)
(core/filter #(pred (root %)) (children rose)))))
(defn permutations
"Create a seq of vectors, where each rose in turn, has been replaced
by its children."
{:no-doc true}
[roses]
(for [[rose index] (map vector roses (range))
child (children rose)]
(assoc roses index child)))
(defn zip
"Apply `f` to the sequence of Rose trees `roses`."
{:no-doc true}
[f roses]
(make-rose
(apply f (map root roses))
(map #(zip f %)
(permutations roses))))
(defn remove
{:no-doc true}
[roses]
(concat
(map-indexed (fn [index _] (exclude-nth index roses)) roses)
(permutations (vec roses))))
(defn ^:private unchunk
"Returns an equivalent lazy seq that is not chunked."
[a-lazy-seq]
(take
#?(:clj Double/POSITIVE_INFINITY :cljs js/Infinity)
a-lazy-seq))
(defn shrink
{:no-doc true}
[f roses]
(if (core/seq roses)
(make-rose (apply f (map root roses))
(map #(shrink f %) (remove (unchunk roses))))
(make-rose (f) [])))
(declare shrink-vector*)
(defn ^:private bifurcate
"Returns a sequence of rose trees representing shrinks that discard
half of the vector of roses."
[f roses]
(when (<= 4 (count roses))
(let [left-count (quot (count roses) 2)]
(lazy-seq
(cons
(shrink-vector* f (subvec roses 0 left-count))
(lazy-seq
(list (shrink-vector* f (subvec roses left-count)))))))))
(defn ^:private shrink-vector*
[f roses]
(let [thing (shrink f roses)]
(make-rose (root thing)
(concat (bifurcate f roses) (children thing)))))
(defn shrink-vector
[f roses]
{:pre [(vector? roses)]}
(let [rose (shrink-vector* f roses)
empty-rose (make-rose (f) [])]
(if (empty? roses)
rose
(make-rose (root rose)
(cons empty-rose (children rose))))))
(defn collapse
"Return a new rose-tree whose depth-one children
are the children from depth one _and_ two of the input
tree."
{:no-doc true}
[rose]
(make-rose (root rose)
(let [the-children (children rose)]
(concat (map collapse the-children)
(map collapse
(mapcat children the-children))))))
(defn- make-stack
[children stack]
(if-let [s (core/seq children)]
(cons children stack)
stack))
(defn seq
"Create a lazy-seq of all of the (unique) nodes in a shrink-tree.
This assumes that two nodes with the same value have the same children.
While it's not common, it's possible to create trees that don't
fit that description. This function is significantly faster than
brute-force enumerating all of the nodes in a tree, as there will
be many duplicates."
[rose]
(let [helper (fn helper [rose seen stack]
(let [node (root rose)
the-children (children rose)]
(lazy-seq
(if-not (seen node)
(cons node
(if (core/seq the-children)
(helper (first the-children) (conj seen node) (make-stack (rest the-children) stack))
(when-let [s (core/seq stack)]
(let [f (ffirst s)
r (rest (first s))]
(helper f (conj seen node) (make-stack r (rest s)))))))
(when-let [s (core/seq stack)]
(let [f (ffirst s)
r (rest (first s))]
(helper f seen (make-stack r (rest s)))))))))]
(helper rose #{} '())))
clojure-test.check-b2ec872/src/target/ 0000775 0000000 0000000 00000000000 14151735222 0017703 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/ 0000775 0000000 0000000 00000000000 14151735222 0020636 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/browser/ 0000775 0000000 0000000 00000000000 14151735222 0022321 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/browser/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0023764 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/browser/clojure/test/ 0000775 0000000 0000000 00000000000 14151735222 0024743 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/browser/clojure/test/check/ 0000775 0000000 0000000 00000000000 14151735222 0026020 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/browser/clojure/test/check/test/ 0000775 0000000 0000000 00000000000 14151735222 0026777 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/browser/clojure/test/check/test/runner.cljs 0000664 0000000 0000000 00000000773 14151735222 0031174 0 ustar 00root root 0000000 0000000 (ns clojure.test.check.test.runner
(:require [cljs.test :as test :refer-macros [run-tests]]
[clojure.test.check.generators :as gen]
[clojure.test.check.test]
[clojure.test.check.random-test]
[clojure.test.check.rose-tree-test]
[clojure.test.check.clojure-test-test]))
(enable-console-print!)
(run-tests
'clojure.test.check.test
'clojure.test.check.random-test
'clojure.test.check.rose-tree-test
'clojure.test.check.clojure-test-test)
clojure-test.check-b2ec872/src/target/cljs/node/ 0000775 0000000 0000000 00000000000 14151735222 0021563 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/node/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0023226 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/node/clojure/test/ 0000775 0000000 0000000 00000000000 14151735222 0024205 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/node/clojure/test/check/ 0000775 0000000 0000000 00000000000 14151735222 0025262 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/node/clojure/test/check/test/ 0000775 0000000 0000000 00000000000 14151735222 0026241 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/node/clojure/test/check/test/runner.cljs 0000664 0000000 0000000 00000001131 14151735222 0030423 0 ustar 00root root 0000000 0000000 (ns clojure.test.check.test.runner
(:require [cljs.nodejs :as nodejs]
[cljs.test :as test :refer-macros [run-tests]]
[clojure.test.check.test]
[clojure.test.check.random-test]
[clojure.test.check.rose-tree-test]
[clojure.test.check.clojure-test-test]
[clojure.test.check.generators :as gen]))
(nodejs/enable-util-print!)
(defn -main []
(run-tests
'clojure.test.check.test
'clojure.test.check.random-test
'clojure.test.check.rose-tree-test
'clojure.test.check.clojure-test-test))
(set! *main-cli-fn* -main)
clojure-test.check-b2ec872/src/target/cljs/self-host/ 0000775 0000000 0000000 00000000000 14151735222 0022542 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/self-host/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0024205 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/self-host/clojure/test/ 0000775 0000000 0000000 00000000000 14151735222 0025164 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/self-host/clojure/test/check/ 0000775 0000000 0000000 00000000000 14151735222 0026241 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/self-host/clojure/test/check/test/ 0000775 0000000 0000000 00000000000 14151735222 0027220 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/target/cljs/self-host/clojure/test/check/test/aux.cljs 0000664 0000000 0000000 00000007204 14151735222 0030675 0 ustar 00root root 0000000 0000000 (ns ^{:doc "This auxiliary namespace is not actually loaded.
Its mere presence cause it to be compiled and thus causes
the libs listed here to be dumped into the compiler output
directory where they can be loaded on demand when running
the tests in self-host mode."}
clojure.test.check.test.aux
(:require
cljs.test
goog.Delay
goog.Disposable
goog.Promise
goog.Throttle
goog.Timer
goog.Uri
goog.color
goog.color.Hsl
goog.color.Hsv
goog.color.Rgb
goog.color.alpha
goog.color.names
goog.crypt
goog.crypt.Aes
goog.crypt.Arc4
goog.crypt.BlobHasher
goog.crypt.Cbc
goog.crypt.Hash
goog.crypt.Hmac
goog.crypt.Md5
goog.crypt.Sha1
goog.crypt.Sha2
goog.crypt.Sha224
goog.crypt.Sha256
goog.crypt.Sha2_64bit
goog.crypt.Sha512
goog.crypt.Sha512_256
goog.crypt.base64
goog.crypt.baseN
goog.crypt.hash32
goog.crypt.hashTester
goog.crypt.pbkdf2
goog.date.Date
goog.date.DateLike
goog.date.DateRange
goog.date.DateTime
goog.date.Interval
goog.date.UtcDateTime
goog.date.duration
goog.date.month
goog.date.relative.TimeDeltaFormatter
goog.date.relative.Unit
goog.date.relativeWithPlurals
goog.date.weekDay
goog.format
goog.format.EmailAddress
goog.format.HtmlPrettyPrinter
goog.format.InternationalizedEmailAddress
goog.format.JsonPrettyPrinter
goog.i18n.BidiFormatter
goog.i18n.CharListDecompressor
goog.i18n.CharPickerData
goog.i18n.DateTimeFormat
goog.i18n.DateTimeParse
goog.i18n.GraphemeBreak
goog.i18n.MessageFormat
goog.i18n.NumberFormat
goog.i18n.TimeZone
goog.i18n.bidi
goog.i18n.bidi.Dir
goog.i18n.bidi.Format
goog.i18n.collation
goog.i18n.currency
goog.i18n.mime
goog.i18n.ordinalRules
goog.i18n.pluralRules
goog.i18n.uChar
goog.i18n.uChar.LocalNameFetcher
goog.i18n.uChar.RemoteNameFetcher
goog.i18n.uCharNames
goog.iter
goog.iter.Iterable
goog.iter.Iterator
goog.json
goog.json.EvalJsonProcessor
goog.json.NativeJsonProcessor
goog.json.Replacer
goog.json.Reviver
goog.json.Serializer
goog.json.hybrid
goog.locale
goog.locale.TimeZoneFingerprint
goog.locale.defaultLocaleNameConstants
goog.locale.genericFontNames
goog.locale.timeZoneDetection
goog.math
goog.math.AffineTransform
goog.math.Bezier
goog.math.Box
goog.math.Coordinate
goog.math.Coordinate3
goog.math.ExponentialBackoff
goog.math.Integer
goog.math.Line
goog.math.Long
goog.math.Matrix
goog.math.Path
goog.math.Path.Segment
goog.math.Range
goog.math.RangeSet
goog.math.Rect
goog.math.Size
goog.math.Vec2
goog.math.Vec3
goog.math.interpolator.Linear1
goog.math.interpolator.Pchip1
goog.math.interpolator.Spline1
goog.math.paths
goog.math.tdma
goog.spell.SpellCheck
goog.string
goog.string.Const
goog.string.StringBuffer
goog.string.Unicode
goog.string.format
goog.string.newlines
goog.string.newlines.Line
goog.structs
goog.structs.AvlTree
goog.structs.AvlTree.Node
goog.structs.CircularBuffer
goog.structs.Heap
goog.structs.InversionMap
goog.structs.LinkedMap
goog.structs.Map
goog.structs.Node
goog.structs.Pool
goog.structs.PriorityPool
goog.structs.PriorityQueue
goog.structs.QuadTree
goog.structs.QuadTree.Node
goog.structs.QuadTree.Point
goog.structs.Queue
goog.structs.Set
goog.structs.SimplePool
goog.structs.StringSet
goog.structs.TreeNode
goog.structs.Trie
goog.text.LoremIpsum))
clojure-test.check-b2ec872/src/target/cljs/self-host/clojure/test/check/test/runner.cljs 0000664 0000000 0000000 00000017607 14151735222 0031421 0 ustar 00root root 0000000 0000000 (ns clojure.test.check.test.runner
(:require [clojure.string :as string]
[cljs.nodejs :as nodejs]
[cljs.js :as cljs]
[cljs.reader :as reader]))
(def out-dir "target/out-self-host")
(def src-paths [out-dir
"src/main/clojure"
"src/test/clojure"])
(defn init-runtime
"Initializes the runtime so that we can use the cljs.user
namespace and so that Google Closure is set up to work
properly with :optimizations :none."
[]
(set! (.-user js/cljs) #js {})
;; monkey-patch isProvided_ to avoid useless warnings
(js* "goog.isProvided_ = function(x) { return false; };")
;; monkey-patch goog.require, skip all the loaded checks
(set! (.-require js/goog)
(fn [name]
(js/CLOSURE_IMPORT_SCRIPT
(aget (.. js/goog -dependencies_ -nameToPath) name))))
;; setup printing
(nodejs/enable-util-print!)
;; redef goog.require to track loaded libs
(set! *loaded-libs* #{"cljs.core"})
(set! (.-require js/goog)
(fn [name reload]
(when (or (not (contains? *loaded-libs* name)) reload)
(set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
(js/CLOSURE_IMPORT_SCRIPT
(aget (.. js/goog -dependencies_ -nameToPath) name))))))
;; Node file reading fns
(def fs (nodejs/require "fs"))
(defn node-read-file
"Accepts a filename to read and a callback. Upon success, invokes
callback with the source. Otherwise invokes the callback with nil."
[filename cb]
(.readFile fs filename "utf-8"
(fn [err source]
(cb (when-not err
source)))))
(defn node-read-file-sync
"Accepts a filename to read. Upon success, returns the source.
Otherwise returns nil."
[filename]
(.readFileSync fs filename "utf-8"))
;; Facilities for loading Closure deps
(defn closure-index
"Builds an index of Closure files. Similar to
cljs.js-deps/goog-dependencies*"
[]
(let [paths-to-provides
(map (fn [[_ path provides]]
[path (map second
(re-seq #"'(.*?)'" provides))])
(re-seq #"\ngoog\.addDependency\('(.*)', \[(.*?)\].*"
(node-read-file-sync (str out-dir "/goog/deps.js"))))]
(into {}
(for [[path provides] paths-to-provides
provide provides]
[(symbol provide) (str out-dir "/goog/" (second (re-find #"(.*)\.js$" path)))]))))
(def closure-index-mem (memoize closure-index))
(defn load-goog
"Loads a Google Closure implementation source file."
[name cb]
(if-let [goog-path (get (closure-index-mem) name)]
(if-let [source (node-read-file-sync (str goog-path ".js"))]
(cb {:source source
:lang :js})
(cb nil))
(cb nil)))
;; Facilities for loading files
(defn- filename->lang
"Converts a filename to a lang keyword by inspecting the file
extension."
[filename]
(if (string/ends-with? filename ".js")
:js
:clj))
(defn replace-extension
"Replaces the extension on a file."
[filename new-extension]
(string/replace filename #".clj[sc]?$" new-extension))
(defn parse-edn
"Parses edn source to Clojure data."
[edn-source]
(reader/read-string edn-source))
(defn- read-some
"Reads the first filename in a sequence of supplied filenames,
using a supplied read-file-fn, calling back upon first successful
read, otherwise calling back with nil. Before calling back, first
attempts to read AOT artifacts (JavaScript and cache edn)."
[[filename & more-filenames] read-file-fn cb]
(if filename
(read-file-fn
filename
(fn [source]
(if source
(let [source-cb-value {:lang (filename->lang filename)
:source source}]
(if (or (string/ends-with? filename ".cljs")
(string/ends-with? filename ".cljc"))
(read-file-fn
(replace-extension filename ".js")
(fn [javascript-source]
(if javascript-source
(read-file-fn
(str filename ".cache.edn")
(fn [cache-edn]
(if cache-edn
(cb {:lang :js
:source javascript-source
:cache (parse-edn cache-edn)})
(cb source-cb-value))))
(cb source-cb-value))))
(cb source-cb-value)))
(read-some more-filenames read-file-fn cb))))
(cb nil)))
(defn filenames-to-try
"Produces a sequence of filenames to try reading, in the
order they should be tried."
[src-paths macros path]
(let [extensions (if macros
[".clj" ".cljc"]
[".cljs" ".cljc" ".js"])]
(for [extension extensions
src-path src-paths]
(str src-path "/" path extension))))
(defn skip-load?
"Indicates namespaces that we either don't need to load,
shouldn't load, or cannot load (owing to unresolved
technical issues)."
[name macros]
((if macros
#{'cljs.core
'cljs.pprint
'cljs.env.macros
'cljs.analyzer.macros
'cljs.compiler.macros}
#{'goog.object
'goog.string
'goog.string.StringBuffer
'goog.array
'cljs.core
'cljs.env
'cljs.pprint
'cljs.tools.reader
'clojure.walk}) name))
;; An atom to keep track of things we've already loaded
(def loaded (atom #{}))
(defn load?
"Determines whether the given namespace should be loaded."
[name macros]
(let [do-not-load (or (@loaded [name macros])
(skip-load? name macros))]
(swap! loaded conj [name macros])
(not do-not-load)))
(defn make-load-fn
"Makes a load function that will read from a sequence of src-paths
using a supplied read-file-fn. It returns a cljs.js-compatible
*load-fn*.
Read-file-fn is a 2-arity function (fn [filename source-cb] ...) where
source-cb is itself a function (fn [source] ...) that needs to be called
with the source of the library (as string)."
[src-paths read-file-fn]
(fn [{:keys [name macros path]} cb]
(if (load? name macros)
(if (re-matches #"^goog/.*" path)
(load-goog name cb)
(read-some (filenames-to-try src-paths macros path) read-file-fn cb))
(cb {:source ""
:lang :js}))))
;; Facilities for evaluating JavaScript
(def vm (nodejs/require "vm"))
(defn node-eval
"Evaluates JavaScript in node."
[{:keys [name source]}]
(if-not js/COMPILED
(.runInThisContext vm source (str (munge name) ".js"))
(js/eval source)))
;; Facilities for driving cljs.js
(def load-fn (make-load-fn src-paths node-read-file))
(defn eval-form
"Evaluates a supplied form in a given namespace,
calling back with the evaluation result."
[st ns form cb]
(cljs/eval st
form
{:ns ns
:context :expr
:load load-fn
:eval node-eval
:verbose false}
cb))
(defn run-tests
"Runs the tests."
[]
(let [st (cljs/empty-state)]
(cljs/load-analysis-cache! st 'cljs.core$macros
(parse-edn (node-read-file-sync (str out-dir "/cljs/core$macros.cljc.cache.edn"))))
(eval-form st 'cljs.user
'(ns runner.core
(:require [cljs.test :as test :refer-macros [run-tests]]
[clojure.test.check.generators :as gen]
[clojure.test.check.test]
[clojure.test.check.random-test]
[clojure.test.check.rose-tree-test]
[clojure.test.check.clojure-test-test]))
(fn [{:keys [value error]}]
(if error
(prn error)
(eval-form st 'runner.core
'(run-tests
'clojure.test.check.test
'clojure.test.check.random-test
'clojure.test.check.rose-tree-test
'clojure.test.check.clojure-test-test)
(fn [{:keys [value error]}]
(when error
(prn error)))))))))
(defn -main [& args]
(init-runtime)
(run-tests))
(set! *main-cli-fn* -main)
clojure-test.check-b2ec872/src/test/ 0000775 0000000 0000000 00000000000 14151735222 0017374 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/test/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0021037 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/test/clojure/clojure/ 0000775 0000000 0000000 00000000000 14151735222 0022502 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/test/clojure/clojure/test/ 0000775 0000000 0000000 00000000000 14151735222 0023461 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/ 0000775 0000000 0000000 00000000000 14151735222 0024536 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/clojure_test_test.cljc 0000664 0000000 0000000 00000023770 14151735222 0031145 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.clojure-test-test
(:require [clojure.set :as set]
[clojure.string :as str]
#?@(:cljs
[[cljs.test
:as test
:include-macros true
:refer [test-var]
:refer-macros [is deftest testing]]
[cljs.reader :refer [read-string]]])
#?(:clj
[clojure.test :as test :refer :all])
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.clojure-test :as ct :refer [defspec]]))
(declare ^:dynamic test-report)
(defn capturing-report [reports m]
(swap! reports conj m)
(test-report m))
(defn ^:private capture-test-var
"Returns map of :reports, :report-counters, :out, and :test-out."
[v]
(let [reports (atom [])]
(binding [test-report test/report
test/report (partial capturing-report reports)]
#?(:clj
(binding [*report-counters* (ref *initial-report-counters*)
*test-out* (java.io.StringWriter.)
*testing-contexts* (list)
*testing-vars* (list)]
(let [out (with-out-str (test-var v))]
{:reports @reports
:report-counters @*report-counters*
:out out
:test-out (str *test-out*)}))
:cljs
(binding [test/*current-env* (test/empty-env)]
(let [out (with-out-str (test-var v))]
;; cljs.test doesn't distinguish between *out* and *test-out*
{:reports @reports
:report-counters (:report-counters test/*current-env*)
:out out
:test-out out}))))))
(defspec default-trial-counts
(prop/for-all* [gen/small-integer] (constantly true)))
(deftest can-use-num-tests-default-value
(let [{:keys [reports]} (capture-test-var #'default-trial-counts)
num-tests (->> reports
(filter #(= ::ct/complete (:type %)))
first
::ct/complete
:num-tests)]
(is (= num-tests ct/*default-test-count*))))
(deftest tcheck-116-debug-prn-should-be-optional
(testing "bind ct/*report-completion* to false to supress completion report"
(binding [ct/*report-completion* false]
(let [{:keys [out]} (capture-test-var #'default-trial-counts)]
(is (= out "")))))
(testing "report completions by default"
(let [{:keys [out]} (capture-test-var #'default-trial-counts)
completion (-> out read-string (select-keys [:test-var :result :num-tests]))]
(is (= completion {:test-var "default-trial-counts"
:result true
:num-tests ct/*default-test-count*})))))
(def trial-counts-num-tests 5000)
(defspec trial-counts trial-counts-num-tests
(prop/for-all* [gen/small-integer] (constantly true)))
(deftest can-specify-num-tests
(let [{:keys [reports]} (capture-test-var #'trial-counts)
num-tests (->> reports
(filter #(= ::ct/complete (:type %)))
first
::ct/complete
:num-tests)]
(is (= num-tests trial-counts-num-tests))))
(deftest can-report-completion-with-specified-num-tests
(let [{:keys [out]} (capture-test-var #'trial-counts)
completion (-> out read-string (select-keys [:test-var :result :num-tests]))]
(is (= completion {:test-var "trial-counts"
:result true
:num-tests trial-counts-num-tests}))))
(deftest can-report-trials-with-dots
(binding [ct/*report-trials* true]
(let [{:keys [out]} (capture-test-var #'trial-counts)]
(is (re-matches #?(:clj (java.util.regex.Pattern/compile "(?s)\\.{5}.+")
:cljs #"\.{5}[\s\S]+")
out)))))
(defspec long-running-spec 1000
(prop/for-all*
[]
#(do
#?(:clj
(Thread/sleep 1)
:cljs
(let [start (.valueOf (js/Date.))]
;; let's do some busy waiting for 1 msec, so we avoid setTimeout
;; which would make our test async
(while (= start
(.valueOf (js/Date.)))
(apply + (range 50)))))
true)))
(defn wait-for-clock-tick
"Allow time to progress to avoid timing issues with sub-millisecond code."
[start]
#?(:clj (Thread/sleep 1)
:cljs (while (>= start (.valueOf (js/Date.)))
(apply + (range 50)))))
(deftest can-report-trials-periodically
(binding [ct/*report-trials* ct/trial-report-periodic
ct/*trial-report-period* 500]
(let [last-trial-report @#'ct/last-trial-report]
(testing "test/report with {:type :begin-test-var} increments last-trial-report"
(let [initial-trial-report @last-trial-report]
(wait-for-clock-tick initial-trial-report)
(test/report {:type :begin-test-var})
(is (> @last-trial-report initial-trial-report))))
(testing "test/report with other :type does not increment last-trial-report"
(let [initial-trial-report @last-trial-report]
(wait-for-clock-tick initial-trial-report)
(test/report {:type :end-test-var})
(is (= @last-trial-report initial-trial-report))))
(testing "running the test increments last-trial-report"
(let [initial-trial-report @last-trial-report]
(wait-for-clock-tick initial-trial-report)
(is (re-seq
#"(Passing trial \d{3} / 1000 for long-running-spec\n)+"
(:test-out
(capture-test-var #'long-running-spec))))
(is (> @last-trial-report initial-trial-report)))))))
(defn- vector-elements-are-unique*
[v]
(== (count v) (count (distinct v))))
(def ^:private vector-elements-are-unique
(prop/for-all*
[(gen/vector gen/small-integer)]
vector-elements-are-unique*))
(defspec this-is-supposed-to-fail 100 vector-elements-are-unique)
(deftest can-report-failures
(let [{:keys [test-out]} (capture-test-var #'this-is-supposed-to-fail)
[result-line expected-line actual-line & more] (->> (str/split-lines test-out)
;; skip any ::shrunk messages
(drop-while #(not (re-find #"^FAIL" %))))]
(is (re-find #"^FAIL in \(this-is-supposed-to-fail\) " result-line))
#?(:clj (is (re-find #"\(clojure_test_test\.cljc:\d+\)$" result-line)))
(is (= expected-line "expected: {:result true}"))
(let [actual (read-string (subs actual-line 10))]
(is (set/subset? #{:result :result-data :seed :failing-size :num-tests :fail :shrunk}
(set (keys actual))))
(is (= false (:result actual))))
(is (nil? more))))
(deftest can-report-shrinking
(testing "don't emit Shrinking messages by default"
(let [{:keys [report-counters test-out]} (capture-test-var #'this-is-supposed-to-fail)]
(is (== 1 (:fail report-counters)))
(is (not (re-find #"Shrinking" test-out)))))
(testing "bind *report-shrinking* to true to emit Shrinking messages"
(binding [ct/*report-shrinking* true]
(let [{:keys [report-counters test-out]} (capture-test-var #'this-is-supposed-to-fail)]
(is (== 1 (:fail report-counters)))
(is (re-seq #"Shrinking this-is-supposed-to-fail starting with parameters \[\[[\s\S]+"
test-out))))))
(deftest tcheck-118-pass-shrunk-input-on-to-clojure-test
(let [{trial ::ct/trial, shrinking ::ct/shrinking, shrunk ::ct/shrunk}
(group-by :type (:reports (capture-test-var #'this-is-supposed-to-fail)))]
;; should have had some successful runs because the initial size
;; is too small for duplicates
(is (seq trial))
(is (= 1 (count shrinking)))
(is (not (-> shrinking first ::ct/params first (->> (apply distinct?)))))
(is (= 1 (count shrunk)))
(let [[a b & more] (-> shrunk first ::ct/params first)]
(is (empty? more))
(is (and a b (= a b))))))
(deftest can-report-shrunk
(testing "supress shrunk report when ct/*report-completion* is bound to false"
(binding [ct/*report-completion* false]
(let [{:keys [test-out]} (capture-test-var #'this-is-supposed-to-fail)]
(is (not (re-find #":type :clojure.test.check.clojure-test/shrunk" test-out))))))
(testing "report shrunk by default"
(let [{:keys [test-out]} (capture-test-var #'this-is-supposed-to-fail)]
(is (re-find #":type :clojure.test.check.clojure-test/shrunk" test-out)))))
(defspec this-throws-an-exception
(prop/for-all [x gen/nat]
(throw (ex-info "this property is terrible" {}))))
(deftest can-re-throw-exceptions-to-clojure-test
(let [{:keys [report-counters test-out]} (capture-test-var #'this-throws-an-exception)]
(is (= report-counters {:test 1, :pass 0, :fail 0, :error 1}))
(is (re-find #"ERROR in \(this-throws-an-exception\)" test-out))
;; TCHECK-151
(is (= 1 (count (re-seq #"this property is terrible" test-out)))
"Only prints exceptions twice")))
(defn test-ns-hook
"Run only tests defined by deftest, ignoring those defined by defspec."
[]
(let [tests (->> (vals (ns-interns #?(:clj (find-ns 'clojure.test.check.clojure-test-test)
:cljs 'clojure.test.check.clojure-test-test)))
(filter #(let [m (meta %)]
(and (:test m)
(not (::ct/defspec m)))))
(sort-by #(:line (meta %))))]
(test/test-vars tests)))
clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/random_test.clj 0000664 0000000 0000000 00000010404 14151735222 0027546 0 ustar 00root root 0000000 0000000 (ns clojure.test.check.random-test
"Tests of the custom RNG. This is a little weird since the subject
of the tests (the random number generator) is also the primary
internal driver of the tests, but hopefully it will still be
meaningful."
(:require [clojure.test.check.clojure-test :refer [defspec]]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.random :as random]))
(def gen-split-steps
(gen/list (gen/elements [:left :right])))
(defn apply-split-steps
[rng steps]
(reduce (fn [rng step]
(let [[rng1 rng2] (random/split rng)]
(case step :left rng1 :right rng2)))
rng
steps))
(def gen-seed
(let [gen-int (gen/choose 0 Integer/MAX_VALUE)]
(gen/fmap (fn [[s1 s2]]
(bit-or s1 (bit-shift-left s2 32)))
(gen/tuple gen-int gen-int))))
(defspec determinism-spec
(prop/for-all [seed gen-seed
steps gen-split-steps]
(let [r1 (random/make-random seed)
r2 (random/make-random seed)]
(= (-> r1 (apply-split-steps steps) (random/rand-long))
(-> r2 (apply-split-steps steps) (random/rand-long))))))
(defn get-256-longs
[rng]
(map random/rand-long
(nth (iterate #(mapcat random/split %) [rng]) 8)))
;; this spec is only statistically certain to pass, not logically
;; certain. The probability of a false failure (1/2^16384 or so) is
;; low enough to ignore.
(defspec different-states-spec
(prop/for-all [seed gen-seed
pre-steps gen-split-steps
post-steps-1 gen-split-steps
post-steps-2 gen-split-steps]
(let [r (random/make-random seed)
r' (apply-split-steps r pre-steps)
[r1 r2] (random/split r')
r1' (apply-split-steps r1 post-steps-1)
r2' (apply-split-steps r2 post-steps-2)]
;; r1' and r2' should not somehow be in the same state
(not= (get-256-longs r1')
(get-256-longs r2')))))
;; Tests of the particular JavaUtilSplittableRandom impl, by
;; comparing with java.util.SplittableRandom on Java 8
(when (try (Class/forName "java.util.SplittableRandom")
(catch ClassNotFoundException e))
(eval
'(defspec java-util-splittable-random-spec
(prop/for-all [seed gen-seed
steps gen-split-steps]
(let [immutable-rng (apply-split-steps
(random/make-java-util-splittable-random seed)
steps)
mutable-rng
^java.util.SplittableRandom
(reduce (fn [^java.util.SplittableRandom rng step]
(let [rng2 (.split rng)]
(case step :left rng :right rng2)))
(java.util.SplittableRandom. seed)
steps)]
(= (random/rand-long immutable-rng)
(.nextLong mutable-rng))))))
;; same test but for rand-double
(eval
'(defspec java-util-splittable-random-spec-double
(prop/for-all [seed gen-seed
steps gen-split-steps]
(let [immutable-rng (apply-split-steps
(random/make-java-util-splittable-random seed)
steps)
mutable-rng
^java.util.SplittableRandom
(reduce (fn [^java.util.SplittableRandom rng step]
(let [rng2 (.split rng)]
(case step :left rng :right rng2)))
(java.util.SplittableRandom. seed)
steps)]
(= (random/rand-double immutable-rng)
(.nextDouble mutable-rng)))))))
(defspec split-n-spec 40
(prop/for-all [seed gen-seed
n gen/nat]
(let [rng (random/make-random seed)]
;; checking that split-n returns the same generators that we
;; would get by doing a particular series of splits manually
(= (map random/rand-long (random/split-n rng n))
(map random/rand-long
(if (zero? n)
[]
(loop [v [], rng rng]
(if (= (dec n) (count v))
(conj v rng)
(let [[rng1 rng2] (random/split rng)]
(recur (conj v rng2) rng1))))))))))
clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/random_test.cljs 0000664 0000000 0000000 00000003264 14151735222 0027737 0 ustar 00root root 0000000 0000000 (ns clojure.test.check.random-test
"Testing that the cljs impl matches the clojure impl."
(:require [cljs.test :refer-macros [deftest is]]
[clojure.test.check.random :as random]))
(deftest longs-test
;; comparing with this code run on clj-jvm:
(comment
(-> 42
(random/make-java-util-splittable-random)
(random/split-n 17)
(->> (mapcat random/split)
(map random/rand-long)
(reduce bit-xor))
(str))
=>
"5298131359241775269")
(is (= "5298131359241775269"
(-> 42
(random/make-java-util-splittable-random)
(random/split-n 17)
(->> (mapcat random/split)
(map random/rand-long)
(reduce #(.xor %1 %2)))
(str)))))
(deftest doubles-test
;; comparing with this code run on clj-jvm:
(comment
(-> -42
(random/make-java-util-splittable-random)
(random/split-n 17)
(->> (mapcat random/split)
(map random/rand-double)
(reduce +))
(str))
=>
"17.39141655134964")
(is (= "17.39141655134964"
(-> -42
(random/make-java-util-splittable-random)
(random/split-n 17)
(->> (mapcat random/split)
(map random/rand-double)
(reduce +))
(str)))))
(deftest auto-seeding-test
(is (distinct? (random/rand-double (random/make-random))
(random/rand-double (random/make-random))
(random/rand-double (random/make-random))
(random/rand-double (random/make-random)))
"Each call to make-random should return a different RNG."))
clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/results_test.cljc 0000664 0000000 0000000 00000001620 14151735222 0030132 0 ustar 00root root 0000000 0000000 ; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.results-test
(:require #?(:cljs
[cljs.test :as test :refer-macros [are deftest testing is]])
#?(:clj
[clojure.test :refer :all])
[clojure.test.check.results :as results]))
(deftest default-passing-values
(is (not (results/pass? nil)))
(is (not (results/pass? false)))
(are [x] (results/pass? x)
:keyword
'symbol
"string"
[]
{}
#{}
()
42
42.0
true))
clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/rose_tree_test.cljc 0000664 0000000 0000000 00000002406 14151735222 0030423 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.rose-tree-test
(:require [clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.rose-tree :as rose]
[clojure.test.check.clojure-test :as ct :refer [defspec]]))
(defn depth-one-children
[rose]
(into [] (map rose/root (rose/children rose))))
(defn depth-one-and-two-children
[rose]
(let [the-children (rose/children rose)]
(into []
(concat
(map rose/root the-children)
(map rose/root (mapcat rose/children the-children))))))
(defspec test-collapse-rose
100
(prop/for-all [i gen/small-integer]
(let [tree (#'gen/int-rose-tree i)]
(= (depth-one-and-two-children tree)
(depth-one-children (rose/collapse tree))))))
clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/test.cljc 0000664 0000000 0000000 00000147215 14151735222 0026364 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey, Reid Draper, and contributors.
; All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.test.check.test
#?(:cljs
(:refer-clojure :exclude [infinite?]))
(:require #?(:cljs
[cljs.test :as test :refer-macros [deftest testing is]])
#?(:clj
[clojure.test :refer :all])
[clojure.test.check :as tc]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.rose-tree :as rose]
[clojure.test.check.random :as random]
[clojure.test.check.results :as results]
[clojure.test.check.clojure-test :as ct :refer [defspec]]
#?(:clj [clojure.test.check.test-specs :as specs])
#?(:cljs [clojure.test.check.random.longs :as rl])
#?(:clj [clojure.edn :as edn]
:cljs [cljs.reader :as edn :refer [read-string]])))
(def gen-seed
(let [gen-int (gen/choose 0 0x100000000)]
(gen/fmap (fn [[s1 s2]]
(bit-or s1 (bit-shift-left s2 32)))
(gen/tuple gen-int gen-int))))
(deftest generators-are-generators
(testing "generator? returns true when called with a generator"
(is (gen/generator? gen/small-integer))
(is (gen/generator? (gen/vector gen/small-integer)))
(is (gen/generator? (gen/return 5)))))
(deftest values-are-not-generators
(testing "generator? returns false when called with a value"
(is (not (gen/generator? 5)))
(is (not (gen/generator? int)))
(is (not (gen/generator? [1 2 3])))))
;; plus and 0 form a monoid
;; ---------------------------------------------------------------------------
(defn passes-monoid-properties
[a b c]
(and (= (+ 0 a) a)
(= (+ a 0) a)
(= (+ a (+ b c)) (+ (+ a b) c))))
(deftest plus-and-0-are-a-monoid
(testing "+ and 0 form a monoid"
(is (let [p (prop/for-all* [gen/small-integer gen/small-integer gen/small-integer] passes-monoid-properties)]
(:result
(tc/quick-check 1000 p)))))
;; NOTE: no ratios in ClojureScript - David
#?(:clj
(testing "with ratios as well"
(is (let [p (prop/for-all* [gen/ratio gen/ratio gen/ratio] passes-monoid-properties)]
(:result
(tc/quick-check 1000 p)))))))
;; reverse
;; ---------------------------------------------------------------------------
(defn reverse-equal?-helper
[l]
(let [r (vec (reverse l))]
(and (= (count l) (count r))
(= (seq l) (rseq r)))))
(deftest reverse-equal?
(testing "For all vectors L, reverse(reverse(L)) == L"
(is (let [p (prop/for-all* [(gen/vector gen/small-integer)] reverse-equal?-helper)]
(:result (tc/quick-check 1000 p))))))
;; failing reverse
;; ---------------------------------------------------------------------------
(deftest bad-reverse-test
(testing "For all vectors L, L == reverse(L). Not true"
(is (false?
(let [p (prop/for-all* [(gen/vector gen/small-integer)] #(= (reverse %) %))]
(:result (tc/quick-check 1000 p)))))))
;; failing element remove
;; ---------------------------------------------------------------------------
(defn first-is-gone
[l]
(not (some #{(first l)} (rest l))))
(deftest bad-remove
(testing "For all vectors L, if we remove the first element E, E should not
longer be in the list. (This is a false assumption)"
(is (false?
(let [p (prop/for-all* [(gen/vector gen/small-integer)] first-is-gone)]
(:result (tc/quick-check 1000 p)))))))
;; exceptions shrink and return as result
;; ---------------------------------------------------------------------------
(def exception (#?(:clj Exception. :cljs js/Error.) "I get caught"))
(defn exception-thrower
[& args]
(throw exception))
(deftest exceptions-are-caught
(testing "Exceptions during testing are caught. They're also shrunk as long
as they continue to throw."
(is (= [exception [0]]
(let [result
(tc/quick-check
1000 (prop/for-all* [gen/small-integer] exception-thrower))]
[(::prop/error (:result-data result))
(get-in result [:shrunk :smallest])])))))
;; result-data
;; ---------------------------------------------------------------------------
(deftest custom-result-data-is-returned-on-failure
(is (= {:foo :bar :baz [42]}
(:result-data
(tc/quick-check 100
(prop/for-all [x gen/nat]
(reify results/Result
(pass? [_] false)
(result-data [_]
{:foo :bar :baz [42]}))))))))
;; TCHECK-131
(deftest exception-results-are-treated-as-failures-for-backwards-compatibility
(doseq [e [(#?(:clj Exception.
:cljs js/Error.)
"Let's pretend this was thrown.")
#?(:clj (Error. "Not an Exception, technically"))]]
(is (false? (:pass?
(tc/quick-check 100
(prop/for-all [x gen/nat]
e)))))))
;; TCHECK-134
#?(:cljs
(defn multiply-check [x y]
(let [goog-math-long (.multiply x y)
test-check (rl/* x y)]
(and (== (.-high_ goog-math-long) (.-high_ test-check))
(== (.-low_ goog-math-long) (.-low_ test-check))))))
#?(:cljs
(deftest multiply-test-check-and-goog
(testing "For goog.math.Long's test.check multiply is the same as goog.math.Long.multiply"
(is (:result
(let [grl @#'gen/gen-raw-long]
(tc/quick-check 1000 (prop/for-all* [grl grl] multiply-check))))))))
;; Count and concat work as expected
;; ---------------------------------------------------------------------------
(defn concat-counts-correct
[a b]
(= (count (concat a b))
(+ (count a) (count b))))
(deftest count-and-concat
(testing "For all vectors A and B:
length(A + B) == length(A) + length(B)"
(is (:result
(let [p (prop/for-all* [(gen/vector gen/small-integer)
(gen/vector gen/small-integer)] concat-counts-correct)]
(tc/quick-check 1000 p))))))
;; Interpose (Count)
;; ---------------------------------------------------------------------------
(defn interpose-twice-the-length ;; (or one less)
[v]
(let [interpose-count (count (interpose :i v))
original-count (count v)]
(or
(= (* 2 original-count) interpose-count)
(= (dec (* 2 original-count)) interpose-count))))
(deftest interpose-creates-sequence-twice-the-length
(testing "Interposing a collection with a value makes its count
twice the original collection, or ones less."
(is (:result
(tc/quick-check 1000 (prop/for-all [v (gen/vector gen/small-integer)] (interpose-twice-the-length v)))))))
;; Lists and vectors are equivalent with seq abstraction
;; ---------------------------------------------------------------------------
(defn list-vector-round-trip-equiv
[a]
;; NOTE: can't use `(into '() ...)` here because that
;; puts the list in reverse order. clojure.test.check found that bug
;; pretty quickly...
(= a (apply list (vec a))))
(deftest list-and-vector-round-trip
(is (:result
(tc/quick-check
1000 (prop/for-all*
[(gen/list gen/small-integer)] list-vector-round-trip-equiv)))))
;; keyword->string->keyword roundtrip
;; ---------------------------------------------------------------------------
;; NOTE cljs: this is one of the slowest due to how keywords are constructed
;; drop N to 100 - David
(deftest keyword-symbol-serialization-roundtrip
(testing "For all keywords and symbol, (comp read-string pr-str) is identity."
(is (:result
(tc/quick-check #?(:clj 1000 :cljs 100)
(prop/for-all [x (gen/one-of [gen/keyword
gen/keyword-ns
gen/symbol
gen/symbol-ns])]
(= x (read-string (pr-str x)))))))))
;; Boolean and/or
;; ---------------------------------------------------------------------------
(deftest boolean-or
(testing "`or` with true and anything else should be true"
(is (:result (tc/quick-check
1000 (prop/for-all*
[gen/boolean] #(or % true)))))))
(deftest boolean-and
(testing "`and` with false and anything else should be false"
(is (:result (tc/quick-check
1000 (prop/for-all*
[gen/boolean] #(not (and % false))))))))
;; Sorting
;; ---------------------------------------------------------------------------
(defn elements-are-in-order-after-sorting
[v]
(every? identity (map <= (partition 2 1 (sort v)))))
(deftest sorting
(testing "For all vectors V, sorted(V) should have the elements in order"
(is (:result
(tc/quick-check
1000
(prop/for-all*
[(gen/vector gen/small-integer)] elements-are-in-order-after-sorting))))))
;; Constant generators
;; ---------------------------------------------------------------------------
;; A constant generator always returns its created value
(defspec constant-generators 100
(prop/for-all [a (gen/return 42)]
(= a 42)))
(deftest constant-generators-dont-shrink
(testing "Generators created with `gen/return` should not shrink"
(is (= [42]
(let [result (tc/quick-check 100
(prop/for-all
[a (gen/return 42)]
false))]
(-> result :shrunk :smallest))))))
;; Tests are deterministic
;; ---------------------------------------------------------------------------
(defn vector-elements-are-unique
[v]
(== (count v) (count (distinct v))))
(defn dissoc-timing-keys
[m]
(-> m
(dissoc :failed-after-ms)
(update :shrunk dissoc :time-shrinking-ms)))
(defn unique-test
[seed]
(tc/quick-check 1000
(prop/for-all*
[(gen/vector gen/small-integer)] vector-elements-are-unique)
:seed seed))
(defn equiv-runs
[seed]
(= (dissoc-timing-keys (unique-test seed))
(dissoc-timing-keys (unique-test seed))))
(deftest tests-are-deterministic
(testing "If two runs are started with the same seed, they should
return the same results."
(is (:result
(tc/quick-check 1000 (prop/for-all* [gen/small-integer] equiv-runs))))))
;; Generating basic generators
;; --------------------------------------------------------------------------
(deftest generators-test
(let [t (fn [generator pred]
(is (:result (tc/quick-check 100
(prop/for-all [x generator]
(pred x))))))
is-char-fn #?(:clj char? :cljs string?)]
(testing "keyword" (t gen/keyword keyword?))
;; No ratio in cljs
#?@(:clj [(testing "ratio" (t gen/ratio (some-fn ratio? integer?)))
(testing "byte" (t gen/byte #(instance? Byte %)))
(testing "bytes" (t gen/bytes #(instance? (Class/forName "[B") %)))]) (testing "char" (t gen/char is-char-fn))
(testing "char-ascii" (t gen/char-ascii is-char-fn))
(testing "char-alphanumeric" (t gen/char-alphanumeric is-char-fn))
(testing "string" (t gen/string string?))
(testing "string-ascii" (t gen/string-ascii string?))
(testing "string-alphanumeric" (t gen/string-alphanumeric string?))
(testing "vector" (t (gen/vector gen/small-integer) vector?))
(testing "list" (t (gen/list gen/small-integer) list?))
(testing "map" (t (gen/map gen/small-integer gen/small-integer) map?))))
;; such-that
;; --------------------------------------------------------------------------
(deftest such-that-allows-customizing-exceptions
(is (thrown-with-msg? #?(:clj Exception :cljs js/Error) #"Oh well!"
(gen/generate
(gen/such-that
#(apply distinct? %)
(gen/vector gen/boolean 5)
{:ex-fn (fn [{:keys [pred gen max-tries]}]
(is (and pred gen max-tries))
(#?(:clj Exception. :cljs js/Error.) "Oh well!"))})))))
;; Distinct collections
;; --------------------------------------------------------------------------
(def gen-distinct-generator
(gen/elements [gen/list-distinct gen/vector-distinct
gen/set gen/sorted-set]))
(def gen-size-bounds-and-pred
"Generates [pred size-opts], where size-opts is a map to pass to
distinct generators, and pred is a predicate on the size of a
collection, to check that it matches the options."
(gen/one-of
[(gen/return [(constantly true) {}])
(gen/fmap (fn [num-elements]
[#{num-elements} {:num-elements num-elements}])
gen/nat)
(gen/fmap (fn [min-elements]
[#(<= min-elements %) {:min-elements min-elements}])
gen/nat)
(gen/fmap (fn [max-elements]
[#(<= % max-elements) {:max-elements max-elements}])
gen/nat)
(gen/fmap (fn [bounds]
(let [[min-elements max-elements] (sort bounds)]
[#(<= min-elements % max-elements)
{:min-elements min-elements
:max-elements max-elements}]))
(gen/tuple gen/nat gen/nat))]))
(defspec map-honors-size-opts
(prop/for-all [[the-map size-pred _]
(gen/bind gen-size-bounds-and-pred
(fn [[pred size-opts]]
(gen/fmap #(vector % pred size-opts)
(gen/map gen/string gen/nat size-opts))))]
(size-pred (count the-map))))
(defspec distinct-collections-honor-size-opts
(prop/for-all [[the-coll size-pred _]
(gen/bind (gen/tuple gen-size-bounds-and-pred
gen-distinct-generator)
(fn [[[pred size-opts] coll-gen]]
(gen/fmap #(vector % pred size-opts)
(coll-gen gen/string size-opts))))]
(size-pred (count the-coll))))
(defspec distinct-collections-are-distinct
(prop/for-all [the-coll
(gen/bind (gen/tuple gen-size-bounds-and-pred
gen-distinct-generator)
(fn [[[_ size-opts] coll-gen]]
(coll-gen gen/string size-opts)))]
(or (empty? the-coll)
(apply distinct? the-coll))))
(defspec distinct-by-collections-are-distinct-by 20
(let [key-fn #(quot % 7)]
(prop/for-all [the-coll
(gen/bind (gen/tuple gen-size-bounds-and-pred
(gen/elements
[(partial gen/vector-distinct-by key-fn)
(partial gen/list-distinct-by key-fn)])
gen-distinct-generator)
(fn [[[_ size-opts] coll-gen]]
(coll-gen (gen/choose -10000 10000) size-opts)))]
(or (empty? the-coll)
(apply distinct? (map key-fn the-coll))))))
(deftest distinct-generators-throw-when-necessary
;; I tried using `are` here but it breaks in cljs
(doseq [g [gen/vector-distinct
gen/list-distinct
gen/set
gen/sorted-set
(partial gen/vector-distinct-by pr-str)
(partial gen/list-distinct-by pr-str)]]
(is (thrown-with-msg? #?(:clj Exception :cljs js/Error) #"Couldn't generate enough distinct elements"
(first (gen/sample
(g gen/boolean {:min-elements 5})))))
(is (thrown-with-msg? #?(:clj Exception :cljs js/Error) #"foo bar"
(first (gen/sample
(g gen/boolean {:min-elements 5
:ex-fn (fn [arg] (ex-info "foo bar" arg))}))))))
(is (thrown? #?(:clj Exception :cljs js/Error)
(first (gen/sample
(gen/map gen/boolean gen/nat {:min-elements 5}))))))
(defspec shrinking-respects-distinctness-and-sizing 20
(prop/for-all [g gen-distinct-generator
seed gen-seed
size (gen/choose 1 20)
[pred opts] gen-size-bounds-and-pred]
(let [rose-tree (gen/call-gen (g (gen/choose 0 1000) opts)
(random/make-random seed) size)
;; inevitably some of these will be way too long to actually
;; test, so this is the easiest thing to do :/
vals (take 1000 (rose/seq rose-tree))]
(every? (fn [coll]
(and (or (empty? coll)
(apply distinct? coll))
(pred (count coll))))
vals))))
(defspec distinct-generators-can-shrink-in-size 20
(prop/for-all [g gen-distinct-generator
seed gen-seed
size (gen/choose 1 20)]
(let [rose-tree (gen/call-gen (g (gen/choose 1 1000))
(random/make-random seed) size)
a-shrink (->> rose-tree
(iterate #(first (rose/children %)))
(take-while identity)
(map rose/root))]
(and (apply > (map #(reduce + %) a-shrink))
(empty? (last a-shrink))))))
(defspec distinct-collections-are-not-biased-in-their-ordering 5
(prop/for-all [g (gen/elements [gen/vector-distinct gen/list-distinct])
seed gen-seed]
(let [rng (random/make-random seed)]
(every?
(->> (gen/lazy-random-states rng)
(take 1000)
(map #(rose/root (gen/call-gen (g gen/nat {:num-elements 3, :max-tries 100}) % 0)))
(set))
[[0 1 2] [0 2 1] [1 0 2] [1 2 0] [2 0 1] [2 1 0]]))))
(defspec distinct-collections-with-few-possible-values 20
(prop/for-all [boolean-sets (gen/vector (gen/resize 5 (gen/set gen/boolean)) 1000)]
(= 4 (count (distinct boolean-sets)))))
(deftest can't-generate-set-of-five-booleans
(let [ex (try
(gen/generate (gen/set gen/boolean {:num-elements 5}))
(is false)
(catch #?(:clj Exception :cljs js/Error) e
e))]
(is (re-find #"Couldn't generate enough distinct elements"
#? (:clj (.getMessage ^Exception ex) :cljs (.-message ex))))
(is (= 5 (-> ex ex-data :num-elements)))))
;; Generating proper matrices
;; ---------------------------------------------------------------------------
(defn proper-matrix?
"Check if provided nested vectors form a proper matrix — that is, all nested
vectors have the same length"
[mtx]
(let [first-size (count (first mtx))]
(every? (partial = first-size) (map count (rest mtx)))))
(deftest proper-matrix-test
(testing "can generate proper matrices"
(is (:result (tc/quick-check
100 (prop/for-all
[mtx (gen/vector (gen/vector gen/small-integer 3) 3)]
(proper-matrix? mtx)))))))
(def bounds-and-vector
(gen/bind (gen/tuple gen/s-pos-int gen/s-pos-int)
(fn [[a b]]
(let [minimum (min a b)
maximum (max a b)]
(gen/tuple (gen/return [minimum maximum])
(gen/vector gen/small-integer minimum maximum))))))
(deftest proper-vector-test
(testing "can generate vectors with sizes in a provided range"
(is (:result (tc/quick-check
100 (prop/for-all
[b-and-v bounds-and-vector]
(let [[[minimum maximum] v] b-and-v
c (count v)]
(and (<= c maximum)
(>= c minimum)))))))))
;; Tuples and Pairs retain their count during shrinking
;; ---------------------------------------------------------------------------
(defn n-int-generators
[n]
(vec (repeat n gen/small-integer)))
(def tuples
[(apply gen/tuple (n-int-generators 1))
(apply gen/tuple (n-int-generators 2))
(apply gen/tuple (n-int-generators 3))
(apply gen/tuple (n-int-generators 4))
(apply gen/tuple (n-int-generators 5))
(apply gen/tuple (n-int-generators 6))])
(defn get-tuple-gen
[index]
(nth tuples (dec index)))
(defn inner-tuple-property
[size]
(prop/for-all [t (get-tuple-gen size)]
false))
(defspec tuples-retain-size-during-shrinking 1000
(prop/for-all [index (gen/choose 1 6)]
(let [result (tc/quick-check
100 (inner-tuple-property index))]
(= index (count (-> result
:shrunk :smallest first))))))
;; Bind works
;; ---------------------------------------------------------------------------
(def nat-vec
(gen/such-that not-empty
(gen/vector gen/nat)))
(def vec-and-elem
(gen/bind nat-vec
(fn [v]
(gen/tuple (gen/elements v) (gen/return v)))))
(defspec element-is-in-vec 100
(prop/for-all [[element coll] vec-and-elem]
(some #{element} coll)))
;; fmap is respected during shrinking
;; ---------------------------------------------------------------------------
(def plus-fifty
(gen/fmap (partial + 50) gen/nat))
(deftest f-map-respected-during-shrinking
(testing "Generators created with fmap should have that function applied
during shrinking"
(is (= [50]
(let [result (tc/quick-check 100
(prop/for-all
[a plus-fifty]
false))]
(-> result :shrunk :smallest))))))
;; Collections can shrink reasonably fast; regression for TCHECK-94
;; ---------------------------------------------------------------------------
(defspec collections-shrink-quickly 200
(prop/for-all [seed gen-seed
coll-gen (let [coll-generators
[gen/vector gen/list #_#_#_#_gen/set
gen/vector-distinct gen/list-distinct
#(gen/map % %)]
coll-gen-gen (gen/elements coll-generators)]
(gen/one-of [coll-gen-gen
#_
(gen/fmap (fn [[g1 g2]] (comp g1 g2))
(gen/tuple coll-gen-gen
coll-gen-gen))]))]
(let [g (coll-gen (gen/frequency [[100 gen/large-integer]
[1 (gen/return :oh-no!)]]))
scalars #(remove coll? (tree-seq coll? seq %))
;; technically this could fail to fail, but running the test
;; 10000 times should ensure it doesn't
result (tc/quick-check 10000
(prop/for-all [coll g]
(->> coll
(scalars)
(not-any? #{:oh-no!})))
:seed seed)
failing-size (-> result :fail first scalars count)
{:keys [smallest total-nodes-visited]} (:shrunk result)]
(and (< (count (scalars (first smallest))) 4)
;; shrink-time should be in proportion to the log of the
;; collection size; multiplying by 3 to add some wiggle
;; room
(< total-nodes-visited (+ 5 (* 3 (Math/log failing-size))))))))
;; gen/small-integer returns an integer when size is a double; regression for TCHECK-73
;; ---------------------------------------------------------------------------
(def gen-double
(gen/fmap (fn [[x y]] (double (+ x (/ y 10))))
(gen/tuple gen/nat (gen/choose 0 9))))
(defspec gen-int-with-double-size 1000
(prop/for-all [size gen-double]
(integer? (gen/generate gen/small-integer size))))
;; recursive-gen doesn't change ints to doubles; regression for TCHECK-73
;; ---------------------------------------------------------------------------
(defspec recursive-generator-test 100
(let [btree* (fn [g] (gen/hash-map
:value gen/small-integer
:left g
:right g))
btree (gen/recursive-gen btree* (gen/return nil))
valid? (fn valid? [tree]
(and (integer? (:value tree))
(or (nil? (:left tree))
(valid? (:left tree)))
(or (nil? (:right tree))
(valid? (:right tree)))))]
(prop/for-all [t btree] (or (nil? t)
(valid? t)))))
;; NOTE cljs: adjust for JS numerics - NB
#?(:clj
(deftest calc-long-increasing
;; access internal gen/calc-long function for testing
(are [low high] (apply < (map #(@#'gen/calc-long % low high) (range 0.0 0.9999 0.111)))
;; low and high should not be too close, 100 is a reasonable spread
(- Long/MAX_VALUE 100) Long/MAX_VALUE
Long/MIN_VALUE (+ Long/MIN_VALUE 100)
Long/MIN_VALUE 0
0 100
-100 0
0 Long/MAX_VALUE
Long/MIN_VALUE Long/MAX_VALUE)))
;; edn rountrips
;; ---------------------------------------------------------------------------
(defn edn-roundtrip?
[value]
(= value (-> value prn-str edn/read-string)))
(def infinities #{1E1000 -1E1000})
(def infinity-syntax?
(edn-roundtrip? (first infinities)))
(defspec edn-roundtrips 200
(prop/for-all [a gen/any-equatable]
(or (edn-roundtrip? a)
;; this keeps the tests passing for clojure 1.8 and older
(and (not infinity-syntax?)
(->> a
(tree-seq coll? seq)
(some infinities))))))
;; not-empty works
;; ---------------------------------------------------------------------------
(defspec not-empty-works 100
(prop/for-all [v (gen/not-empty (gen/vector gen/boolean))]
(not-empty v)))
;; no-shrink works
;; ---------------------------------------------------------------------------
(defn run-no-shrink
[i]
(tc/quick-check 100
(prop/for-all [coll (gen/vector gen/nat)]
(some #{i} coll))))
(defspec no-shrink-works 100
(prop/for-all [i gen/nat]
(let [result (run-no-shrink i)]
(if (:result result)
true
(= (:fail result)
(-> result :shrunk :smallest))))))
;; elements works with a variety of input
;; ---------------------------------------------------------------------------
(deftest elements-with-empty
(is (thrown? #?(:clj AssertionError :cljs js/Error)
(gen/elements ()))))
(defspec elements-with-a-set 100
(prop/for-all [num (gen/elements #{9 10 11 12})]
(<= 9 num 12)))
;; choose respects bounds during shrinking
;; ---------------------------------------------------------------------------
(def range-gen
(gen/fmap (fn [[a b]]
[(min a b) (max a b)])
(gen/tuple gen/small-integer gen/small-integer)))
(defspec choose-respects-bounds-during-shrinking 100
(prop/for-all [[mini maxi] range-gen
random-seed gen/nat
size gen/nat]
(let [tree (gen/call-gen
(gen/choose mini maxi)
(random/make-random random-seed)
size)]
(every?
#(and (<= mini %) (>= maxi %))
(rose/seq tree)))))
;; rand-range copes with full range of longs as bounds
;; ---------------------------------------------------------------------------
;; NOTE cljs: need to adjust for JS numerics - David
#?(:clj
(deftest rand-range-copes-with-full-range-of-longs
(let [[low high] (reduce
(fn [[low high :as margins] x]
(cond
(< x low) [x high]
(> x high) [low x]
:else margins))
[Long/MAX_VALUE Long/MIN_VALUE]
; choose uses rand-range directly, reasonable proxy for its
; guarantees
(take 1e6 (gen/sample-seq (gen/choose Long/MIN_VALUE Long/MAX_VALUE))))]
(is (< low high))
(is (< low Integer/MIN_VALUE))
(is (> high Integer/MAX_VALUE)))))
;; rand-range yields values inclusive of both lower & upper bounds provided to it
;; further, that generators that use rand-range use its full range of values
;; ---------------------------------------------------------------------------
(deftest rand-range-uses-inclusive-bounds
(let [bounds [5 7]
rand-range (fn [r] (apply #'gen/rand-range r bounds))]
(loop [trials 0
bounds (set bounds)
r (random/make-random)]
(cond
(== trials 10000)
;; the probability of this happening by chance is roughly 1 in
;; 10^1761 so we can safely assume something's wrong if it does
(is nil "rand-range didn't return both of its bounds after 10000 trials")
(empty? bounds) (is true)
:else (let [[r1 r2] (random/split r)]
(recur (inc trials) (disj bounds (rand-range r1)) r2))))))
(deftest elements-generates-all-provided-values
(let [options [:a 42 'c/d "foo"]]
(is (->> (reductions
disj
(set options)
(gen/sample-seq (gen/elements options)))
(take 10000)
(some empty?))
;; the probability of this happening by chance is roughly 1 in
;; 10^1249 so we can safely assume something's wrong if it does
"elements didn't return all of its candidate values after 10000 trials")))
;; shuffling a vector generates a permutation of that vector
;; ---------------------------------------------------------------------------
(def original-vector-and-permutation
(gen/bind (gen/vector gen/small-integer)
#(gen/tuple (gen/return %) (gen/shuffle %))))
(defspec shuffled-vector-is-a-permutation-of-original 100
(prop/for-all [[coll permutation] original-vector-and-permutation]
(= (sort coll) (sort permutation))))
;; UUIDs
;; ---------------------------------------------------------------------------
(defspec uuid-generates-uuids
(prop/for-all [uuid gen/uuid]
(and (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) uuid)
;; check that we got the special fields right
(re-matches #"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"
(str uuid)))))
(deftest uuid-generates-distinct-values
(is (apply distinct?
(gen/sample gen/uuid 1000))))
;; large integers
;; ---------------------------------------------------------------------------
(defn gen-bounds-and-pred
"Generates a map that may contain :min and :max, and a pred
that checks a number satisfies those constraints."
[num-gen]
(gen/one-of [(gen/return [{} (constantly true)])
(gen/fmap (fn [x] [{:min x} #(<= x %)]) num-gen)
(gen/fmap (fn [x] [{:max x} #(<= % x)]) num-gen)
(gen/fmap (fn [bounds]
(let [[lb ub] (sort bounds)]
[{:min lb :max ub} #(<= lb % ub)]))
(gen/tuple num-gen num-gen))]))
(def MAX_INTEGER #?(:clj Long/MAX_VALUE :cljs (dec (apply * (repeat 53 2)))))
(def MIN_INTEGER #?(:clj Long/MIN_VALUE :cljs (- MAX_INTEGER)))
(defn native-integer?
[x]
#?(:clj (instance? Long x)
:cljs (and (integer? x) (<= MIN_INTEGER x MAX_INTEGER))))
(defspec large-integer-spec 500
(prop/for-all [x gen/large-integer]
(native-integer? x)))
(defspec large-integer-bounds-spec 500
(prop/for-all [[opts pred x]
(gen/bind (gen-bounds-and-pred gen/large-integer)
(fn [[opts pred]]
(gen/fmap #(vector opts pred %)
(gen/large-integer* opts))))]
(pred x)))
(defspec large-integer-distribution-spec 5
(prop/for-all [xs (gen/no-shrink
(gen/vector (gen/resize 150 gen/large-integer) 10000))]
(every? (fn [[lb ub]]
(some #(<= lb % ub) xs))
[[0 10]
[-10 -1]
[10000 100000]
[-100000 -10000]
[MIN_INTEGER (/ MIN_INTEGER 2)]
[(/ MAX_INTEGER 2) MAX_INTEGER]])))
;; doubles
;; ---------------------------------------------------------------------------
(defn infinite?
[x]
#?(:clj (Double/isInfinite x)
:cljs (or (= @#'gen/POS_INFINITY x)
(= @#'gen/NEG_INFINITY x))))
(defn nan?
[x]
#?(:clj (Double/isNaN x)
:cljs (.isNaN js/Number x)))
(defspec double-test 100
(prop/for-all [x gen/double]
#?(:clj (instance? Double x)
:cljs (number? x))))
(defspec double-distribution-test 5
(prop/for-all [xs (gen/no-shrink
(gen/vector (gen/resize 100 gen/double) 10000))]
(and (some #(= @#'gen/POS_INFINITY %) xs)
(some #(= @#'gen/NEG_INFINITY %) xs)
(some nan? xs)
(every? (fn [[lb ub]]
(some #(<= lb % ub) xs))
[[-1e303 -1e200]
[-1e200 -1e100]
[-1e100 -1.0]
[0.0 0.0]
[1.0 1e100]
[1e100 1e200]
[1e200 1e303]])
(let [mods (->> xs
(remove infinite?)
(remove nan?)
(map #(mod % 1.0)))]
(every? (fn [[lb ub]]
(some #(<= lb % ub) mods))
[[0.0 0.1]
[0.1 0.2]
[0.25 0.75]
[0.8 0.9]
[0.9 1.0]])))))
(defspec double-bounds-spec 500
(prop/for-all [[opts pred x]
(gen/bind (gen-bounds-and-pred (gen/double* {:infinite? false, :NaN? false}))
(fn [[opts pred]]
(gen/fmap #(vector opts pred %)
(gen/double* (assoc opts
:infinite? false,
:NaN? false)))))]
(pred x)))
;; bigints
;; ---------------------------------------------------------------------------
#?(:clj
(defspec size-bounded-bigint-generates-integers 1000
(prop/for-all [x gen/size-bounded-bigint]
(integer? x))))
#?(:clj
(defspec size-bounded-bigint-distribution-test 6
(prop/for-all [[xs size]
;; the 200 restriction (a NOOP with only 6 trials)
;; relates to the probability assertions below,
;; and also keeps the test from using too much
;; memory
(gen/scale #(min 200 (* 40 %))
(gen/tuple
;; no-shrink because it would screw up
;; the distribution, so most of the
;; shrink would be meaningless
(gen/no-shrink
(gen/vector gen/size-bounded-bigint 10000))
(gen/sized gen/return)))]
(let [ex-ub (apply * (repeat (* 6 size) 2N))
ex-lb (- ex-ub)]
;; TODO: Would be nice to rewrite this so we also get
;; assertions about other ranges, like smaller numbers
(and
;; everything's in the defined range
(every? #(< ex-lb % ex-ub) xs)
;; testing that the numbers get reasonably close to the
;; bounds
;;
;; chose 32 here so that the probability of false-positive
;; failure is roughly 10^-17; I wish we could test a tighter
;; bound, like (quot ex-ub 2), but there's only a 1/1200
;; chance of a given integer falling in that range at
;; size=200, so this test would fail some 10^-5 of the time,
;; which is too often IMO
(let [ub* (quot ex-ub 32)]
(->> xs
(apply max)
(<= ub*)))
(let [lb* (quot ex-lb 32)]
(->> xs
(apply min)
(>= lb*))))))))
#?(:clj
(defspec size-bounded-bigint-shrinks-effectively 50
(prop/for-all [bound (gen/scale #(min % 150) gen/size-bounded-bigint)
seed gen-seed]
(let [prop (if (neg? bound)
(prop/for-all [n gen/size-bounded-bigint]
(not (<= n bound)))
(prop/for-all [n gen/size-bounded-bigint]
(not (>= n bound))))
res (tc/quick-check 10000 prop :seed seed)]
(and
(not (:pass? res))
(-> res :shrunk :smallest first (= bound)))))))
;; vector can generate large vectors; regression for TCHECK-49
;; ---------------------------------------------------------------------------
(deftest large-vector-test
(is (= 100000
(count (first (gen/sample
(gen/vector gen/nat 100000)
1))))))
;; scale controls growth rate of generators
;; ---------------------------------------------------------------------------
(deftest scale-test
(let [g (gen/scale (partial min 10) gen/nat) ;; should limit size to 10
samples (gen/sample g 1000)]
(is (every? (partial >= 11) samples))
(is (some (partial = 10) samples))))
;; generator dev helpers
;; ---------------------------------------------------------------------------
(deftest generate-test
(is (string? (gen/generate gen/string)))
(is (string? (gen/generate gen/string 42))))
(defspec generate-with-seed-test
(prop/for-all [seed gen-seed
size gen/nat]
(apply = (repeatedly 5 #(gen/generate gen/string size seed)))))
;; defspec macro
;; ---------------------------------------------------------------------------
(defspec run-only-once 1 (prop/for-all* [gen/small-integer] (constantly true)))
(defspec run-default-times (prop/for-all* [gen/small-integer] (constantly true)))
(defspec run-with-map1 {:num-tests 1} (prop/for-all* [gen/small-integer] (constantly true)))
;; run-with-map succeeds only because gen/small-integer returns 0 for its first result. If it runs
;; multiple trials, we expect a failure. This test verifies that the num-tests works.
(defspec run-with-map {:num-tests 1
:seed 1}
(prop/for-all [a gen/small-integer]
(= a 0)))
(def my-defspec-options {:num-tests 1 :seed 1})
;; Regression test for old anaphoric issue with defspec: "seed", "times", and "max-size" were
;; used literally as parameters in the test function signature and could unexpectedly
;; shadow lexical names.
(def seed 0)
(defspec run-with-symbolic-options my-defspec-options
(prop/for-all [a gen/small-integer]
(= a seed)))
(defspec run-with-no-options
(prop/for-all [a gen/small-integer]
(integer? a)))
(defspec run-float-time 1e3
(prop/for-all [a gen/small-integer]
(integer? a)))
;; verify that the created tests work when called by name with options
(deftest spec-called-with-options
(is (= (select-keys (run-only-once) [:num-tests :result]) {:num-tests 1 :result true}))
;; run-with-map should succeed only if it runs exactly once, otherwise it's expected to fail
(is (= (select-keys (run-with-map) [:num-tests :result]) {:num-tests 1 :result true}))
(is (false? (:result (run-with-map 25))))
(is (false? (:result (run-with-symbolic-options 100 :seed 1 :max-size 20))))
(is (:result (run-with-symbolic-options 1 :seed 1 :max-size 20))))
;; let macro
;; ---------------------------------------------------------------------------
(defspec let-as-fmap-spec 20
(prop/for-all [s (gen/let [n gen/nat]
(str n))]
(re-matches #"\d+" s)))
(defspec let-as-bind-spec 20
(prop/for-all [[xs x] (gen/let [xs (gen/not-empty (gen/vector gen/nat))]
(gen/tuple (gen/return xs) (gen/elements xs)))]
(some #{x} xs)))
(defspec let-with-multiple-clauses-spec 20
(prop/for-all [[xs x] (gen/let [xs (gen/not-empty (gen/vector gen/nat))
x (gen/elements xs)]
[xs x])]
(some #{x} xs)))
;; A test to maintain the behavior assumed by TCHECK-133
(defspec independent-let-clauses-shrink-correctly 10
(let [gen-let-with-independent-clauses
;; gen/let unnecessarily ties these three generators together
;; with gen/bind rather than gen/tuple (because it can't
;; reliably detect independence), which theoretically could
;; lead to poor shrinking, but because the immutable random
;; number generator gets reused during gen/bind shrinking, it
;; turns out okay (the y and z generators get re-run, but with
;; the same parameters, so they generate the same value)
(gen/let [x gen/large-integer
y gen/large-integer
z gen/large-integer]
[x y z])
failing-prop
(prop/for-all [nums gen-let-with-independent-clauses]
(not (every? #(< 100 % 1000) nums)))]
(prop/for-all [seed gen-seed]
;; I suspect that this property is likely enough to fail
;; that 1000000 trials will virtually always trigger it,
;; but I haven't done the math on that.
(let [res (tc/quick-check 1000000 failing-prop :seed seed)]
(and (false? (:result res))
(= [101 101 101]
(-> res :shrunk :smallest first)))))))
;; reporter-fn
;; ---------------------------------------------------------------------------
(deftest reporter-fn-calls-test
(testing "a failing prop"
(let [calls (atom [])
reporter-fn (fn [arg]
#?(:clj (is (specs/valid-reporter-fn-call? arg)))
(swap! calls conj arg))
prop (prop/for-all [n gen/nat]
(> 5 n))]
(tc/quick-check 1000 prop :reporter-fn reporter-fn)
(is (= #{:trial :failure :shrink-step :shrunk}
(->> @calls (map :type) set)))))
(testing "a successful prop"
(let [calls (atom [])
reporter-fn (fn [arg]
#?(:clj (is (specs/valid-reporter-fn-call? arg)))
(swap! calls conj arg))
prop (prop/for-all [n gen/nat]
(<= 0 n))]
(tc/quick-check 5 prop :reporter-fn reporter-fn)
(is (= #{:trial :complete}
(->> @calls (map :type) set))))))
(deftest shrink-step-events-test
(let [events (atom [])
reporter-fn (partial swap! events conj)
pred (fn [n] (not (< 100 n)))
prop (prop/for-all [n (gen/scale (partial * 10) gen/nat)]
(pred n))]
(tc/quick-check 100 prop :reporter-fn reporter-fn)
(let [shrink-steps (filter #(= :shrink-step (:type %)) @events)
[passing-steps failing-steps] ((juxt filter remove)
#(-> % :shrinking :result results/pass?)
shrink-steps)
get-args-and-smallest-args (juxt (comp first :args :shrinking)
(comp first :smallest :shrinking))]
(is (every? #(not (pred (-> % :shrinking :args first))) failing-steps)
"pred on args is falsey in all failing steps")
(is (every? #(pred (-> % :shrinking :args first)) passing-steps)
"pred on args is truthy in all passing steps")
(is (->> failing-steps
(map get-args-and-smallest-args)
(every? (fn [[args current-smallest-args]] (= args current-smallest-args))))
"for every failing step, current-smallest args are equal to args")
(is (->> passing-steps
(map get-args-and-smallest-args)
(every? (fn [[args current-smallest-args]] (< args current-smallest-args))))
"for every passing step, current-smallest args are smaller than args")
(let [shrunk-args (map (comp first :args) failing-steps)]
(is (= shrunk-args
(reverse (sort shrunk-args)))
"failing steps args are sorted in descending order")))))
;; TCHECK-77 Regression
;; ---------------------------------------------------------------------------
;; Note cljs: need to adjust for JS numerics - NB
#?(:clj
(deftest choose-distribution-sanity-check
(testing "Should not get the same random value more than 90% of the time"
;; This is a probabilistic test; the odds of a false-positive
;; failure for the ranges with two elements should be roughly 1 in
;; 10^162 (and even rarer for larger ranges), so it will never
;; ever happen.
(are [low high] (let [xs (gen/sample (gen/choose low high) 1000)
count-of-most-frequent (apply max (vals (frequencies xs)))]
(< count-of-most-frequent 900))
(dec Long/MAX_VALUE) Long/MAX_VALUE
Long/MIN_VALUE (inc Long/MIN_VALUE)
Long/MIN_VALUE 0
0 1
-1 0
0 Long/MAX_VALUE
Long/MIN_VALUE Long/MAX_VALUE))))
;; TCHECK-82 Regression
;; ---------------------------------------------------------------------------
(deftest shrinking-laziness-test
(testing "That the shrinking process doesn't accidentally do extra work"
(let [state (atom 80)
prop (prop/for-all [xs (gen/vector gen/large-integer)]
(pos? (swap! state dec)))
res (tc/quick-check 100 prop :seed 42)
test-runs-during-shrinking (- @state)]
(is (= test-runs-during-shrinking
(get-in res [:shrunk :total-nodes-visited]))))))
;; TCHECK-32 Regression
;; ---------------------------------------------------------------------------
;; The original code reported in TCHECK-32 used 100 test runs,
;; but I'm using 50 here because 100 pushes the limits of my
;; node.js default memory allocation. I don't consider that
;; the same as the original problem, though, because it's just
;; as likely to OOM for (let [g (gen/vector gen/string)] (gen/map g g)),
;; which shows that the problem has more to do with 2D collections of
;; strings/etc. cranked up to size=200 than it has to do with
;; gen/any in particular.
(defspec merge-is-idempotent-and-this-spec-doesn't-OOM 50
(prop/for-all [m (gen/map gen/any-equatable gen/any-equatable)]
(= m (merge m m))))
(defn frequency-shrinking-prop
[bad-weight]
(prop/for-all [x (gen/frequency [[bad-weight (gen/return [:gen1 :bad])]
[10 (gen/fmap (partial vector :gen2)
(gen/list (gen/elements [:good
42
"a string"
:bad])))]])]
(not (re-find #"bad" (pr-str x)))))
;; TCHECK-114 Regression
;; ---------------------------------------------------------------------------
(deftest frequency-should-be-able-to-shrink-to-earlier-generators
(let [prop (frequency-shrinking-prop 1)]
;; we can't test that gen2 ALWAYS shrinks to gen1 because of TCHECK-120
(is (->> (range 1000)
(map #(tc/quick-check 1000 prop :seed %))
(some (fn [{:keys [fail shrunk]}]
(and shrunk
(= :gen2 (ffirst fail))
(= :gen1 (ffirst (:smallest shrunk))))))))))
;; TCHECK-129 Regression
;; ---------------------------------------------------------------------------
(deftest frequency-should-not-shrink-to-zero-weighted-generators
(let [prop (frequency-shrinking-prop 0)]
(is (->> (range 1000)
(map #(tc/quick-check 1000 prop :seed %))
(not-any? (fn [{:keys [fail shrunk]}]
(and shrunk
(= :gen2 (ffirst fail))
(= :gen1 (ffirst (:smallest shrunk))))))))))
;; prop/for-all
;; ---------------------------------------------------------------------------
(deftest for-all-takes-multiple-expressions
(let [a (atom [])
p (prop/for-all [x gen/nat]
(swap! a conj x)
(= x x))]
(is (:result (tc/quick-check 1000 p)))
(is (= 1000 (count @a)))
(is (every? integer? @a))))
;; TCHECK-142
;; ---------------------------------------------------------------------------
(deftest quick-check-result-keys-test
(testing "Pass"
(let [m (tc/quick-check 10 (prop/for-all [x gen/nat] x))]
(is (true? (:result m)))
(is (true? (:pass? m)))
(is (not (contains? m :result-data)))))
(testing "Falsy Fail"
(let [m (tc/quick-check 10 (prop/for-all [x gen/nat] false))]
(is (false? (:result m)))
(is (false? (:pass? m)))
(is (nil? (:result-data m)))))
(testing "Protocol Fail"
(let [m (tc/quick-check 1000 (prop/for-all [x gen/nat]
(reify results/Result
(pass? [_] (< x 70))
(result-data [_] {:foo 42 :x x}))))]
(is (false? (:result m)))
(is (false? (:pass? m)))
(let [[x] (:fail m)]
(is (= {:foo 42 :x x} (:result-data m))))
(is (= {:foo 42 :x 70} (:result-data (:shrunk m))))))
(testing "Error"
(let [m (tc/quick-check 1000 (prop/for-all [x gen/nat]
(or (< x 70)
(throw (ex-info "Dang!" {:x x})))))]
;; okay maybe this is where cljs needs to do something different
(is (instance? #?(:clj clojure.lang.ExceptionInfo
:cljs ExceptionInfo)
(:result m))
"legacy position for the error object")
(is (false? (:pass? m)))
(is (= [::prop/error] (keys (:result-data m))))
(let [[x] (:fail m)]
(is (= {:x x} (-> m :result-data ::prop/error ex-data))))
(is (= 70 (-> m :shrunk :result-data ::prop/error ex-data :x))))))
;; TCHECK-150
;; ---------------------------------------------------------------------------
#?(:cljs
(deftest throwing-arbitrary-objects-fails-tests-in-cljs
(let [res (tc/quick-check 10 (prop/for-all [x gen/nat] (throw "a string")))]
(is (false? (:pass? res)) "is definitely a failure")
(is (:shrunk res) "evidenced by the fact that it shrunk")
(is (instance? js/Error (:result res))
"The legacy :result key has an Error object so nobody gets confused"))))
;; TCHECK-95
;; ---------------------------------------------------------------------------
(deftest timing-keys
(let [{:keys [time-elapsed-ms]}
(tc/quick-check 10 (prop/for-all [x gen/nat] (= x x x x)))]
(is (integer? time-elapsed-ms))
(is (<= 0 time-elapsed-ms)))
(let [{:keys [failed-after-ms]
{:keys [time-shrinking-ms]} :shrunk}
(tc/quick-check 1000 (prop/for-all [x gen/nat] (not (<= 47 x 55))))]
(is (integer? failed-after-ms))
(is (<= 0 failed-after-ms))
(is (integer? time-shrinking-ms))
(is (<= 0 time-shrinking-ms))))
;; equatable generators
;; ---------------------------------------------------------------------------
(defspec equatable-generators-generate-equatable-things 100
(prop/for-all [g (gen/elements
[gen/simple-type-equatable gen/simple-type-printable-equatable
gen/any-equatable gen/any-printable-equatable])
seed gen-seed
size (gen/sized gen/return)]
(let [x (gen/generate g size seed)
y (gen/generate g size seed)]
(= x y))))
clojure-test.check-b2ec872/src/test/clojure/clojure/test/check/test_specs.clj 0000664 0000000 0000000 00000004100 14151735222 0027377 0 ustar 00root root 0000000 0000000 (ns clojure.test.check.test-specs
)
(if (let [{:keys [major minor]} *clojure-version*]
(and (= 1 major) (< minor 9)))
;; don't bother testing this on older clojures
(def valid-reporter-fn-call? (constantly true))
(do
(require '[clojure.spec.alpha :as s])
(eval
'(do
(s/def ::base
(s/keys :req-un [::type ::seed ::num-tests
::property]))
(defmulti type->spec :type)
(defmethod type->spec :trial
[_]
(s/merge ::base
(s/keys :req-un [::args
::result
::result-data])))
(defmethod type->spec :failure
[_]
(s/merge ::base
(s/keys :req-un [::fail
::failing-size
::result
::result-data])))
(s/def ::shrunk
(s/keys :req-un [::depth ::result ::result-data ::smallest ::total-nodes-visited]))
(s/def ::shrinking
(s/merge ::shrunk (s/keys :req-un [::args])))
(defmethod type->spec :shrink-step
[_]
(s/merge ::base
(s/keys :req-un [::fail
::failing-size
::result
::result-data
::shrinking])))
(defmethod type->spec :shrunk
[_]
(s/merge ::base
(s/keys :req-un [::fail
::failing-size
::result
::result-data
::shrunk])))
(defmethod type->spec :complete
[_]
(s/merge ::base
(s/keys :req-un [::result])))
(s/def ::value (s/multi-spec type->spec :type))
(defn valid-reporter-fn-call?
[m]
(or
(s/valid? ::value m)
(s/explain ::value m)))))))
clojure-test.check-b2ec872/test-runners/ 0000775 0000000 0000000 00000000000 14151735222 0020277 5 ustar 00root root 0000000 0000000 clojure-test.check-b2ec872/test-runners/run.js 0000664 0000000 0000000 00000000402 14151735222 0021435 0 ustar 00root root 0000000 0000000 try {
require("source-map-support").install();
} catch(err) {
}
require("../target/cljs/node_dev/out/goog/bootstrap/nodejs.js");
require("../target/cljs/node_dev/tests.js");
goog.require("clojure.test.check.test.runner");
goog.require("cljs.nodejscli");
clojure-test.check-b2ec872/test-runners/run_tests_adv.html 0000664 0000000 0000000 00000000230 14151735222 0024040 0 ustar 00root root 0000000 0000000
clojure-test.check-b2ec872/test-runners/run_tests_dev.html 0000664 0000000 0000000 00000000557 14151735222 0024060 0 ustar 00root root 0000000 0000000