Skip to content

Commit 76dd995

Browse files
committed
Port pytruth as lib/truth
Signed-off-by: Pierre Fenoll <[email protected]>
1 parent 94adf23 commit 76dd995

28 files changed

+4255
-0
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ require (
77
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
88
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
99
github.com/google/go-cmp v0.5.1 // indirect
10+
github.com/pmezard/go-difflib v1.0.0
11+
github.com/stretchr/testify v1.7.0
1012
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f
1113
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
1214
google.golang.org/protobuf v1.25.0

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
88
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
99
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
1010
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
11+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
12+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1113
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
1214
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
1315
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -27,7 +29,12 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
2729
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
2830
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
2931
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
32+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
33+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3034
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
35+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
36+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
37+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3138
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3239
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
3340
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -70,5 +77,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
7077
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
7178
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
7279
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
80+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
81+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
82+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7383
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
7484
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

lib/truth/README.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package truth // import "go.starlark.net/lib/truth"
2+
3+
Package truth defines builtins and methods to express test
4+
assertions within Starlark programs in the fashion of https://truth.dev
5+
6+
This package is a Starlark port of PyTruth (2c3717ddad 2021-03-10)
7+
https://github.com/google/pytruth
8+
9+
The Starlark:
10+
11+
assert.that(a).is_equal_to(b)
12+
assert.that(c).named("my value").is_true()
13+
assert.that(d).contains(a)
14+
assert.that(d).contains_all_of(a, b).in_order()
15+
assert.that(d).contains_any_of(a, b, c)
16+
17+
is equivalent to the following Python:
18+
19+
from truth.truth import AssertThat
20+
AssertThat(a).IsEqualTo(b)
21+
AssertThat(c).Named("my value").IsTrue()
22+
AssertThat(d).Contains(a)
23+
AssertThat(d).ContainsAllOf(a, b).InOrder()
24+
AssertThat(d).ContainsAnyOf(a, b, c)
25+
26+
Often, tests assert a relationship between a value produced by the test (the
27+
"actual" value) and some reference value (the "expected" value). It is
28+
strongly recommended that the actual value is made the subject of the
29+
assertion. For example:
30+
31+
assert.that(actual).is_equal_to(expected) # Recommended.
32+
assert.that(expected).is_equal_to(actual) # Not recommended.
33+
assert.that(actual).is_in(expected_possibilities) # Recommended.
34+
assert.that(expected_possibilities).contains(actual) # Not recommended.
35+
36+
Some assertions
37+
38+
assert.that(a).is_equal_to(b)
39+
assert.that(a).is_not_equal_to(b)
40+
assert.that(a).is_truthy()
41+
assert.that(a).is_falsy()
42+
assert.that(a).is_true()
43+
assert.that(a).is_false()
44+
assert.that(a).is_none()
45+
assert.that(a).is_not_none()
46+
assert.that(a).is_in(b)
47+
assert.that(a).is_any_of(b, c, d)
48+
assert.that(a).is_not_in(b)
49+
assert.that(a).is_none_of(b, c, d)
50+
assert.that(a).is_of_type(type(b))
51+
assert.that(a).is_not_of_type(type(b))
52+
assert.that(a).has_attribute(b)
53+
assert.that(a).does_not_have_attribute(b)
54+
assert.that(a).is_callable()
55+
assert.that(a).is_not_callable()
56+
assert.that(a).is_less_than(b)
57+
assert.that(a).is_greater_than(b)
58+
assert.that(a).is_at_most(b)
59+
assert.that(a).is_at_least(b)
60+
61+
Truthiness
62+
63+
Predicates `.is_true()` and `.is_false()` match *only* `True` and `False`.
64+
For `.is_truthy()` and `.is_falsy()`, `(starlark.Value).Truth() bool` is used.
65+
66+
assert.that(True).is_true()
67+
assert.that(False).is_false()
68+
assert.that(1).is_true() # fails
69+
assert.that(0).is_false() # fails
70+
assert.that(None).is_true() # fails
71+
assert.that(None).is_false() # fails
72+
assert.that(True).is_truthy()
73+
assert.that(False).is_falsy()
74+
assert.that(1).is_truthy()
75+
assert.that(0).is_falsy()
76+
assert.that(None).is_truthy() # fails
77+
assert.that(None).is_falsy()
78+
79+
Strings
80+
81+
assert.that("abc").has_length(3)
82+
assert.that("abc").starts_with("a")
83+
assert.that("abc").ends_with("c")
84+
assert.that("abc").matches("a.+") # prepends "^" to regexp
85+
assert.that("abc").does_not_match("b.+") # prepends "^" to regexp
86+
assert.that("abc").contains_match("b.+")
87+
assert.that("abc").does_not_contain_match("c.+")
88+
89+
Numbers
90+
91+
assert.that(a).is_zero()
92+
assert.that(a).is_non_zero()
93+
assert.that(a).is_positive_infinity()
94+
assert.that(a).is_not_positive_infinity()
95+
assert.that(a).is_negative_infinity()
96+
assert.that(a).is_not_negative_infinity()
97+
assert.that(a).is_finite()
98+
assert.that(a).is_not_finite()
99+
assert.that(a).is_nan()
100+
assert.that(a).is_not_nan()
101+
assert.that(a).is_within(delta).of(b)
102+
assert.that(a).is_not_within(delta).of(b)
103+
104+
Lists, strings, and other iterables
105+
106+
`cmp(x,y)` should return negative if x < y, zero if x == y and positive if x > y.
107+
*Ordered* means that the iterable's elements must increase (or decrease,
108+
depending on `cmp`) from beginning to end. Adjacent elements are allowed to be equal.
109+
*Strictly ordered* means that in addition, the elements must be unique
110+
(i.e. monotonically increasing or decreasing).
111+
112+
assert.that(a).has_size(n)
113+
assert.that(a).is_empty()
114+
assert.that(a).is_not_empty()
115+
assert.that(a).contains(b)
116+
assert.that(a).does_not_contain(b)
117+
assert.that(a).contains_all_of(b, c)
118+
assert.that(a).contains_all_in([b, c])
119+
assert.that(a).contains_any_of(b, c)
120+
assert.that(a).contains_any_in([b, c])
121+
assert.that(a).contains_exactly(b, c)
122+
assert.that(sorted(a)).contains_exactly_elements_in(sorted(b)).in_order()
123+
assert.that(a).contains_none_of(b, c)
124+
assert.that(a).contains_none_in([b, c])
125+
assert.that(a).contains_no_duplicates()
126+
assert.that(a).is_ordered()
127+
assert.that(a).is_ordered_according_to(cmp)
128+
assert.that(a).is_strictly_ordered()
129+
assert.that(a).is_strictly_ordered_according_to(cmp)
130+
131+
Asserting order
132+
133+
By default, `.contains_all...` and `.contains_exactly...` do not enforce that the
134+
order of the elements in the subject under test matches that of the expected
135+
value. To do that, append `.in_order()` to the returned predicate.
136+
137+
assert.that([2, 4, 6]).contains_all_of(6, 2)
138+
assert.that([2, 4, 6]).contains_all_of(6, 2).in_order() # fails
139+
assert.that([2, 4, 6]).contains_all_of(2, 6).in_order()
140+
assert.that((1, 2, 3)).contains_all_in((1, 3)).in_order()
141+
assert.that([2, 4, 6]).contains_exactly(2, 6, 4)
142+
assert.that([2, 4, 6]).contains_exactly(2, 6, 4).in_order() # fails
143+
assert.that([2, 4, 6]).contains_exactly(2, 4, 6).in_order()
144+
assert.that((1, 2, 3)).contains_exactly_elements_in([1, 2, 3]).in_order()
145+
146+
When using `.in_order()`, ensure that both the subject under test and the expected
147+
value have a defined order, otherwise the result is undefined.
148+
For example, `assert.that(aList).contains_exactly_elements_in(aSet).in_order()`
149+
may or may not succeed, depending on how the `set` implements ordering.
150+
The builtin set datatype does not implement ordering.
151+
These assertions *may or may not* succeed:
152+
153+
assert.that((1, 2, 3)).contains_all_in(set([1, 3])).in_order()
154+
assert.that(set([3, 2, 1])).contains_exactly_elements_in((1, 2, 3)).in_order()
155+
assert.that({1:2, 3:4}).contains_all_in((1, 3)).in_order()
156+
157+
Dictionaries, in addition to the table above
158+
159+
assert.that(d).contains_key(k)
160+
assert.that(d).does_not_contain_key(k)
161+
assert.that(d).contains_item(k, v)
162+
assert.that(d).does_not_contain_item(k, v)
163+
assert.that(d).contains_exactly(k1, v1, k2, v2)
164+
assert.that(d1).contains_exactly_items_in(d2)
165+
assert.that(d1.items()).contains_all_in(d2.items())
166+
167+
Notes (in no particular order):
168+
169+
`None` is not comparable (as in Python 3), so assertions involving `<` `>`
170+
`<=` `>=` on `None` such as `assert.that(a).is_greater_than(None)`
171+
fail with `InvalidAssertion` error.
172+
It is recommended to first check the `None`-ness of values with `.is_none()`
173+
or `.is_not_none()` before performing inequility assertions.
174+
175+
As in Python, `0`, `0.0` and `-0.0` all compare equal. The assertions
176+
`.is_zero()` and `.is_non_zero()` are provided for semantic convenience.
177+
178+
Starlark strings are not iterable (unlike Python's) but are iterated on as
179+
slices of utf-8 runes when needed in this implementation. This works:
180+
assert.that("abcdefg").contains_all_of("a", "c", "e").in_order()
181+
assert.that("abcdefg").is_strictly_ordered()
182+
183+
In `.contains...()` assertions a "duplicate values counter" is used that
184+
relies on the `(starlark.Value).String() string` reprensentation of values
185+
instead of `(starlark.Value).Hash() (uint32, error)` as some basic types
186+
are not hashable (list, dict, set).
187+
For this reason **it is recommended to only pass values of the main data types
188+
built in to the interpreter to functions of this package:**
189+
* NoneType
190+
* bool
191+
* int
192+
* float
193+
* string
194+
* list
195+
* tuple
196+
* dict
197+
* set
198+
* function
199+
* builtin_function_or_method
200+
201+
It is possible to incorrectly express assertions (see all but the last line in
202+
this block):
203+
assert.that(x)
204+
assert.that(x).named(z)
205+
assert.that(x).is_within(y)
206+
assert.that(x).is_not_within(y)
207+
assert.that(x).is_not_within(y).of(z)
208+
This is why each call to `.that(...)` first checks that no non-terminated
209+
assertions were previously executed in the current thread.
210+
A `Close(*starlark.Thread) error` function is also provided to ensure
211+
this property holds after the interpreter returns.
212+
213+
This library is threadsafe; you may execute multiple assertions in parallel.
214+
215+
const Default = "assert"
216+
var LocalThreadKeyForClose = Default
217+
func Asserted(th *starlark.Thread) bool
218+
func Close(th *starlark.Thread) (err error)
219+
func NewModule(predeclared starlark.StringDict)
220+
func That(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, ...) (starlark.Value, error)
221+
type InvalidAssertion string
222+
type T struct{ ... }
223+
type TruthAssertion string
224+
type UnhandledError struct{ ... }
225+
type UnresolvedError string

0 commit comments

Comments
 (0)