whatcanGOwrong
This commit is contained in:
Vendored
+354
@@ -0,0 +1,354 @@
|
||||
# Tests of Starlark assignment.
|
||||
|
||||
# This is a "chunked" file: each "---" effectively starts a new file.
|
||||
|
||||
# tuple assignment
|
||||
load("assert.star", "assert")
|
||||
|
||||
() = () # empty ok
|
||||
|
||||
a, b, c = 1, 2, 3
|
||||
assert.eq(a, 1)
|
||||
assert.eq(b, 2)
|
||||
assert.eq(c, 3)
|
||||
|
||||
(d, e, f,) = (1, 2, 3) # trailing comma ok
|
||||
---
|
||||
(a, b, c) = 1 ### "got int in sequence assignment"
|
||||
---
|
||||
(a, b) = () ### "too few values to unpack"
|
||||
---
|
||||
(a, b) = (1,) ### "too few values to unpack"
|
||||
---
|
||||
(a, b, c) = (1, 2) ### "too few values to unpack"
|
||||
---
|
||||
(a, b) = (1, 2, 3) ### "too many values to unpack"
|
||||
---
|
||||
() = 1 ### "got int in sequence assignment"
|
||||
---
|
||||
() = (1,) ### "too many values to unpack"
|
||||
---
|
||||
() = (1, 2) ### "too many values to unpack"
|
||||
---
|
||||
# list assignment
|
||||
load("assert.star", "assert")
|
||||
|
||||
[] = [] # empty ok
|
||||
|
||||
[a, b, c] = [1, 2, 3]
|
||||
assert.eq(a, 1)
|
||||
assert.eq(b, 2)
|
||||
assert.eq(c, 3)
|
||||
|
||||
[d, e, f,] = [1, 2, 3] # trailing comma ok
|
||||
---
|
||||
[a, b, c] = 1 ### "got int in sequence assignment"
|
||||
---
|
||||
[a, b] = [] ### "too few values to unpack"
|
||||
---
|
||||
[a, b] = [1] ### "too few values to unpack"
|
||||
---
|
||||
[a, b, c] = [1, 2] ### "too few values to unpack"
|
||||
---
|
||||
[a, b] = [1, 2, 3] ### "too many values to unpack"
|
||||
---
|
||||
[] = 1 ### "got int in sequence assignment"
|
||||
---
|
||||
[] = [1] ### "too many values to unpack"
|
||||
---
|
||||
[] = [1, 2] ### "too many values to unpack"
|
||||
---
|
||||
# list-tuple assignment
|
||||
load("assert.star", "assert")
|
||||
|
||||
# empty ok
|
||||
[] = ()
|
||||
() = []
|
||||
|
||||
[a, b, c] = (1, 2, 3)
|
||||
assert.eq(a, 1)
|
||||
assert.eq(b, 2)
|
||||
assert.eq(c, 3)
|
||||
|
||||
[a2, b2, c2] = 1, 2, 3 # bare tuple ok
|
||||
|
||||
(d, e, f) = [1, 2, 3]
|
||||
assert.eq(d, 1)
|
||||
assert.eq(e, 2)
|
||||
assert.eq(f, 3)
|
||||
|
||||
[g, h, (i, j)] = (1, 2, [3, 4])
|
||||
assert.eq(g, 1)
|
||||
assert.eq(h, 2)
|
||||
assert.eq(i, 3)
|
||||
assert.eq(j, 4)
|
||||
|
||||
(k, l, [m, n]) = [1, 2, (3, 4)]
|
||||
assert.eq(k, 1)
|
||||
assert.eq(l, 2)
|
||||
assert.eq(m, 3)
|
||||
assert.eq(n, 4)
|
||||
|
||||
---
|
||||
# misc assignment
|
||||
load("assert.star", "assert")
|
||||
|
||||
def assignment():
|
||||
a = [1, 2, 3]
|
||||
a[1] = 5
|
||||
assert.eq(a, [1, 5, 3])
|
||||
a[-2] = 2
|
||||
assert.eq(a, [1, 2, 3])
|
||||
assert.eq("%d %d" % (5, 7), "5 7")
|
||||
x={}
|
||||
x[1] = 2
|
||||
x[1] += 3
|
||||
assert.eq(x[1], 5)
|
||||
def f12(): x[(1, "abc", {})] = 1
|
||||
assert.fails(f12, "unhashable type: dict")
|
||||
|
||||
assignment()
|
||||
|
||||
---
|
||||
# augmented assignment
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
x += 1
|
||||
assert.eq(x, 2)
|
||||
x *= 3
|
||||
assert.eq(x, 6)
|
||||
f()
|
||||
|
||||
---
|
||||
# effects of evaluating LHS occur only once
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
count = [0] # count[0] is the number of calls to f
|
||||
|
||||
def f():
|
||||
count[0] += 1
|
||||
return count[0]
|
||||
|
||||
x = [1, 2, 3]
|
||||
x[f()] += 1
|
||||
|
||||
assert.eq(x, [1, 3, 3]) # sole call to f returned 1
|
||||
assert.eq(count[0], 1) # f was called only once
|
||||
|
||||
---
|
||||
# Order of evaluation.
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
calls = []
|
||||
|
||||
def f(name, result):
|
||||
calls.append(name)
|
||||
return result
|
||||
|
||||
# The right side is evaluated before the left in an ordinary assignment.
|
||||
calls.clear()
|
||||
f("array", [0])[f("index", 0)] = f("rhs", 0)
|
||||
assert.eq(calls, ["rhs", "array", "index"])
|
||||
|
||||
calls.clear()
|
||||
f("lhs1", [0])[0], f("lhs2", [0])[0] = f("rhs1", 0), f("rhs2", 0)
|
||||
assert.eq(calls, ["rhs1", "rhs2", "lhs1", "lhs2"])
|
||||
|
||||
# Left side is evaluated first (and only once) in an augmented assignment.
|
||||
calls.clear()
|
||||
f("array", [0])[f("index", 0)] += f("addend", 1)
|
||||
assert.eq(calls, ["array", "index", "addend"])
|
||||
|
||||
---
|
||||
# global referenced before assignment
|
||||
|
||||
def f():
|
||||
return g ### "global variable g referenced before assignment"
|
||||
|
||||
f()
|
||||
|
||||
g = 1
|
||||
|
||||
---
|
||||
# Free variables are captured by reference, so this is ok.
|
||||
load("assert.star", "assert")
|
||||
|
||||
def f():
|
||||
def g():
|
||||
return outer
|
||||
outer = 1
|
||||
return g()
|
||||
|
||||
assert.eq(f(), 1)
|
||||
|
||||
---
|
||||
load("assert.star", "assert")
|
||||
|
||||
printok = [False]
|
||||
|
||||
# This program should resolve successfully but fail dynamically.
|
||||
# However, the Java implementation currently reports the dynamic
|
||||
# error at the x=1 statement (b/33975425). I think we need to simplify
|
||||
# the resolver algorithm to what we have implemented.
|
||||
def use_before_def():
|
||||
print(x) # dynamic error: local var referenced before assignment
|
||||
printok[0] = True
|
||||
x = 1 # makes 'x' local
|
||||
|
||||
assert.fails(use_before_def, 'local variable x referenced before assignment')
|
||||
assert.true(not printok[0]) # execution of print statement failed
|
||||
|
||||
---
|
||||
x = [1]
|
||||
x.extend([2]) # ok
|
||||
|
||||
def f():
|
||||
x += [4] ### "local variable x referenced before assignment"
|
||||
|
||||
f()
|
||||
|
||||
---
|
||||
|
||||
z += 3 ### "global variable z referenced before assignment"
|
||||
|
||||
---
|
||||
load("assert.star", "assert")
|
||||
|
||||
# It's ok to define a global that shadows a built-in...
|
||||
list = []
|
||||
assert.eq(type(list), "list")
|
||||
|
||||
# ...but then all uses refer to the global,
|
||||
# even if they occur before the binding use.
|
||||
# See github.com/google/skylark/issues/116.
|
||||
assert.fails(lambda: tuple, "global variable tuple referenced before assignment")
|
||||
tuple = ()
|
||||
|
||||
---
|
||||
# option:set
|
||||
# Same as above, but set is dialect-specific;
|
||||
# we shouldn't notice any difference.
|
||||
load("assert.star", "assert")
|
||||
|
||||
set = [1, 2, 3]
|
||||
assert.eq(type(set), "list")
|
||||
|
||||
# As in Python 2 and Python 3,
|
||||
# all 'in x' expressions in a comprehension are evaluated
|
||||
# in the comprehension's lexical block, except the first,
|
||||
# which is resolved in the outer block.
|
||||
x = [[1, 2]]
|
||||
assert.eq([x for x in x for y in x],
|
||||
[[1, 2], [1, 2]])
|
||||
|
||||
---
|
||||
# A comprehension establishes a single new lexical block,
|
||||
# not one per 'for' clause.
|
||||
x = [1, 2]
|
||||
_ = [x for _ in [3] for x in x] ### "local variable x referenced before assignment"
|
||||
|
||||
---
|
||||
load("assert.star", "assert")
|
||||
|
||||
# assign singleton sequence to 1-tuple
|
||||
(x,) = (1,)
|
||||
assert.eq(x, 1)
|
||||
(y,) = [1]
|
||||
assert.eq(y, 1)
|
||||
|
||||
# assign 1-tuple to variable
|
||||
z = (1,)
|
||||
assert.eq(type(z), "tuple")
|
||||
assert.eq(len(z), 1)
|
||||
assert.eq(z[0], 1)
|
||||
|
||||
# assign value to parenthesized variable
|
||||
(a) = 1
|
||||
assert.eq(a, 1)
|
||||
|
||||
---
|
||||
# assignment to/from fields.
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
hf = hasfields()
|
||||
hf.x = 1
|
||||
assert.eq(hf.x, 1)
|
||||
hf.x = [1, 2]
|
||||
hf.x += [3, 4]
|
||||
assert.eq(hf.x, [1, 2, 3, 4])
|
||||
freeze(hf)
|
||||
def setX(hf):
|
||||
hf.x = 2
|
||||
def setY(hf):
|
||||
hf.y = 3
|
||||
assert.fails(lambda: setX(hf), "cannot set field on a frozen hasfields")
|
||||
assert.fails(lambda: setY(hf), "cannot set field on a frozen hasfields")
|
||||
|
||||
---
|
||||
# destucturing assignment in a for loop.
|
||||
load("assert.star", "assert")
|
||||
|
||||
def f():
|
||||
res = []
|
||||
for (x, y), z in [(["a", "b"], 3), (["c", "d"], 4)]:
|
||||
res.append((x, y, z))
|
||||
return res
|
||||
assert.eq(f(), [("a", "b", 3), ("c", "d", 4)])
|
||||
|
||||
def g():
|
||||
a = {}
|
||||
for i, a[i] in [("one", 1), ("two", 2)]:
|
||||
pass
|
||||
return a
|
||||
assert.eq(g(), {"one": 1, "two": 2})
|
||||
|
||||
---
|
||||
# parenthesized LHS in augmented assignment (success)
|
||||
# option:globalreassign
|
||||
load("assert.star", "assert")
|
||||
|
||||
a = 5
|
||||
(a) += 3
|
||||
assert.eq(a, 8)
|
||||
|
||||
---
|
||||
# parenthesized LHS in augmented assignment (error)
|
||||
|
||||
(a) += 5 ### "global variable a referenced before assignment"
|
||||
|
||||
---
|
||||
# option:globalreassign
|
||||
load("assert.star", "assert")
|
||||
assert = 1
|
||||
load("assert.star", "assert")
|
||||
|
||||
---
|
||||
# option:globalreassign option:loadbindsglobally
|
||||
load("assert.star", "assert")
|
||||
assert = 1
|
||||
load("assert.star", "assert")
|
||||
|
||||
---
|
||||
# option:loadbindsglobally
|
||||
_ = assert ### "global variable assert referenced before assignment"
|
||||
load("assert.star", "assert")
|
||||
|
||||
---
|
||||
_ = assert ### "local variable assert referenced before assignment"
|
||||
load("assert.star", "assert")
|
||||
|
||||
---
|
||||
def f(): assert.eq(1, 1) # forward ref OK
|
||||
load("assert.star", "assert")
|
||||
f()
|
||||
|
||||
---
|
||||
# option:loadbindsglobally
|
||||
def f(): assert.eq(1, 1) # forward ref OK
|
||||
load("assert.star", "assert")
|
||||
f()
|
||||
Vendored
+167
@@ -0,0 +1,167 @@
|
||||
# Benchmarks of Starlark execution
|
||||
# option:set
|
||||
|
||||
def bench_range_construction(b):
|
||||
for _ in range(b.n):
|
||||
range(200)
|
||||
|
||||
def bench_range_iteration(b):
|
||||
for _ in range(b.n):
|
||||
for x in range(200):
|
||||
pass
|
||||
|
||||
# Make a 2-level call tree of 100 * 100 calls.
|
||||
def bench_calling(b):
|
||||
list = range(100)
|
||||
|
||||
def g():
|
||||
for x in list:
|
||||
pass
|
||||
|
||||
def f():
|
||||
for x in list:
|
||||
g()
|
||||
|
||||
for _ in range(b.n):
|
||||
f()
|
||||
|
||||
# Measure overhead of calling a trivial built-in method.
|
||||
emptydict = {}
|
||||
range1000 = range(1000)
|
||||
|
||||
def bench_builtin_method(b):
|
||||
for _ in range(b.n):
|
||||
for _ in range1000:
|
||||
emptydict.get(None)
|
||||
|
||||
def bench_int(b):
|
||||
for _ in range(b.n):
|
||||
a = 0
|
||||
for _ in range1000:
|
||||
a += 1
|
||||
|
||||
def bench_bigint(b):
|
||||
for _ in range(b.n):
|
||||
a = 1 << 31 # maxint32 + 1
|
||||
for _ in range1000:
|
||||
a += 1
|
||||
|
||||
def bench_gauss(b):
|
||||
# Sum of arithmetic series. All results fit in int32.
|
||||
for _ in range(b.n):
|
||||
acc = 0
|
||||
for x in range(92000):
|
||||
acc += x
|
||||
|
||||
def bench_mix(b):
|
||||
"Benchmark of a simple mix of computation (for, if, arithmetic, comprehension)."
|
||||
for _ in range(b.n):
|
||||
x = 0
|
||||
for i in range(50):
|
||||
if i:
|
||||
x += 1
|
||||
a = [x for x in range(i)]
|
||||
|
||||
largedict = {str(v): v for v in range(1000)}
|
||||
|
||||
def bench_dict_equal(b):
|
||||
"Benchmark of dict equality operation."
|
||||
for _ in range(b.n):
|
||||
if largedict != largedict:
|
||||
fail("invalid comparison")
|
||||
|
||||
largeset = set([v for v in range(1000)])
|
||||
|
||||
def bench_set_equal(b):
|
||||
"Benchmark of set union operation."
|
||||
for _ in range(b.n):
|
||||
if largeset != largeset:
|
||||
fail("invalid comparison")
|
||||
|
||||
flat = { "int": 1, "float": 0.2, "string": "string", "list": [], "bool": True, "nil": None, "tuple": (1, 2, 3) }
|
||||
deep = {
|
||||
"type": "int",
|
||||
"value": 1,
|
||||
"next": {
|
||||
"type": "float",
|
||||
"value": 0.2,
|
||||
"next": {
|
||||
"type": "string",
|
||||
"value": "string",
|
||||
"next": {
|
||||
"type": "list",
|
||||
"value": [ 1, "", True, None, (1, 2) ],
|
||||
"next": {
|
||||
"type": "bool",
|
||||
"value": True,
|
||||
"next": {
|
||||
"type": "tuple",
|
||||
"value": (1, 2.0, "3"),
|
||||
"next": None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deep_list = [ deep for _ in range(100) ]
|
||||
|
||||
def bench_to_json_flat_mixed(b):
|
||||
"Benchmark json.encode builtin with flat mixed input"
|
||||
for _ in range(b.n):
|
||||
json.encode(flat)
|
||||
|
||||
def bench_to_json_flat_big(b):
|
||||
"Benchmark json.encode builtin with big flat integer input"
|
||||
for _ in range(b.n):
|
||||
json.encode(largedict)
|
||||
|
||||
def bench_to_json_deep(b):
|
||||
"Benchmark json.encode builtin with deep input"
|
||||
for _ in range(b.n):
|
||||
json.encode(deep)
|
||||
|
||||
def bench_to_json_deep_list(b):
|
||||
"Benchmark json.encode builtin with a list of deep input"
|
||||
for _ in range(b.n):
|
||||
json.encode(deep)
|
||||
|
||||
def bench_issubset_unique_large_small(b):
|
||||
"Benchmark set.issubset builtin"
|
||||
s = set(range(10000))
|
||||
for _ in range(b.n):
|
||||
s.issubset(range(1000))
|
||||
|
||||
def bench_issubset_unique_small_large(b):
|
||||
"Benchmark set.issubset builtin"
|
||||
s = set(range(1000))
|
||||
for _ in range(b.n):
|
||||
s.issubset(range(10000))
|
||||
|
||||
def bench_issubset_unique_same(b):
|
||||
"Benchmark set.issubset builtin"
|
||||
s = set(range(1000))
|
||||
for _ in range(b.n):
|
||||
s.issubset(range(1000))
|
||||
|
||||
def bench_issubset_duplicate_large_small(b):
|
||||
"Benchmark set.issubset builtin"
|
||||
s = set(range(10000))
|
||||
l = list(range(200)) * 5
|
||||
for _ in range(b.n):
|
||||
s.issubset(range(1000))
|
||||
|
||||
def bench_issubset_duplicate_small_large(b):
|
||||
"Benchmark set.issubset builtin"
|
||||
s = set(range(1000))
|
||||
l = list(range(2000)) * 5
|
||||
for _ in range(b.n):
|
||||
s.issubset(l)
|
||||
|
||||
def bench_issubset_duplicate_same(b):
|
||||
"Benchmark set.issubset builtin"
|
||||
s = set(range(1000))
|
||||
l = list(range(200)) * 5
|
||||
for _ in range(b.n):
|
||||
s.issubset(l)
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# Tests of Starlark 'bool'
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# truth
|
||||
assert.true(True)
|
||||
assert.true(not False)
|
||||
assert.true(not not True)
|
||||
assert.true(not not 1 >= 1)
|
||||
|
||||
# precedence of not
|
||||
assert.true(not not 2 > 1)
|
||||
# assert.true(not (not 2) > 1) # TODO(adonovan): fix: gives error for False > 1.
|
||||
# assert.true(not ((not 2) > 1)) # TODO(adonovan): fix
|
||||
# assert.true(not ((not (not 2)) > 1)) # TODO(adonovan): fix
|
||||
# assert.true(not not not (2 > 1))
|
||||
|
||||
# bool conversion
|
||||
assert.eq(
|
||||
[bool(), bool(1), bool(0), bool("hello"), bool("")],
|
||||
[False, True, False, True, False],
|
||||
)
|
||||
|
||||
# comparison
|
||||
assert.true(None == None)
|
||||
assert.true(None != False)
|
||||
assert.true(None != True)
|
||||
assert.eq(1 == 1, True)
|
||||
assert.eq(1 == 2, False)
|
||||
assert.true(False == False)
|
||||
assert.true(True == True)
|
||||
|
||||
# ordered comparison
|
||||
assert.true(False < True)
|
||||
assert.true(False <= True)
|
||||
assert.true(False <= False)
|
||||
assert.true(True > False)
|
||||
assert.true(True >= False)
|
||||
assert.true(True >= True)
|
||||
|
||||
# conditional expression
|
||||
assert.eq(1 if 3 > 2 else 0, 1)
|
||||
assert.eq(1 if "foo" else 0, 1)
|
||||
assert.eq(1 if "" else 0, 0)
|
||||
|
||||
# short-circuit evaluation of 'and' and 'or':
|
||||
# 'or' yields the first true operand, or the last if all are false.
|
||||
assert.eq(0 or "" or [] or 0, 0)
|
||||
assert.eq(0 or "" or [] or 123 or 1 // 0, 123)
|
||||
assert.fails(lambda : 0 or "" or [] or 0 or 1 // 0, "division by zero")
|
||||
|
||||
# 'and' yields the first false operand, or the last if all are true.
|
||||
assert.eq(1 and "a" and [1] and 123, 123)
|
||||
assert.eq(1 and "a" and [1] and 0 and 1 // 0, 0)
|
||||
assert.fails(lambda : 1 and "a" and [1] and 123 and 1 // 0, "division by zero")
|
||||
|
||||
# Built-ins that want a bool want an actual bool, not a truth value.
|
||||
# See github.com/bazelbuild/starlark/issues/30
|
||||
assert.eq(''.splitlines(True), [])
|
||||
assert.fails(lambda: ''.splitlines(1), 'got int, want bool')
|
||||
assert.fails(lambda: ''.splitlines("hello"), 'got string, want bool')
|
||||
assert.fails(lambda: ''.splitlines(0.0), 'got float, want bool')
|
||||
Vendored
+240
@@ -0,0 +1,240 @@
|
||||
# Tests of Starlark built-in functions
|
||||
# option:set
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# len
|
||||
assert.eq(len([1, 2, 3]), 3)
|
||||
assert.eq(len((1, 2, 3)), 3)
|
||||
assert.eq(len({1: 2}), 1)
|
||||
assert.fails(lambda: len(1), "int.*has no len")
|
||||
|
||||
# and, or
|
||||
assert.eq(123 or "foo", 123)
|
||||
assert.eq(0 or "foo", "foo")
|
||||
assert.eq(123 and "foo", "foo")
|
||||
assert.eq(0 and "foo", 0)
|
||||
none = None
|
||||
_1 = none and none[0] # rhs is not evaluated
|
||||
_2 = (not none) or none[0] # rhs is not evaluated
|
||||
|
||||
# abs
|
||||
assert.eq(abs(2.0), 2.0)
|
||||
assert.eq(abs(0.0), 0.0)
|
||||
assert.eq(abs(-2.0), 2.0)
|
||||
assert.eq(abs(2), 2)
|
||||
assert.eq(abs(0), 0)
|
||||
assert.eq(abs(-2), 2)
|
||||
assert.eq(abs(float("inf")), float("inf"))
|
||||
assert.eq(abs(float("-inf")), float("inf"))
|
||||
assert.eq(abs(float("nan")), float("nan"))
|
||||
assert.fails(lambda: abs("0"), "got string, want int or float")
|
||||
maxint32 = (1 << 31) - 1
|
||||
assert.eq(abs(+123 * maxint32), +123 * maxint32)
|
||||
assert.eq(abs(-123 * maxint32), +123 * maxint32)
|
||||
|
||||
# any, all
|
||||
assert.true(all([]))
|
||||
assert.true(all([1, True, "foo"]))
|
||||
assert.true(not all([1, True, ""]))
|
||||
assert.true(not any([]))
|
||||
assert.true(any([0, False, "foo"]))
|
||||
assert.true(not any([0, False, ""]))
|
||||
|
||||
# in
|
||||
assert.true(3 in [1, 2, 3])
|
||||
assert.true(4 not in [1, 2, 3])
|
||||
assert.true(3 in (1, 2, 3))
|
||||
assert.true(4 not in (1, 2, 3))
|
||||
assert.fails(lambda: 3 in "foo", "in.*requires string as left operand")
|
||||
assert.true(123 in {123: ""})
|
||||
assert.true(456 not in {123:""})
|
||||
assert.true([] not in {123: ""})
|
||||
|
||||
# sorted
|
||||
assert.eq(sorted([42, 123, 3]), [3, 42, 123])
|
||||
assert.eq(sorted([42, 123, 3], reverse=True), [123, 42, 3])
|
||||
assert.eq(sorted(["wiz", "foo", "bar"]), ["bar", "foo", "wiz"])
|
||||
assert.eq(sorted(["wiz", "foo", "bar"], reverse=True), ["wiz", "foo", "bar"])
|
||||
assert.fails(lambda: sorted([1, 2, None, 3]), "int < NoneType not implemented")
|
||||
assert.fails(lambda: sorted([1, "one"]), "string < int not implemented")
|
||||
# custom key function
|
||||
assert.eq(sorted(["two", "three", "four"], key=len),
|
||||
["two", "four", "three"])
|
||||
assert.eq(sorted(["two", "three", "four"], key=len, reverse=True),
|
||||
["three", "four", "two"])
|
||||
assert.fails(lambda: sorted([1, 2, 3], key=None), "got NoneType, want callable")
|
||||
# sort is stable
|
||||
pairs = [(4, 0), (3, 1), (4, 2), (2, 3), (3, 4), (1, 5), (2, 6), (3, 7)]
|
||||
assert.eq(sorted(pairs, key=lambda x: x[0]),
|
||||
[(1, 5),
|
||||
(2, 3), (2, 6),
|
||||
(3, 1), (3, 4), (3, 7),
|
||||
(4, 0), (4, 2)])
|
||||
assert.fails(lambda: sorted(1), 'sorted: for parameter iterable: got int, want iterable')
|
||||
|
||||
# reversed
|
||||
assert.eq(reversed([1, 144, 81, 16]), [16, 81, 144, 1])
|
||||
|
||||
# set
|
||||
assert.contains(set([1, 2, 3]), 1)
|
||||
assert.true(4 not in set([1, 2, 3]))
|
||||
assert.eq(len(set([1, 2, 3])), 3)
|
||||
assert.eq(sorted([x for x in set([1, 2, 3])]), [1, 2, 3])
|
||||
|
||||
# dict
|
||||
assert.eq(dict([(1, 2), (3, 4)]), {1: 2, 3: 4})
|
||||
assert.eq(dict([(1, 2), (3, 4)], foo="bar"), {1: 2, 3: 4, "foo": "bar"})
|
||||
assert.eq(dict({1:2, 3:4}), {1: 2, 3: 4})
|
||||
assert.eq(dict({1:2, 3:4}.items()), {1: 2, 3: 4})
|
||||
|
||||
# range
|
||||
assert.eq("range", type(range(10)))
|
||||
assert.eq("range(10)", str(range(0, 10, 1)))
|
||||
assert.eq("range(1, 10)", str(range(1, 10)))
|
||||
assert.eq(range(0, 5, 10), range(0, 5, 11))
|
||||
assert.eq("range(0, 10, -1)", str(range(0, 10, -1)))
|
||||
assert.fails(lambda: {range(10): 10}, "unhashable: range")
|
||||
assert.true(bool(range(1, 2)))
|
||||
assert.true(not(range(2, 1))) # an empty range is false
|
||||
assert.eq([x*x for x in range(5)], [0, 1, 4, 9, 16])
|
||||
assert.eq(list(range(5)), [0, 1, 2, 3, 4])
|
||||
assert.eq(list(range(-5)), [])
|
||||
assert.eq(list(range(2, 5)), [2, 3, 4])
|
||||
assert.eq(list(range(5, 2)), [])
|
||||
assert.eq(list(range(-2, -5)), [])
|
||||
assert.eq(list(range(-5, -2)), [-5, -4, -3])
|
||||
assert.eq(list(range(2, 10, 3)), [2, 5, 8])
|
||||
assert.eq(list(range(10, 2, -3)), [10, 7, 4])
|
||||
assert.eq(list(range(-2, -10, -3)), [-2, -5, -8])
|
||||
assert.eq(list(range(-10, -2, 3)), [-10, -7, -4])
|
||||
assert.eq(list(range(10, 2, -1)), [10, 9, 8, 7, 6, 5, 4, 3])
|
||||
assert.eq(list(range(5)[1:]), [1, 2, 3, 4])
|
||||
assert.eq(len(range(5)[1:]), 4)
|
||||
assert.eq(list(range(5)[:2]), [0, 1])
|
||||
assert.eq(list(range(10)[1:]), [1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
assert.eq(list(range(10)[1:9:2]), [1, 3, 5, 7])
|
||||
assert.eq(list(range(10)[1:10:2]), [1, 3, 5, 7, 9])
|
||||
assert.eq(list(range(10)[1:11:2]), [1, 3, 5, 7, 9])
|
||||
assert.eq(list(range(10)[::-2]), [9, 7, 5, 3, 1])
|
||||
assert.eq(list(range(0, 10, 2)[::2]), [0, 4, 8])
|
||||
assert.eq(list(range(0, 10, 2)[::-2]), [8, 4, 0])
|
||||
# range() is limited by the width of the Go int type (int32 or int64).
|
||||
assert.fails(lambda: range(1<<64), "... out of range .want value in signed ..-bit range")
|
||||
assert.eq(len(range(0x7fffffff)), 0x7fffffff) # O(1)
|
||||
# Two ranges compare equal if they denote the same sequence:
|
||||
assert.eq(range(0), range(2, 1, 3)) # []
|
||||
assert.eq(range(0, 3, 2), range(0, 4, 2)) # [0, 2]
|
||||
assert.ne(range(1, 10), range(2, 10))
|
||||
assert.fails(lambda: range(0) < range(0), "range < range not implemented")
|
||||
# <number> in <range>
|
||||
assert.contains(range(3), 1)
|
||||
assert.contains(range(3), 2.0) # acts like 2
|
||||
assert.fails(lambda: True in range(3), "requires integer.*not bool") # bools aren't numbers
|
||||
assert.fails(lambda: "one" in range(10), "requires integer.*not string")
|
||||
assert.true(4 not in range(4))
|
||||
assert.true(1e15 not in range(4)) # too big for int32
|
||||
assert.true(1e100 not in range(4)) # too big for int64
|
||||
# https://github.com/google/starlark-go/issues/116
|
||||
assert.fails(lambda: range(0, 0, 2)[:][0], "index 0 out of range: empty range")
|
||||
|
||||
# list
|
||||
assert.eq(list("abc".elems()), ["a", "b", "c"])
|
||||
assert.eq(sorted(list({"a": 1, "b": 2})), ['a', 'b'])
|
||||
|
||||
# min, max
|
||||
assert.eq(min(5, -2, 1, 7, 3), -2)
|
||||
assert.eq(max(5, -2, 1, 7, 3), 7)
|
||||
assert.eq(min([5, -2, 1, 7, 3]), -2)
|
||||
assert.eq(min("one", "two", "three", "four"), "four")
|
||||
assert.eq(max("one", "two", "three", "four"), "two")
|
||||
assert.fails(min, "min requires at least one positional argument")
|
||||
assert.fails(lambda: min(1), "not iterable")
|
||||
assert.fails(lambda: min([]), "empty")
|
||||
assert.eq(min(5, -2, 1, 7, 3, key=lambda x: x*x), 1) # min absolute value
|
||||
assert.eq(min(5, -2, 1, 7, 3, key=lambda x: -x), 7) # min negated value
|
||||
|
||||
# enumerate
|
||||
assert.eq(enumerate("abc".elems()), [(0, "a"), (1, "b"), (2, "c")])
|
||||
assert.eq(enumerate([False, True, None], 42), [(42, False), (43, True), (44, None)])
|
||||
|
||||
# zip
|
||||
assert.eq(zip(), [])
|
||||
assert.eq(zip([]), [])
|
||||
assert.eq(zip([1, 2, 3]), [(1,), (2,), (3,)])
|
||||
assert.eq(zip("".elems()), [])
|
||||
assert.eq(zip("abc".elems(),
|
||||
list("def".elems()),
|
||||
"hijk".elems()),
|
||||
[("a", "d", "h"), ("b", "e", "i"), ("c", "f", "j")])
|
||||
z1 = [1]
|
||||
assert.eq(zip(z1), [(1,)])
|
||||
z1.append(2)
|
||||
assert.eq(zip(z1), [(1,), (2,)])
|
||||
assert.fails(lambda: zip(z1, 1), "zip: argument #2 is not iterable: int")
|
||||
z1.append(3)
|
||||
|
||||
# dir for builtin_function_or_method
|
||||
assert.eq(dir(None), [])
|
||||
assert.eq(dir({})[:3], ["clear", "get", "items"]) # etc
|
||||
assert.eq(dir(1), [])
|
||||
assert.eq(dir([])[:3], ["append", "clear", "extend"]) # etc
|
||||
|
||||
# hasattr, getattr, dir
|
||||
# hasfields is an application-defined type defined in eval_test.go.
|
||||
hf = hasfields()
|
||||
assert.eq(dir(hf), [])
|
||||
assert.true(not hasattr(hf, "x"))
|
||||
assert.fails(lambda: getattr(hf, "x"), "no .x field or method")
|
||||
assert.eq(getattr(hf, "x", 42), 42)
|
||||
hf.x = 1
|
||||
assert.true(hasattr(hf, "x"))
|
||||
assert.eq(getattr(hf, "x"), 1)
|
||||
assert.eq(hf.x, 1)
|
||||
hf.x = 2
|
||||
assert.eq(getattr(hf, "x"), 2)
|
||||
assert.eq(hf.x, 2)
|
||||
# built-in types can have attributes (methods) too.
|
||||
myset = set([])
|
||||
assert.eq(dir(myset), ["add", "clear", "difference", "discard", "intersection", "issubset", "issuperset", "pop", "remove", "symmetric_difference", "union"])
|
||||
assert.true(hasattr(myset, "union"))
|
||||
assert.true(not hasattr(myset, "onion"))
|
||||
assert.eq(str(getattr(myset, "union")), "<built-in method union of set value>")
|
||||
assert.fails(lambda: getattr(myset, "onion"), "no .onion field or method")
|
||||
assert.eq(getattr(myset, "onion", 42), 42)
|
||||
|
||||
# dir returns a new, sorted, mutable list
|
||||
assert.eq(sorted(dir("")), dir("")) # sorted
|
||||
dir("").append("!") # mutable
|
||||
assert.true("!" not in dir("")) # new
|
||||
|
||||
# error messages should suggest spelling corrections
|
||||
hf.one = 1
|
||||
hf.two = 2
|
||||
hf.three = 3
|
||||
hf.forty_five = 45
|
||||
assert.fails(lambda: hf.One, 'no .One field.*did you mean .one')
|
||||
assert.fails(lambda: hf.oone, 'no .oone field.*did you mean .one')
|
||||
assert.fails(lambda: hf.FortyFive, 'no .FortyFive field.*did you mean .forty_five')
|
||||
assert.fails(lambda: hf.trhee, 'no .trhee field.*did you mean .three')
|
||||
assert.fails(lambda: hf.thirty, 'no .thirty field or method$') # no suggestion
|
||||
|
||||
# spell check in setfield too
|
||||
def setfield(): hf.noForty_Five = 46 # "no" prefix => SetField returns NoSuchField
|
||||
assert.fails(setfield, 'no .noForty_Five field.*did you mean .forty_five')
|
||||
|
||||
# repr
|
||||
assert.eq(repr(1), "1")
|
||||
assert.eq(repr("x"), '"x"')
|
||||
assert.eq(repr(["x", 1]), '["x", 1]')
|
||||
|
||||
# fail
|
||||
---
|
||||
fail() ### `fail: $`
|
||||
x = 1//0 # unreachable
|
||||
---
|
||||
fail(1) ### `fail: 1`
|
||||
---
|
||||
fail(1, 2, 3) ### `fail: 1 2 3`
|
||||
---
|
||||
fail(1, 2, 3, sep="/") ### `fail: 1/2/3`
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
# Tests of 'bytes' (immutable byte strings).
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# bytes(string) -- UTF-k to UTF-8 transcoding with U+FFFD replacement
|
||||
hello = bytes("hello, 世界")
|
||||
goodbye = bytes("goodbye")
|
||||
empty = bytes("")
|
||||
nonprinting = bytes("\t\n\x7F\u200D") # TAB, NEWLINE, DEL, ZERO_WIDTH_JOINER
|
||||
assert.eq(bytes("hello, 世界"[:-1]), b"hello, 世��")
|
||||
|
||||
# bytes(iterable of int) -- construct from numeric byte values
|
||||
assert.eq(bytes([65, 66, 67]), b"ABC")
|
||||
assert.eq(bytes((65, 66, 67)), b"ABC")
|
||||
assert.eq(bytes([0xf0, 0x9f, 0x98, 0xbf]), b"😿")
|
||||
assert.fails(lambda: bytes([300]),
|
||||
"at index 0, 300 out of range .want value in unsigned 8-bit range")
|
||||
assert.fails(lambda: bytes([b"a"]),
|
||||
"at index 0, got bytes, want int")
|
||||
assert.fails(lambda: bytes(1), "want string, bytes, or iterable of ints")
|
||||
|
||||
# literals
|
||||
assert.eq(b"hello, 世界", hello)
|
||||
assert.eq(b"goodbye", goodbye)
|
||||
assert.eq(b"", empty)
|
||||
assert.eq(b"\t\n\x7F\u200D", nonprinting)
|
||||
assert.ne("abc", b"abc")
|
||||
assert.eq(b"\012\xff\u0400\U0001F63F", b"\n\xffЀ😿") # see scanner tests for more
|
||||
assert.eq(rb"\r\n\t", b"\\r\\n\\t") # raw
|
||||
|
||||
# type
|
||||
assert.eq(type(hello), "bytes")
|
||||
|
||||
# len
|
||||
assert.eq(len(hello), 13)
|
||||
assert.eq(len(goodbye), 7)
|
||||
assert.eq(len(empty), 0)
|
||||
assert.eq(len(b"A"), 1)
|
||||
assert.eq(len(b"Ѐ"), 2)
|
||||
assert.eq(len(b"世"), 3)
|
||||
assert.eq(len(b"😿"), 4)
|
||||
|
||||
# truth
|
||||
assert.true(hello)
|
||||
assert.true(goodbye)
|
||||
assert.true(not empty)
|
||||
|
||||
# str(bytes) does UTF-8 to UTF-k transcoding.
|
||||
# TODO(adonovan): specify.
|
||||
assert.eq(str(hello), "hello, 世界")
|
||||
assert.eq(str(hello[:-1]), "hello, 世��") # incomplete UTF-8 encoding => U+FFFD
|
||||
assert.eq(str(goodbye), "goodbye")
|
||||
assert.eq(str(empty), "")
|
||||
assert.eq(str(nonprinting), "\t\n\x7f\u200d")
|
||||
assert.eq(str(b"\xED\xB0\x80"), "���") # UTF-8 encoding of unpaired surrogate => U+FFFD x 3
|
||||
|
||||
# repr
|
||||
assert.eq(repr(hello), r'b"hello, 世界"')
|
||||
assert.eq(repr(hello[:-1]), r'b"hello, 世\xe7\x95"') # (incomplete UTF-8 encoding )
|
||||
assert.eq(repr(goodbye), 'b"goodbye"')
|
||||
assert.eq(repr(empty), 'b""')
|
||||
assert.eq(repr(nonprinting), 'b"\\t\\n\\x7f\\u200d"')
|
||||
|
||||
# equality
|
||||
assert.eq(hello, hello)
|
||||
assert.ne(hello, goodbye)
|
||||
assert.eq(b"goodbye", goodbye)
|
||||
|
||||
# ordered comparison
|
||||
assert.lt(b"abc", b"abd")
|
||||
assert.lt(b"abc", b"abcd")
|
||||
assert.lt(b"\x7f", b"\x80") # bytes compare as uint8, not int8
|
||||
|
||||
# bytes are dict-hashable
|
||||
dict = {hello: 1, goodbye: 2}
|
||||
dict[b"goodbye"] = 3
|
||||
assert.eq(len(dict), 2)
|
||||
assert.eq(dict[goodbye], 3)
|
||||
|
||||
# hash(bytes) is 32-bit FNV-1a.
|
||||
assert.eq(hash(b""), 0x811c9dc5)
|
||||
assert.eq(hash(b"a"), 0xe40c292c)
|
||||
assert.eq(hash(b"ab"), 0x4d2505ca)
|
||||
assert.eq(hash(b"abc"), 0x1a47e90b)
|
||||
|
||||
# indexing
|
||||
assert.eq(goodbye[0], b"g")
|
||||
assert.eq(goodbye[-1], b"e")
|
||||
assert.fails(lambda: goodbye[100], "out of range")
|
||||
|
||||
# slicing
|
||||
assert.eq(goodbye[:4], b"good")
|
||||
assert.eq(goodbye[4:], b"bye")
|
||||
assert.eq(goodbye[::2], b"gobe")
|
||||
assert.eq(goodbye[3:4], b"d") # special case: len=1
|
||||
assert.eq(goodbye[4:4], b"") # special case: len=0
|
||||
|
||||
# bytes in bytes
|
||||
assert.eq(b"bc" in b"abcd", True)
|
||||
assert.eq(b"bc" in b"dcab", False)
|
||||
assert.fails(lambda: "bc" in b"dcab", "requires bytes or int as left operand, not string")
|
||||
|
||||
# int in bytes
|
||||
assert.eq(97 in b"abc", True) # 97='a'
|
||||
assert.eq(100 in b"abc", False) # 100='d'
|
||||
assert.fails(lambda: 256 in b"abc", "int in bytes: 256 out of range")
|
||||
assert.fails(lambda: -1 in b"abc", "int in bytes: -1 out of range")
|
||||
|
||||
# ord TODO(adonovan): specify
|
||||
assert.eq(ord(b"a"), 97)
|
||||
assert.fails(lambda: ord(b"ab"), "ord: bytes has length 2, want 1")
|
||||
assert.fails(lambda: ord(b""), "ord: bytes has length 0, want 1")
|
||||
|
||||
# repeat (bytes * int)
|
||||
assert.eq(goodbye * 3, b"goodbyegoodbyegoodbye")
|
||||
assert.eq(3 * goodbye, b"goodbyegoodbyegoodbye")
|
||||
|
||||
# elems() returns an iterable value over 1-byte substrings.
|
||||
assert.eq(type(hello.elems()), "bytes.elems")
|
||||
assert.eq(str(hello.elems()), "b\"hello, 世界\".elems()")
|
||||
assert.eq(list(hello.elems()), [104, 101, 108, 108, 111, 44, 32, 228, 184, 150, 231, 149, 140])
|
||||
assert.eq(bytes([104, 101, 108, 108, 111, 44, 32, 228, 184, 150, 231, 149, 140]), hello)
|
||||
assert.eq(list(goodbye.elems()), [103, 111, 111, 100, 98, 121, 101])
|
||||
assert.eq(list(empty.elems()), [])
|
||||
assert.eq(bytes(hello.elems()), hello) # bytes(iterable) is dual to bytes.elems()
|
||||
|
||||
# x[i] = ...
|
||||
def f():
|
||||
b"abc"[1] = b"B"
|
||||
|
||||
assert.fails(f, "bytes.*does not support.*assignment")
|
||||
|
||||
# TODO(adonovan): the specification is not finalized in many areas:
|
||||
# - chr, ord functions
|
||||
# - encoding/decoding bytes to string.
|
||||
# - methods: find, index, split, etc.
|
||||
#
|
||||
# Summary of string operations (put this in spec).
|
||||
#
|
||||
# string to number:
|
||||
# - bytes[i] returns numeric value of ith byte.
|
||||
# - ord(string) returns numeric value of sole code point in string.
|
||||
# - ord(string[i]) is not a useful operation: fails on non-ASCII; see below.
|
||||
# Q. Perhaps ord should return the first (not sole) code point? Then it becomes a UTF-8 decoder.
|
||||
# Perhaps ord(string, index=int) should apply the index and relax the len=1 check.
|
||||
# - string.codepoint() iterates over 1-codepoint substrings.
|
||||
# - string.codepoint_ords() iterates over numeric values of code points in string.
|
||||
# - string.elems() iterates over 1-element (UTF-k code) substrings.
|
||||
# - string.elem_ords() iterates over numeric UTF-k code values.
|
||||
# - string.elem_ords()[i] returns numeric value of ith element (UTF-k code).
|
||||
# - string.elems()[i] returns substring of a single element (UTF-k code).
|
||||
# - int(string) parses string as decimal (or other) numeric literal.
|
||||
#
|
||||
# number to string:
|
||||
# - chr(int) returns string, UTF-k encoding of Unicode code point (like Python).
|
||||
# Redundant with '%c' % int (which Python2 calls 'unichr'.)
|
||||
# - bytes(chr(int)) returns byte string containing UTF-8 encoding of one code point.
|
||||
# - bytes([int]) returns 1-byte string (with regrettable list allocation).
|
||||
# - str(int) - format number as decimal.
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
# Tests of Starlark control flow
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def controlflow():
|
||||
# elif
|
||||
x = 0
|
||||
if True:
|
||||
x=1
|
||||
elif False:
|
||||
assert.fail("else of true")
|
||||
else:
|
||||
assert.fail("else of else of true")
|
||||
assert.true(x)
|
||||
|
||||
x = 0
|
||||
if False:
|
||||
assert.fail("then of false")
|
||||
elif True:
|
||||
x = 1
|
||||
else:
|
||||
assert.fail("else of true")
|
||||
assert.true(x)
|
||||
|
||||
x = 0
|
||||
if False:
|
||||
assert.fail("then of false")
|
||||
elif False:
|
||||
assert.fail("then of false")
|
||||
else:
|
||||
x = 1
|
||||
assert.true(x)
|
||||
controlflow()
|
||||
|
||||
def loops():
|
||||
y = ""
|
||||
for x in [1, 2, 3, 4, 5]:
|
||||
if x == 2:
|
||||
continue
|
||||
if x == 4:
|
||||
break
|
||||
y = y + str(x)
|
||||
return y
|
||||
assert.eq(loops(), "13")
|
||||
|
||||
# return
|
||||
g = 123
|
||||
def f(x):
|
||||
for g in (1, 2, 3):
|
||||
if g == x:
|
||||
return g
|
||||
assert.eq(f(2), 2)
|
||||
assert.eq(f(4), None) # falling off end => return None
|
||||
assert.eq(g, 123) # unchanged by local use of g in function
|
||||
|
||||
# infinite sequences
|
||||
def fib(n):
|
||||
seq = []
|
||||
for x in fibonacci: # fibonacci is an infinite iterable defined in eval_test.go
|
||||
if len(seq) == n:
|
||||
break
|
||||
seq.append(x)
|
||||
return seq
|
||||
assert.eq(fib(10), [0, 1, 1, 2, 3, 5, 8, 13, 21, 34])
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
# Tests of Starlark 'dict'
|
||||
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
# literals
|
||||
assert.eq({}, {})
|
||||
assert.eq({"a": 1}, {"a": 1})
|
||||
assert.eq({"a": 1,}, {"a": 1})
|
||||
|
||||
# truth
|
||||
assert.true({False: False})
|
||||
assert.true(not {})
|
||||
|
||||
# dict + dict is no longer supported.
|
||||
assert.fails(lambda: {"a": 1} + {"b": 2}, 'unknown binary op: dict \\+ dict')
|
||||
|
||||
# dict comprehension
|
||||
assert.eq({x: x*x for x in range(3)}, {0: 0, 1: 1, 2: 4})
|
||||
|
||||
# dict.pop
|
||||
x6 = {"a": 1, "b": 2}
|
||||
assert.eq(x6.pop("a"), 1)
|
||||
assert.eq(str(x6), '{"b": 2}')
|
||||
assert.fails(lambda: x6.pop("c"), "pop: missing key")
|
||||
assert.eq(x6.pop("c", 3), 3)
|
||||
assert.eq(x6.pop("c", None), None) # default=None tests an edge case of UnpackArgs
|
||||
assert.eq(x6.pop("b"), 2)
|
||||
assert.eq(len(x6), 0)
|
||||
|
||||
# dict.popitem
|
||||
x7 = {"a": 1, "b": 2}
|
||||
assert.eq([x7.popitem(), x7.popitem()], [("a", 1), ("b", 2)])
|
||||
assert.fails(x7.popitem, "empty dict")
|
||||
assert.eq(len(x7), 0)
|
||||
|
||||
# dict.keys, dict.values
|
||||
x8 = {"a": 1, "b": 2}
|
||||
assert.eq(x8.keys(), ["a", "b"])
|
||||
assert.eq(x8.values(), [1, 2])
|
||||
|
||||
# equality
|
||||
assert.eq({"a": 1, "b": 2}, {"a": 1, "b": 2})
|
||||
assert.eq({"a": 1, "b": 2,}, {"a": 1, "b": 2})
|
||||
assert.eq({"a": 1, "b": 2}, {"b": 2, "a": 1})
|
||||
|
||||
# insertion order is preserved
|
||||
assert.eq(dict([("a", 0), ("b", 1), ("c", 2), ("b", 3)]).keys(), ["a", "b", "c"])
|
||||
assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)]).keys(), ["b", "a", "c"])
|
||||
assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)])["b"], 2)
|
||||
# ...even after rehashing (which currently occurs after key 'i'):
|
||||
small = dict([("a", 0), ("b", 1), ("c", 2)])
|
||||
small.update([("d", 4), ("e", 5), ("f", 6), ("g", 7), ("h", 8), ("i", 9), ("j", 10), ("k", 11)])
|
||||
assert.eq(small.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"])
|
||||
|
||||
# Duplicate keys are not permitted in dictionary expressions (see b/35698444).
|
||||
# (Nor in keyword args to function calls---checked by resolver.)
|
||||
assert.fails(lambda: {"aa": 1, "bb": 2, "cc": 3, "bb": 4}, 'duplicate key: "bb"')
|
||||
|
||||
# Check that even with many positional args, keyword collisions are detected.
|
||||
assert.fails(lambda: dict({'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"')
|
||||
assert.fails(lambda: dict({'a': 2, 'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"')
|
||||
# positional/keyword arg key collisions are ok
|
||||
assert.eq(dict((['a', 2], ), a=4), {'a': 4})
|
||||
assert.eq(dict((['a', 2], ['a', 3]), a=4), {'a': 4})
|
||||
|
||||
# index
|
||||
def setIndex(d, k, v):
|
||||
d[k] = v
|
||||
|
||||
x9 = {}
|
||||
assert.fails(lambda: x9["a"], 'key "a" not in dict')
|
||||
x9["a"] = 1
|
||||
assert.eq(x9["a"], 1)
|
||||
assert.eq(x9, {"a": 1})
|
||||
assert.fails(lambda: setIndex(x9, [], 2), 'unhashable type: list')
|
||||
freeze(x9)
|
||||
assert.fails(lambda: setIndex(x9, "a", 3), 'cannot insert into frozen hash table')
|
||||
|
||||
x9a = {}
|
||||
x9a[1, 2] = 3 # unparenthesized tuple is allowed here
|
||||
assert.eq(x9a.keys()[0], (1, 2))
|
||||
|
||||
# dict.get
|
||||
x10 = {"a": 1}
|
||||
assert.eq(x10.get("a"), 1)
|
||||
assert.eq(x10.get("b"), None)
|
||||
assert.eq(x10.get("a", 2), 1)
|
||||
assert.eq(x10.get("b", 2), 2)
|
||||
|
||||
# dict.clear
|
||||
x11 = {"a": 1}
|
||||
assert.contains(x11, "a")
|
||||
assert.eq(x11["a"], 1)
|
||||
x11.clear()
|
||||
assert.fails(lambda: x11["a"], 'key "a" not in dict')
|
||||
assert.true("a" not in x11)
|
||||
freeze(x11)
|
||||
assert.fails(x11.clear, "cannot clear frozen hash table")
|
||||
|
||||
# dict.setdefault
|
||||
x12 = {"a": 1}
|
||||
assert.eq(x12.setdefault("a"), 1)
|
||||
assert.eq(x12["a"], 1)
|
||||
assert.eq(x12.setdefault("b"), None)
|
||||
assert.eq(x12["b"], None)
|
||||
assert.eq(x12.setdefault("c", 2), 2)
|
||||
assert.eq(x12["c"], 2)
|
||||
assert.eq(x12.setdefault("c", 3), 2)
|
||||
assert.eq(x12["c"], 2)
|
||||
freeze(x12)
|
||||
assert.eq(x12.setdefault("a", 1), 1) # no change, no error
|
||||
assert.fails(lambda: x12.setdefault("d", 1), "cannot insert into frozen hash table")
|
||||
|
||||
# dict.update
|
||||
x13 = {"a": 1}
|
||||
x13.update(a=2, b=3)
|
||||
assert.eq(x13, {"a": 2, "b": 3})
|
||||
x13.update([("b", 4), ("c", 5)])
|
||||
assert.eq(x13, {"a": 2, "b": 4, "c": 5})
|
||||
x13.update({"c": 6, "d": 7})
|
||||
assert.eq(x13, {"a": 2, "b": 4, "c": 6, "d": 7})
|
||||
freeze(x13)
|
||||
assert.fails(lambda: x13.update({"a": 8}), "cannot insert into frozen hash table")
|
||||
|
||||
# dict as a sequence
|
||||
#
|
||||
# for loop
|
||||
x14 = {1:2, 3:4}
|
||||
def keys(dict):
|
||||
keys = []
|
||||
for k in dict: keys.append(k)
|
||||
return keys
|
||||
assert.eq(keys(x14), [1, 3])
|
||||
#
|
||||
# comprehension
|
||||
assert.eq([x for x in x14], [1, 3])
|
||||
#
|
||||
# varargs
|
||||
def varargs(*args): return args
|
||||
x15 = {"one": 1}
|
||||
assert.eq(varargs(*x15), ("one",))
|
||||
|
||||
# kwargs parameter does not alias the **kwargs dict
|
||||
def kwargs(**kwargs): return kwargs
|
||||
x16 = kwargs(**x15)
|
||||
assert.eq(x16, x15)
|
||||
x15["two"] = 2 # mutate
|
||||
assert.ne(x16, x15)
|
||||
|
||||
# iterator invalidation
|
||||
def iterator1():
|
||||
dict = {1:1, 2:1}
|
||||
for k in dict:
|
||||
dict[2*k] = dict[k]
|
||||
assert.fails(iterator1, "insert.*during iteration")
|
||||
|
||||
def iterator2():
|
||||
dict = {1:1, 2:1}
|
||||
for k in dict:
|
||||
dict.pop(k)
|
||||
assert.fails(iterator2, "delete.*during iteration")
|
||||
|
||||
def iterator3():
|
||||
def f(d):
|
||||
d[3] = 3
|
||||
dict = {1:1, 2:1}
|
||||
_ = [f(dict) for x in dict]
|
||||
assert.fails(iterator3, "insert.*during iteration")
|
||||
|
||||
# This assignment is not a modification-during-iteration:
|
||||
# the sequence x should be completely iterated before
|
||||
# the assignment occurs.
|
||||
def f():
|
||||
x = {1:2, 2:4}
|
||||
a, x[0] = x
|
||||
assert.eq(a, 1)
|
||||
assert.eq(x, {1: 2, 2: 4, 0: 2})
|
||||
f()
|
||||
|
||||
# Regression test for a bug in hashtable.delete
|
||||
def test_delete():
|
||||
d = {}
|
||||
|
||||
# delete tail first
|
||||
d["one"] = 1
|
||||
d["two"] = 2
|
||||
assert.eq(str(d), '{"one": 1, "two": 2}')
|
||||
d.pop("two")
|
||||
assert.eq(str(d), '{"one": 1}')
|
||||
d.pop("one")
|
||||
assert.eq(str(d), '{}')
|
||||
|
||||
# delete head first
|
||||
d["one"] = 1
|
||||
d["two"] = 2
|
||||
assert.eq(str(d), '{"one": 1, "two": 2}')
|
||||
d.pop("one")
|
||||
assert.eq(str(d), '{"two": 2}')
|
||||
d.pop("two")
|
||||
assert.eq(str(d), '{}')
|
||||
|
||||
# delete middle
|
||||
d["one"] = 1
|
||||
d["two"] = 2
|
||||
d["three"] = 3
|
||||
assert.eq(str(d), '{"one": 1, "two": 2, "three": 3}')
|
||||
d.pop("two")
|
||||
assert.eq(str(d), '{"one": 1, "three": 3}')
|
||||
d.pop("three")
|
||||
assert.eq(str(d), '{"one": 1}')
|
||||
d.pop("one")
|
||||
assert.eq(str(d), '{}')
|
||||
|
||||
test_delete()
|
||||
|
||||
# Regression test for github.com/google/starlark-go/issues/128.
|
||||
assert.fails(lambda: dict(None), 'got NoneType, want iterable')
|
||||
assert.fails(lambda: {}.update(None), 'got NoneType, want iterable')
|
||||
|
||||
---
|
||||
# Verify position of an "unhashable key" error in a dict literal.
|
||||
|
||||
_ = {
|
||||
"one": 1,
|
||||
["two"]: 2, ### "unhashable type: list"
|
||||
"three": 3,
|
||||
}
|
||||
|
||||
---
|
||||
# Verify position of a "duplicate key" error in a dict literal.
|
||||
|
||||
_ = {
|
||||
"one": 1,
|
||||
"one": 1, ### `duplicate key: "one"`
|
||||
"three": 3,
|
||||
}
|
||||
|
||||
---
|
||||
# Verify position of an "unhashable key" error in a dict comprehension.
|
||||
|
||||
_ = {
|
||||
k: v ### "unhashable type: list"
|
||||
for k, v in [
|
||||
("one", 1),
|
||||
(["two"], 2),
|
||||
("three", 3),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
---
|
||||
# dict | dict (union)
|
||||
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
empty_dict = dict()
|
||||
dict_with_a_b = dict(a=1, b=[1, 2])
|
||||
dict_with_b = dict(b=[1, 2])
|
||||
dict_with_other_b = dict(b=[3, 4])
|
||||
|
||||
assert.eq(empty_dict | dict_with_a_b, dict_with_a_b)
|
||||
# Verify iteration order.
|
||||
assert.eq((empty_dict | dict_with_a_b).items(), dict_with_a_b.items())
|
||||
assert.eq(dict_with_a_b | empty_dict, dict_with_a_b)
|
||||
assert.eq((dict_with_a_b | empty_dict).items(), dict_with_a_b.items())
|
||||
assert.eq(dict_with_b | dict_with_a_b, dict_with_a_b)
|
||||
assert.eq((dict_with_b | dict_with_a_b).items(), dict(b=[1, 2], a=1).items())
|
||||
assert.eq(dict_with_a_b | dict_with_b, dict_with_a_b)
|
||||
assert.eq((dict_with_a_b | dict_with_b).items(), dict_with_a_b.items())
|
||||
assert.eq(dict_with_b | dict_with_other_b, dict_with_other_b)
|
||||
assert.eq((dict_with_b | dict_with_other_b).items(), dict_with_other_b.items())
|
||||
assert.eq(dict_with_other_b | dict_with_b, dict_with_b)
|
||||
assert.eq((dict_with_other_b | dict_with_b).items(), dict_with_b.items())
|
||||
|
||||
assert.eq(empty_dict, dict())
|
||||
assert.eq(dict_with_b, dict(b=[1,2]))
|
||||
|
||||
assert.fails(lambda: dict() | [], "unknown binary op: dict [|] list")
|
||||
|
||||
# dict |= dict (in-place union)
|
||||
|
||||
def test_dict_union_assignment():
|
||||
x = dict()
|
||||
saved = x
|
||||
x |= {"a": 1}
|
||||
x |= {"b": 2}
|
||||
x |= {"c": "3", 7: 4}
|
||||
x |= {"b": "5", "e": 6}
|
||||
want = {"a": 1, "b": "5", "c": "3", 7: 4, "e": 6}
|
||||
assert.eq(x, want)
|
||||
assert.eq(x.items(), want.items())
|
||||
assert.eq(saved, x) # they are aliases
|
||||
|
||||
a = {8: 1, "b": 2}
|
||||
b = {"b": 1, "c": 6}
|
||||
c = {"d": 7}
|
||||
d = {(5, "a"): ("c", 8)}
|
||||
orig_a, orig_c = a, c
|
||||
a |= b
|
||||
c |= a
|
||||
c |= d
|
||||
expected_2 = {"d": 7, 8: 1, "b": 1, "c": 6, (5, "a"): ("c", 8)}
|
||||
assert.eq(c, expected_2)
|
||||
assert.eq(c.items(), expected_2.items())
|
||||
assert.eq(b, {"b": 1, "c": 6})
|
||||
|
||||
# aliasing:
|
||||
assert.eq(a, orig_a)
|
||||
assert.eq(c, orig_c)
|
||||
a.clear()
|
||||
c.clear()
|
||||
assert.eq(a, orig_a)
|
||||
assert.eq(c, orig_c)
|
||||
|
||||
test_dict_union_assignment()
|
||||
|
||||
def dict_union_assignment_type_mismatch():
|
||||
some_dict = dict()
|
||||
some_dict |= []
|
||||
|
||||
assert.fails(dict_union_assignment_type_mismatch, "unknown binary op: dict [|] list")
|
||||
+508
@@ -0,0 +1,508 @@
|
||||
# Tests of Starlark 'float'
|
||||
# option:set
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# TODO(adonovan): more tests:
|
||||
# - precision
|
||||
# - limits
|
||||
|
||||
# type
|
||||
assert.eq(type(0.0), "float")
|
||||
|
||||
# truth
|
||||
assert.true(123.0)
|
||||
assert.true(-1.0)
|
||||
assert.true(not 0.0)
|
||||
assert.true(-1.0e-45)
|
||||
assert.true(float("NaN"))
|
||||
|
||||
# not iterable
|
||||
assert.fails(lambda: len(0.0), 'has no len')
|
||||
assert.fails(lambda: [x for x in 0.0], 'float value is not iterable')
|
||||
|
||||
# literals
|
||||
assert.eq(type(1.234), "float")
|
||||
assert.eq(type(1e10), "float")
|
||||
assert.eq(type(1e+10), "float")
|
||||
assert.eq(type(1e-10), "float")
|
||||
assert.eq(type(1.234e10), "float")
|
||||
assert.eq(type(1.234e+10), "float")
|
||||
assert.eq(type(1.234e-10), "float")
|
||||
|
||||
# int/float equality
|
||||
assert.eq(0.0, 0)
|
||||
assert.eq(0, 0.0)
|
||||
assert.eq(1.0, 1)
|
||||
assert.eq(1, 1.0)
|
||||
assert.true(1.23e45 != 1229999999999999973814869011019624571608236031)
|
||||
assert.true(1.23e45 == 1229999999999999973814869011019624571608236032)
|
||||
assert.true(1.23e45 != 1229999999999999973814869011019624571608236033)
|
||||
assert.true(1229999999999999973814869011019624571608236031 != 1.23e45)
|
||||
assert.true(1229999999999999973814869011019624571608236032 == 1.23e45)
|
||||
assert.true(1229999999999999973814869011019624571608236033 != 1.23e45)
|
||||
|
||||
# loss of precision
|
||||
p53 = 1<<53
|
||||
assert.eq(float(p53-1), p53-1)
|
||||
assert.eq(float(p53+0), p53+0)
|
||||
assert.eq(float(p53+1), p53+0) #
|
||||
assert.eq(float(p53+2), p53+2)
|
||||
assert.eq(float(p53+3), p53+4) #
|
||||
assert.eq(float(p53+4), p53+4)
|
||||
assert.eq(float(p53+5), p53+4) #
|
||||
assert.eq(float(p53+6), p53+6)
|
||||
assert.eq(float(p53+7), p53+8) #
|
||||
assert.eq(float(p53+8), p53+8)
|
||||
|
||||
# Regression test for https://github.com/google/starlark-go/issues/375.
|
||||
maxint64 = (1<<63)-1
|
||||
assert.eq(int(float(maxint64)), 9223372036854775808)
|
||||
|
||||
assert.true(float(p53+1) != p53+1) # comparisons are exact
|
||||
assert.eq(float(p53+1) - (p53+1), 0) # arithmetic entails rounding
|
||||
|
||||
assert.fails(lambda: {123.0: "f", 123: "i"}, "duplicate key: 123")
|
||||
|
||||
# equal int/float values have same hash
|
||||
d = {123.0: "x"}
|
||||
d[123] = "y"
|
||||
assert.eq(len(d), 1)
|
||||
assert.eq(d[123.0], "y")
|
||||
|
||||
# literals (mostly covered by scanner tests)
|
||||
assert.eq(str(0.), "0.0")
|
||||
assert.eq(str(.0), "0.0")
|
||||
assert.true(5.0 != 4.999999999999999)
|
||||
assert.eq(5.0, 4.9999999999999999) # both literals denote 5.0
|
||||
assert.eq(1.23e45, 1.23 * 1000000000000000000000000000000000000000000000)
|
||||
assert.eq(str(1.23e-45 - (1.23 / 1000000000000000000000000000000000000000000000)), "-1.5557538194652854e-61")
|
||||
|
||||
nan = float("NaN")
|
||||
inf = float("+Inf")
|
||||
neginf = float("-Inf")
|
||||
negzero = (-1e-323 / 10)
|
||||
|
||||
# -- arithmetic --
|
||||
|
||||
# +float, -float
|
||||
assert.eq(+(123.0), 123.0)
|
||||
assert.eq(-(123.0), -123.0)
|
||||
assert.eq(-(-(123.0)), 123.0)
|
||||
assert.eq(+(inf), inf)
|
||||
assert.eq(-(inf), neginf)
|
||||
assert.eq(-(neginf), inf)
|
||||
assert.eq(str(-(nan)), "nan")
|
||||
# +
|
||||
assert.eq(1.2e3 + 5.6e7, 5.60012e+07)
|
||||
assert.eq(1.2e3 + 1, 1201)
|
||||
assert.eq(1 + 1.2e3, 1201)
|
||||
assert.eq(str(1.2e3 + nan), "nan")
|
||||
assert.eq(inf + 0, inf)
|
||||
assert.eq(inf + 1, inf)
|
||||
assert.eq(inf + inf, inf)
|
||||
assert.eq(str(inf + neginf), "nan")
|
||||
# -
|
||||
assert.eq(1.2e3 - 5.6e7, -5.59988e+07)
|
||||
assert.eq(1.2e3 - 1, 1199)
|
||||
assert.eq(1 - 1.2e3, -1199)
|
||||
assert.eq(str(1.2e3 - nan), "nan")
|
||||
assert.eq(inf - 0, inf)
|
||||
assert.eq(inf - 1, inf)
|
||||
assert.eq(str(inf - inf), "nan")
|
||||
assert.eq(inf - neginf, inf)
|
||||
# *
|
||||
assert.eq(1.5e6 * 2.2e3, 3.3e9)
|
||||
assert.eq(1.5e6 * 123, 1.845e+08)
|
||||
assert.eq(123 * 1.5e6, 1.845e+08)
|
||||
assert.eq(str(1.2e3 * nan), "nan")
|
||||
assert.eq(str(inf * 0), "nan")
|
||||
assert.eq(inf * 1, inf)
|
||||
assert.eq(inf * inf, inf)
|
||||
assert.eq(inf * neginf, neginf)
|
||||
# %
|
||||
assert.eq(100.0 % 7.0, 2)
|
||||
assert.eq(100.0 % -7.0, -5) # NB: different from Go / Java
|
||||
assert.eq(-100.0 % 7.0, 5) # NB: different from Go / Java
|
||||
assert.eq(-100.0 % -7.0, -2)
|
||||
assert.eq(-100.0 % 7, 5)
|
||||
assert.eq(100 % 7.0, 2)
|
||||
assert.eq(str(1.2e3 % nan), "nan")
|
||||
assert.eq(str(inf % 1), "nan")
|
||||
assert.eq(str(inf % inf), "nan")
|
||||
assert.eq(str(inf % neginf), "nan")
|
||||
# /
|
||||
assert.eq(str(100.0 / 7.0), "14.285714285714286")
|
||||
assert.eq(str(100 / 7.0), "14.285714285714286")
|
||||
assert.eq(str(100.0 / 7), "14.285714285714286")
|
||||
assert.eq(str(100.0 / nan), "nan")
|
||||
# //
|
||||
assert.eq(100.0 // 7.0, 14)
|
||||
assert.eq(100 // 7.0, 14)
|
||||
assert.eq(100.0 // 7, 14)
|
||||
assert.eq(100.0 // -7.0, -15)
|
||||
assert.eq(100 // -7.0, -15)
|
||||
assert.eq(100.0 // -7, -15)
|
||||
assert.eq(str(1 // neginf), "-0.0")
|
||||
assert.eq(str(100.0 // nan), "nan")
|
||||
|
||||
# addition
|
||||
assert.eq(0.0 + 1.0, 1.0)
|
||||
assert.eq(1.0 + 1.0, 2.0)
|
||||
assert.eq(1.25 + 2.75, 4.0)
|
||||
assert.eq(5.0 + 7.0, 12.0)
|
||||
assert.eq(5.1 + 7, 12.1) # float + int
|
||||
assert.eq(7 + 5.1, 12.1) # int + float
|
||||
|
||||
# subtraction
|
||||
assert.eq(5.0 - 7.0, -2.0)
|
||||
assert.eq(5.1 - 7.1, -2.0)
|
||||
assert.eq(5.5 - 7, -1.5)
|
||||
assert.eq(5 - 7.5, -2.5)
|
||||
assert.eq(0.0 - 1.0, -1.0)
|
||||
|
||||
# multiplication
|
||||
assert.eq(5.0 * 7.0, 35.0)
|
||||
assert.eq(5.5 * 2.5, 13.75)
|
||||
assert.eq(5.5 * 7, 38.5)
|
||||
assert.eq(5 * 7.1, 35.5)
|
||||
|
||||
# real division (like Python 3)
|
||||
# The / operator is available only when the 'fp' dialect option is enabled.
|
||||
assert.eq(100.0 / 8.0, 12.5)
|
||||
assert.eq(100.0 / -8.0, -12.5)
|
||||
assert.eq(-100.0 / 8.0, -12.5)
|
||||
assert.eq(-100.0 / -8.0, 12.5)
|
||||
assert.eq(98.0 / 8.0, 12.25)
|
||||
assert.eq(98.0 / -8.0, -12.25)
|
||||
assert.eq(-98.0 / 8.0, -12.25)
|
||||
assert.eq(-98.0 / -8.0, 12.25)
|
||||
assert.eq(2.5 / 2.0, 1.25)
|
||||
assert.eq(2.5 / 2, 1.25)
|
||||
assert.eq(5 / 4.0, 1.25)
|
||||
assert.eq(5 / 4, 1.25)
|
||||
assert.fails(lambda: 1.0 / 0, "floating-point division by zero")
|
||||
assert.fails(lambda: 1.0 / 0.0, "floating-point division by zero")
|
||||
assert.fails(lambda: 1 / 0.0, "floating-point division by zero")
|
||||
|
||||
# floored division
|
||||
assert.eq(100.0 // 8.0, 12.0)
|
||||
assert.eq(100.0 // -8.0, -13.0)
|
||||
assert.eq(-100.0 // 8.0, -13.0)
|
||||
assert.eq(-100.0 // -8.0, 12.0)
|
||||
assert.eq(98.0 // 8.0, 12.0)
|
||||
assert.eq(98.0 // -8.0, -13.0)
|
||||
assert.eq(-98.0 // 8.0, -13.0)
|
||||
assert.eq(-98.0 // -8.0, 12.0)
|
||||
assert.eq(2.5 // 2.0, 1.0)
|
||||
assert.eq(2.5 // 2, 1.0)
|
||||
assert.eq(5 // 4.0, 1.0)
|
||||
assert.eq(5 // 4, 1)
|
||||
assert.eq(type(5 // 4), "int")
|
||||
assert.fails(lambda: 1.0 // 0, "floored division by zero")
|
||||
assert.fails(lambda: 1.0 // 0.0, "floored division by zero")
|
||||
assert.fails(lambda: 1 // 0.0, "floored division by zero")
|
||||
|
||||
# remainder
|
||||
assert.eq(100.0 % 8.0, 4.0)
|
||||
assert.eq(100.0 % -8.0, -4.0)
|
||||
assert.eq(-100.0 % 8.0, 4.0)
|
||||
assert.eq(-100.0 % -8.0, -4.0)
|
||||
assert.eq(98.0 % 8.0, 2.0)
|
||||
assert.eq(98.0 % -8.0, -6.0)
|
||||
assert.eq(-98.0 % 8.0, 6.0)
|
||||
assert.eq(-98.0 % -8.0, -2.0)
|
||||
assert.eq(2.5 % 2.0, 0.5)
|
||||
assert.eq(2.5 % 2, 0.5)
|
||||
assert.eq(5 % 4.0, 1.0)
|
||||
assert.fails(lambda: 1.0 % 0, "floating-point modulo by zero")
|
||||
assert.fails(lambda: 1.0 % 0.0, "floating-point modulo by zero")
|
||||
assert.fails(lambda: 1 % 0.0, "floating-point modulo by zero")
|
||||
|
||||
# floats cannot be used as indices, even if integral
|
||||
assert.fails(lambda: "abc"[1.0], "want int")
|
||||
assert.fails(lambda: ["A", "B", "C"].insert(1.0, "D"), "want int")
|
||||
assert.fails(lambda: range(3)[1.0], "got float, want int")
|
||||
|
||||
# -- comparisons --
|
||||
# NaN
|
||||
assert.true(nan == nan) # \
|
||||
assert.true(nan >= nan) # unlike Python
|
||||
assert.true(nan <= nan) # /
|
||||
assert.true(not (nan > nan))
|
||||
assert.true(not (nan < nan))
|
||||
assert.true(not (nan != nan)) # unlike Python
|
||||
# Sort is stable: 0.0 and -0.0 are equal, but they are not permuted.
|
||||
# Similarly 1 and 1.0.
|
||||
assert.eq(
|
||||
str(sorted([inf, neginf, nan, 1e300, -1e300, 1.0, -1.0, 1, -1, 1e-300, -1e-300, 0, 0.0, negzero, 1e-300, -1e-300])),
|
||||
"[-inf, -1e+300, -1.0, -1, -1e-300, -1e-300, 0, 0.0, -0.0, 1e-300, 1e-300, 1.0, 1, 1e+300, +inf, nan]")
|
||||
|
||||
# Sort is stable, and its result contains no adjacent x, y such that y > x.
|
||||
# Note: Python's reverse sort is unstable; see https://bugs.python.org/issue36095.
|
||||
assert.eq(str(sorted([7, 3, nan, 1, 9])), "[1, 3, 7, 9, nan]")
|
||||
assert.eq(str(sorted([7, 3, nan, 1, 9], reverse=True)), "[nan, 9, 7, 3, 1]")
|
||||
|
||||
# All NaN values compare equal. (Identical objects compare equal.)
|
||||
nandict = {nan: 1}
|
||||
nandict[nan] = 2
|
||||
assert.eq(len(nandict), 1) # (same as Python)
|
||||
assert.eq(nandict[nan], 2) # (same as Python)
|
||||
assert.fails(lambda: {nan: 1, nan: 2}, "duplicate key: nan")
|
||||
|
||||
nandict[float('nan')] = 3 # a distinct NaN object
|
||||
assert.eq(str(nandict), "{nan: 3}") # (Python: {nan: 2, nan: 3})
|
||||
|
||||
assert.eq(str({inf: 1, neginf: 2}), "{+inf: 1, -inf: 2}")
|
||||
|
||||
# zero
|
||||
assert.eq(0.0, negzero)
|
||||
|
||||
# inf
|
||||
assert.eq(+inf / +inf, nan)
|
||||
assert.eq(+inf / -inf, nan)
|
||||
assert.eq(-inf / +inf, nan)
|
||||
assert.eq(0.0 / +inf, 0.0)
|
||||
assert.eq(0.0 / -inf, 0.0)
|
||||
assert.true(inf > -inf)
|
||||
assert.eq(inf, -neginf)
|
||||
# TODO(adonovan): assert inf > any finite number, etc.
|
||||
|
||||
# negative zero
|
||||
negz = -0
|
||||
assert.eq(negz, 0)
|
||||
|
||||
# min/max ordering with NaN (the greatest float value)
|
||||
assert.eq(max([1, nan, 3]), nan)
|
||||
assert.eq(max([nan, 2, 3]), nan)
|
||||
assert.eq(min([1, nan, 3]), 1)
|
||||
assert.eq(min([nan, 2, 3]), 2)
|
||||
|
||||
# float/float comparisons
|
||||
fltmax = 1.7976931348623157e+308 # approx
|
||||
fltmin = 4.9406564584124654e-324 # approx
|
||||
assert.lt(-inf, -fltmax)
|
||||
assert.lt(-fltmax, -1.0)
|
||||
assert.lt(-1.0, -fltmin)
|
||||
assert.lt(-fltmin, 0.0)
|
||||
assert.lt(0, fltmin)
|
||||
assert.lt(fltmin, 1.0)
|
||||
assert.lt(1.0, fltmax)
|
||||
assert.lt(fltmax, inf)
|
||||
|
||||
# int/float comparisons
|
||||
assert.eq(0, 0.0)
|
||||
assert.eq(1, 1.0)
|
||||
assert.eq(-1, -1.0)
|
||||
assert.ne(-1, -1.0 + 1e-7)
|
||||
assert.lt(-2, -2 + 1e-15)
|
||||
|
||||
# int conversion (rounds towards zero)
|
||||
assert.eq(int(100.1), 100)
|
||||
assert.eq(int(100.0), 100)
|
||||
assert.eq(int(99.9), 99)
|
||||
assert.eq(int(-99.9), -99)
|
||||
assert.eq(int(-100.0), -100)
|
||||
assert.eq(int(-100.1), -100)
|
||||
assert.eq(int(1e100), int("10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104"))
|
||||
assert.fails(lambda: int(inf), "cannot convert.*infinity")
|
||||
assert.fails(lambda: int(nan), "cannot convert.*NaN")
|
||||
|
||||
# -- float() function --
|
||||
assert.eq(float(), 0.0)
|
||||
# float(bool)
|
||||
assert.eq(float(False), 0.0)
|
||||
assert.eq(float(True), 1.0)
|
||||
# float(int)
|
||||
assert.eq(float(0), 0.0)
|
||||
assert.eq(float(1), 1.0)
|
||||
assert.eq(float(123), 123.0)
|
||||
assert.eq(float(123 * 1000000 * 1000000 * 1000000 * 1000000 * 1000000), 1.23e+32)
|
||||
# float(float)
|
||||
assert.eq(float(1.1), 1.1)
|
||||
assert.fails(lambda: float(None), "want number or string")
|
||||
assert.ne(False, 0.0) # differs from Python
|
||||
assert.ne(True, 1.0)
|
||||
# float(string)
|
||||
assert.eq(float("1.1"), 1.1)
|
||||
assert.fails(lambda: float("1.1abc"), "invalid float literal")
|
||||
assert.fails(lambda: float("1e100.0"), "invalid float literal")
|
||||
assert.fails(lambda: float("1e1000"), "floating-point number too large")
|
||||
assert.eq(float("-1.1"), -1.1)
|
||||
assert.eq(float("+1.1"), +1.1)
|
||||
assert.eq(float("+Inf"), inf)
|
||||
assert.eq(float("-Inf"), neginf)
|
||||
assert.eq(float("NaN"), nan)
|
||||
assert.eq(float("NaN"), nan)
|
||||
assert.eq(float("+NAN"), nan)
|
||||
assert.eq(float("-nan"), nan)
|
||||
assert.eq(str(float("Inf")), "+inf")
|
||||
assert.eq(str(float("+INF")), "+inf")
|
||||
assert.eq(str(float("-inf")), "-inf")
|
||||
assert.eq(str(float("+InFiniTy")), "+inf")
|
||||
assert.eq(str(float("-iNFiniTy")), "-inf")
|
||||
assert.fails(lambda: float("one point two"), "invalid float literal: one point two")
|
||||
assert.fails(lambda: float("1.2.3"), "invalid float literal: 1.2.3")
|
||||
assert.fails(lambda: float(123 << 500 << 500 << 50), "int too large to convert to float")
|
||||
assert.fails(lambda: float(-123 << 500 << 500 << 50), "int too large to convert to float")
|
||||
assert.fails(lambda: float(str(-123 << 500 << 500 << 50)), "floating-point number too large")
|
||||
|
||||
# -- implicit float(int) conversions --
|
||||
assert.fails(lambda: (1<<500<<500<<500) + 0.0, "int too large to convert to float")
|
||||
assert.fails(lambda: 0.0 + (1<<500<<500<<500), "int too large to convert to float")
|
||||
assert.fails(lambda: (1<<500<<500<<500) - 0.0, "int too large to convert to float")
|
||||
assert.fails(lambda: 0.0 - (1<<500<<500<<500), "int too large to convert to float")
|
||||
assert.fails(lambda: (1<<500<<500<<500) * 1.0, "int too large to convert to float")
|
||||
assert.fails(lambda: 1.0 * (1<<500<<500<<500), "int too large to convert to float")
|
||||
assert.fails(lambda: (1<<500<<500<<500) / 1.0, "int too large to convert to float")
|
||||
assert.fails(lambda: 1.0 / (1<<500<<500<<500), "int too large to convert to float")
|
||||
assert.fails(lambda: (1<<500<<500<<500) // 1.0, "int too large to convert to float")
|
||||
assert.fails(lambda: 1.0 // (1<<500<<500<<500), "int too large to convert to float")
|
||||
assert.fails(lambda: (1<<500<<500<<500) % 1.0, "int too large to convert to float")
|
||||
assert.fails(lambda: 1.0 % (1<<500<<500<<500), "int too large to convert to float")
|
||||
|
||||
|
||||
# -- int function --
|
||||
assert.eq(int(0.0), 0)
|
||||
assert.eq(int(1.0), 1)
|
||||
assert.eq(int(1.1), 1)
|
||||
assert.eq(int(0.9), 0)
|
||||
assert.eq(int(-1.1), -1.0)
|
||||
assert.eq(int(-1.0), -1.0)
|
||||
assert.eq(int(-0.9), 0.0)
|
||||
assert.eq(int(1.23e+32), 123000000000000004979083645550592)
|
||||
assert.eq(int(-1.23e-32), 0)
|
||||
assert.eq(int(1.23e-32), 0)
|
||||
assert.fails(lambda: int(float("+Inf")), "cannot convert float infinity to integer")
|
||||
assert.fails(lambda: int(float("-Inf")), "cannot convert float infinity to integer")
|
||||
assert.fails(lambda: int(float("NaN")), "cannot convert float NaN to integer")
|
||||
|
||||
|
||||
# hash
|
||||
# Check that equal float and int values have the same internal hash.
|
||||
def checkhash():
|
||||
for a in [1.23e100, 1.23e10, 1.23e1, 1.23,
|
||||
1, 4294967295, 8589934591, 9223372036854775807]:
|
||||
for b in [a, -a, 1/a, -1/a]:
|
||||
f = float(b)
|
||||
i = int(b)
|
||||
if f == i:
|
||||
fh = {f: None}
|
||||
ih = {i: None}
|
||||
if fh != ih:
|
||||
assert.true(False, "{%v: None} != {%v: None}: hashes vary" % fh, ih)
|
||||
checkhash()
|
||||
|
||||
# string formatting
|
||||
|
||||
# %d
|
||||
assert.eq("%d" % 0, "0")
|
||||
assert.eq("%d" % 0.0, "0")
|
||||
assert.eq("%d" % 123, "123")
|
||||
assert.eq("%d" % 123.0, "123")
|
||||
assert.eq("%d" % 1.23e45, "1229999999999999973814869011019624571608236032")
|
||||
# (see below for '%d' % NaN/Inf)
|
||||
assert.eq("%d" % negzero, "0")
|
||||
assert.fails(lambda: "%d" % float("NaN"), "cannot convert float NaN to integer")
|
||||
assert.fails(lambda: "%d" % float("+Inf"), "cannot convert float infinity to integer")
|
||||
assert.fails(lambda: "%d" % float("-Inf"), "cannot convert float infinity to integer")
|
||||
|
||||
# %e
|
||||
assert.eq("%e" % 0, "0.000000e+00")
|
||||
assert.eq("%e" % 0.0, "0.000000e+00")
|
||||
assert.eq("%e" % 123, "1.230000e+02")
|
||||
assert.eq("%e" % 123.0, "1.230000e+02")
|
||||
assert.eq("%e" % 1.23e45, "1.230000e+45")
|
||||
assert.eq("%e" % -1.23e-45, "-1.230000e-45")
|
||||
assert.eq("%e" % nan, "nan")
|
||||
assert.eq("%e" % inf, "+inf")
|
||||
assert.eq("%e" % neginf, "-inf")
|
||||
assert.eq("%e" % negzero, "-0.000000e+00")
|
||||
assert.fails(lambda: "%e" % "123", "requires float, not str")
|
||||
# %f
|
||||
assert.eq("%f" % 0, "0.000000")
|
||||
assert.eq("%f" % 0.0, "0.000000")
|
||||
assert.eq("%f" % 123, "123.000000")
|
||||
assert.eq("%f" % 123.0, "123.000000")
|
||||
# Note: Starlark/Java emits 1230000000000000000000000000000000000000000000.000000. Why?
|
||||
assert.eq("%f" % 1.23e45, "1229999999999999973814869011019624571608236032.000000")
|
||||
assert.eq("%f" % -1.23e-45, "-0.000000")
|
||||
assert.eq("%f" % nan, "nan")
|
||||
assert.eq("%f" % inf, "+inf")
|
||||
assert.eq("%f" % neginf, "-inf")
|
||||
assert.eq("%f" % negzero, "-0.000000")
|
||||
assert.fails(lambda: "%f" % "123", "requires float, not str")
|
||||
# %g
|
||||
assert.eq("%g" % 0, "0.0")
|
||||
assert.eq("%g" % 0.0, "0.0")
|
||||
assert.eq("%g" % 123, "123.0")
|
||||
assert.eq("%g" % 123.0, "123.0")
|
||||
assert.eq("%g" % 1.110, "1.11")
|
||||
assert.eq("%g" % 1e5, "100000.0")
|
||||
assert.eq("%g" % 1e6, "1e+06") # Note: threshold of scientific notation is 1e17 in Starlark/Java
|
||||
assert.eq("%g" % 1.23e45, "1.23e+45")
|
||||
assert.eq("%g" % -1.23e-45, "-1.23e-45")
|
||||
assert.eq("%g" % nan, "nan")
|
||||
assert.eq("%g" % inf, "+inf")
|
||||
assert.eq("%g" % neginf, "-inf")
|
||||
assert.eq("%g" % negzero, "-0.0")
|
||||
# str uses %g
|
||||
assert.eq(str(0.0), "0.0")
|
||||
assert.eq(str(123.0), "123.0")
|
||||
assert.eq(str(1.23e45), "1.23e+45")
|
||||
assert.eq(str(-1.23e-45), "-1.23e-45")
|
||||
assert.eq(str(nan), "nan")
|
||||
assert.eq(str(inf), "+inf")
|
||||
assert.eq(str(neginf), "-inf")
|
||||
assert.eq(str(negzero), "-0.0")
|
||||
assert.fails(lambda: "%g" % "123", "requires float, not str")
|
||||
|
||||
i0 = 1
|
||||
f0 = 1.0
|
||||
assert.eq(type(i0), "int")
|
||||
assert.eq(type(f0), "float")
|
||||
|
||||
ops = {
|
||||
'+': lambda x, y: x + y,
|
||||
'-': lambda x, y: x - y,
|
||||
'*': lambda x, y: x * y,
|
||||
'/': lambda x, y: x / y,
|
||||
'//': lambda x, y: x // y,
|
||||
'%': lambda x, y: x % y,
|
||||
}
|
||||
|
||||
# Check that if either argument is a float, so too is the result.
|
||||
def checktypes():
|
||||
want = set("""
|
||||
int + int = int
|
||||
int + float = float
|
||||
float + int = float
|
||||
float + float = float
|
||||
int - int = int
|
||||
int - float = float
|
||||
float - int = float
|
||||
float - float = float
|
||||
int * int = int
|
||||
int * float = float
|
||||
float * int = float
|
||||
float * float = float
|
||||
int / int = float
|
||||
int / float = float
|
||||
float / int = float
|
||||
float / float = float
|
||||
int // int = int
|
||||
int // float = float
|
||||
float // int = float
|
||||
float // float = float
|
||||
int % int = int
|
||||
int % float = float
|
||||
float % int = float
|
||||
float % float = float
|
||||
"""[1:].splitlines())
|
||||
for opname in ("+", "-", "*", "/", "%"):
|
||||
for x in [i0, f0]:
|
||||
for y in [i0, f0]:
|
||||
op = ops[opname]
|
||||
got = "%s %s %s = %s" % (type(x), opname, type(y), type(op(x, y)))
|
||||
assert.contains(want, got)
|
||||
checktypes()
|
||||
Vendored
+329
@@ -0,0 +1,329 @@
|
||||
# Tests of Starlark 'function'
|
||||
# option:set
|
||||
|
||||
# TODO(adonovan):
|
||||
# - add some introspection functions for looking at function values
|
||||
# and test that functions have correct position, free vars, names of locals, etc.
|
||||
# - move the hard-coded tests of parameter passing from eval_test.go to here.
|
||||
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
# Test lexical scope and closures:
|
||||
def outer(x):
|
||||
def inner(y):
|
||||
return x + x + y # multiple occurrences of x should create only 1 freevar
|
||||
return inner
|
||||
|
||||
z = outer(3)
|
||||
assert.eq(z(5), 11)
|
||||
assert.eq(z(7), 13)
|
||||
z2 = outer(4)
|
||||
assert.eq(z2(5), 13)
|
||||
assert.eq(z2(7), 15)
|
||||
assert.eq(z(5), 11)
|
||||
assert.eq(z(7), 13)
|
||||
|
||||
# Function name
|
||||
assert.eq(str(outer), '<function outer>')
|
||||
assert.eq(str(z), '<function inner>')
|
||||
assert.eq(str(str), '<built-in function str>')
|
||||
assert.eq(str("".startswith), '<built-in method startswith of string value>')
|
||||
|
||||
# Stateful closure
|
||||
def squares():
|
||||
x = [0]
|
||||
def f():
|
||||
x[0] += 1
|
||||
return x[0] * x[0]
|
||||
return f
|
||||
|
||||
sq = squares()
|
||||
assert.eq(sq(), 1)
|
||||
assert.eq(sq(), 4)
|
||||
assert.eq(sq(), 9)
|
||||
assert.eq(sq(), 16)
|
||||
|
||||
# Freezing a closure
|
||||
sq2 = freeze(sq)
|
||||
assert.fails(sq2, "frozen list")
|
||||
|
||||
# recursion detection, simple
|
||||
def fib(x):
|
||||
if x < 2:
|
||||
return x
|
||||
return fib(x-2) + fib(x-1)
|
||||
assert.fails(lambda: fib(10), "function fib called recursively")
|
||||
|
||||
# recursion detection, advanced
|
||||
#
|
||||
# A simplistic recursion check that looks for repeated calls to the
|
||||
# same function value will not detect recursion using the Y
|
||||
# combinator, which creates a new closure at each step of the
|
||||
# recursion. To truly prohibit recursion, the dynamic check must look
|
||||
# for repeated calls of the same syntactic function body.
|
||||
Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
|
||||
fibgen = lambda fib: lambda x: (x if x<2 else fib(x-1)+fib(x-2))
|
||||
fib2 = Y(fibgen)
|
||||
assert.fails(lambda: [fib2(x) for x in range(10)], "function lambda called recursively")
|
||||
|
||||
# However, this stricter check outlaws many useful programs
|
||||
# that are still bounded, and creates a hazard because
|
||||
# helper functions such as map below cannot be used to
|
||||
# call functions that themselves use map:
|
||||
def map(f, seq): return [f(x) for x in seq]
|
||||
def double(x): return x+x
|
||||
assert.eq(map(double, [1, 2, 3]), [2, 4, 6])
|
||||
assert.eq(map(double, ["a", "b", "c"]), ["aa", "bb", "cc"])
|
||||
def mapdouble(x): return map(double, x)
|
||||
assert.fails(lambda: map(mapdouble, ([1, 2, 3], ["a", "b", "c"])),
|
||||
'function map called recursively')
|
||||
# With the -recursion option it would yield [[2, 4, 6], ["aa", "bb", "cc"]].
|
||||
|
||||
# call of function not through its name
|
||||
# (regression test for parsing suffixes of primary expressions)
|
||||
hf = hasfields()
|
||||
hf.x = [len]
|
||||
assert.eq(hf.x[0]("abc"), 3)
|
||||
def f():
|
||||
return lambda: 1
|
||||
assert.eq(f()(), 1)
|
||||
assert.eq(["abc"][0][0].upper(), "A")
|
||||
|
||||
# functions may be recursively defined,
|
||||
# so long as they don't dynamically recur.
|
||||
calls = []
|
||||
def yin(x):
|
||||
calls.append("yin")
|
||||
if x:
|
||||
yang(False)
|
||||
|
||||
def yang(x):
|
||||
calls.append("yang")
|
||||
if x:
|
||||
yin(False)
|
||||
|
||||
yin(True)
|
||||
assert.eq(calls, ["yin", "yang"])
|
||||
|
||||
calls.clear()
|
||||
yang(True)
|
||||
assert.eq(calls, ["yang", "yin"])
|
||||
|
||||
|
||||
# builtin_function_or_method use identity equivalence.
|
||||
closures = set(["".count for _ in range(10)])
|
||||
assert.eq(len(closures), 10)
|
||||
|
||||
---
|
||||
# Default values of function parameters are mutable.
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
def f(x=[0]):
|
||||
return x
|
||||
|
||||
assert.eq(f(), [0])
|
||||
|
||||
f().append(1)
|
||||
assert.eq(f(), [0, 1])
|
||||
|
||||
# Freezing a function value freezes its parameter defaults.
|
||||
freeze(f)
|
||||
assert.fails(lambda: f().append(2), "cannot append to frozen list")
|
||||
|
||||
---
|
||||
# This is a well known corner case of parsing in Python.
|
||||
load("assert.star", "assert")
|
||||
|
||||
f = lambda x: 1 if x else 0
|
||||
assert.eq(f(True), 1)
|
||||
assert.eq(f(False), 0)
|
||||
|
||||
x = True
|
||||
f2 = (lambda x: 1) if x else 0
|
||||
assert.eq(f2(123), 1)
|
||||
|
||||
tf = lambda: True, lambda: False
|
||||
assert.true(tf[0]())
|
||||
assert.true(not tf[1]())
|
||||
|
||||
---
|
||||
# Missing parameters are correctly reported
|
||||
# in functions of more than 64 parameters.
|
||||
# (This tests a corner case of the implementation:
|
||||
# we avoid a map allocation for <64 parameters)
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def f(a, b, c, d, e, f, g, h,
|
||||
i, j, k, l, m, n, o, p,
|
||||
q, r, s, t, u, v, w, x,
|
||||
y, z, A, B, C, D, E, F,
|
||||
G, H, I, J, K, L, M, N,
|
||||
O, P, Q, R, S, T, U, V,
|
||||
W, X, Y, Z, aa, bb, cc, dd,
|
||||
ee, ff, gg, hh, ii, jj, kk, ll,
|
||||
mm):
|
||||
pass
|
||||
|
||||
assert.fails(lambda: f(
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62, 63, 64), "missing 1 argument \\(mm\\)")
|
||||
|
||||
assert.fails(lambda: f(
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62, 63, 64, 65,
|
||||
mm = 100), 'multiple values for parameter "mm"')
|
||||
|
||||
---
|
||||
# Regression test for github.com/google/starlark-go/issues/21,
|
||||
# which concerns dynamic checks.
|
||||
# Related: https://github.com/bazelbuild/starlark/issues/21,
|
||||
# which concerns static checks.
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def f(*args, **kwargs):
|
||||
return args, kwargs
|
||||
|
||||
assert.eq(f(x=1, y=2), ((), {"x": 1, "y": 2}))
|
||||
assert.fails(lambda: f(x=1, **dict(x=2)), 'multiple values for parameter "x"')
|
||||
|
||||
def g(x, y):
|
||||
return x, y
|
||||
|
||||
assert.eq(g(1, y=2), (1, 2))
|
||||
assert.fails(lambda: g(1, y=2, **{'y': 3}), 'multiple values for parameter "y"')
|
||||
|
||||
---
|
||||
# Regression test for a bug in CALL_VAR_KW.
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def f(a, b, x, y):
|
||||
return a+b+x+y
|
||||
|
||||
assert.eq(f(*("a", "b"), **dict(y="y", x="x")) + ".", 'abxy.')
|
||||
---
|
||||
# Order of evaluation of function arguments.
|
||||
# Regression test for github.com/google/skylark/issues/135.
|
||||
load("assert.star", "assert")
|
||||
|
||||
r = []
|
||||
|
||||
def id(x):
|
||||
r.append(x)
|
||||
return x
|
||||
|
||||
def f(*args, **kwargs):
|
||||
return (args, kwargs)
|
||||
|
||||
y = f(id(1), id(2), x=id(3), *[id(4)], **dict(z=id(5)))
|
||||
assert.eq(y, ((1, 2, 4), dict(x=3, z=5)))
|
||||
|
||||
# This matches Python2 and Starlark-in-Java, but not Python3 [1 2 4 3 6].
|
||||
# *args and *kwargs are evaluated last.
|
||||
# (Python[23] also allows keyword arguments after *args.)
|
||||
# See github.com/bazelbuild/starlark#13 for spec change.
|
||||
assert.eq(r, [1, 2, 3, 4, 5])
|
||||
|
||||
---
|
||||
# option:recursion
|
||||
# See github.com/bazelbuild/starlark#170
|
||||
load("assert.star", "assert")
|
||||
|
||||
def a():
|
||||
list = []
|
||||
def b(n):
|
||||
list.append(n)
|
||||
if n > 0:
|
||||
b(n - 1) # recursive reference to b
|
||||
|
||||
b(3)
|
||||
return list
|
||||
|
||||
assert.eq(a(), [3, 2, 1, 0])
|
||||
|
||||
def c():
|
||||
list = []
|
||||
x = 1
|
||||
def d():
|
||||
list.append(x) # this use of x observes both assignments
|
||||
d()
|
||||
x = 2
|
||||
d()
|
||||
return list
|
||||
|
||||
assert.eq(c(), [1, 2])
|
||||
|
||||
def e():
|
||||
def f():
|
||||
return x # forward reference ok: x is a closure cell
|
||||
x = 1
|
||||
return f()
|
||||
|
||||
assert.eq(e(), 1)
|
||||
|
||||
---
|
||||
load("assert.star", "assert")
|
||||
|
||||
def e():
|
||||
x = 1
|
||||
def f():
|
||||
print(x) # this reference to x fails
|
||||
x = 3 # because this assignment makes x local to f
|
||||
f()
|
||||
|
||||
assert.fails(e, "local variable x referenced before assignment")
|
||||
|
||||
def f():
|
||||
def inner():
|
||||
return x
|
||||
if False:
|
||||
x = 0
|
||||
return x # fails (x is an uninitialized cell of this function)
|
||||
|
||||
assert.fails(f, "local variable x referenced before assignment")
|
||||
|
||||
def g():
|
||||
def inner():
|
||||
return x # fails (x is an uninitialized cell of the enclosing function)
|
||||
if False:
|
||||
x = 0
|
||||
return inner()
|
||||
|
||||
assert.fails(g, "local variable x referenced before assignment")
|
||||
|
||||
---
|
||||
# A trailing comma is allowed in any function definition or call.
|
||||
# This reduces the need to edit neighboring lines when editing defs
|
||||
# or calls splayed across multiple lines.
|
||||
|
||||
def a(x,): pass
|
||||
def b(x, y=None, ): pass
|
||||
def c(x, y=None, *args, ): pass
|
||||
def d(x, y=None, *args, z=None, ): pass
|
||||
def e(x, y=None, *args, z=None, **kwargs, ): pass
|
||||
|
||||
a(1,)
|
||||
b(1, y=2, )
|
||||
#c(1, *[], )
|
||||
#d(1, *[], z=None, )
|
||||
#e(1, *[], z=None, *{}, )
|
||||
|
||||
---
|
||||
# Unpack provides spell check for argument names.
|
||||
load("assert.star", "assert")
|
||||
|
||||
assert.fails(lambda: min([], keg=1), ".+did you mean key\\?")
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
# Functions used in starlark_test.TestParamDefault().
|
||||
|
||||
def all_required(a, b, c): pass
|
||||
def all_opt(a="a", b=None, c=""): pass
|
||||
def mix_required_opt(a, b, c="c", d="d"): pass
|
||||
def with_varargs(a, b="b", *args): pass
|
||||
def with_varargs_kwonly(a, b="b", *args, c="c", d): pass
|
||||
def with_kwonly(a, b="b", *, c="c", d): pass
|
||||
def with_kwargs(a, b="b", c="c", **kwargs): pass
|
||||
def with_varargs_kwonly_kwargs(a, b="b", *args, c="c", d, e="e", **kwargs): pass
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
# Tests of Starlark 'int'
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# basic arithmetic
|
||||
assert.eq(0 - 1, -1)
|
||||
assert.eq(0 + 1, +1)
|
||||
assert.eq(1 + 1, 2)
|
||||
assert.eq(5 + 7, 12)
|
||||
assert.eq(5 * 7, 35)
|
||||
assert.eq(5 - 7, -2)
|
||||
|
||||
# int boundaries
|
||||
maxint64 = (1 << 63) - 1
|
||||
minint64 = -1 << 63
|
||||
maxint32 = (1 << 31) - 1
|
||||
minint32 = -1 << 31
|
||||
assert.eq(maxint64, 9223372036854775807)
|
||||
assert.eq(minint64, -9223372036854775808)
|
||||
assert.eq(maxint32, 2147483647)
|
||||
assert.eq(minint32, -2147483648)
|
||||
|
||||
# truth
|
||||
def truth():
|
||||
assert.true(not 0)
|
||||
for m in [1, maxint32]: # Test small/big ranges
|
||||
assert.true(123 * m)
|
||||
assert.true(-1 * m)
|
||||
|
||||
truth()
|
||||
|
||||
# floored division
|
||||
# (For real division, see float.star.)
|
||||
def division():
|
||||
for m in [1, maxint32]: # Test small/big ranges
|
||||
assert.eq((100 * m) // (7 * m), 14)
|
||||
assert.eq((100 * m) // (-7 * m), -15)
|
||||
assert.eq((-100 * m) // (7 * m), -15) # NB: different from Go/Java
|
||||
assert.eq((-100 * m) // (-7 * m), 14) # NB: different from Go/Java
|
||||
assert.eq((98 * m) // (7 * m), 14)
|
||||
assert.eq((98 * m) // (-7 * m), -14)
|
||||
assert.eq((-98 * m) // (7 * m), -14)
|
||||
assert.eq((-98 * m) // (-7 * m), 14)
|
||||
|
||||
division()
|
||||
|
||||
# remainder
|
||||
def remainder():
|
||||
for m in [1, maxint32]: # Test small/big ranges
|
||||
assert.eq((100 * m) % (7 * m), 2 * m)
|
||||
assert.eq((100 * m) % (-7 * m), -5 * m) # NB: different from Go/Java
|
||||
assert.eq((-100 * m) % (7 * m), 5 * m) # NB: different from Go/Java
|
||||
assert.eq((-100 * m) % (-7 * m), -2 * m)
|
||||
assert.eq((98 * m) % (7 * m), 0)
|
||||
assert.eq((98 * m) % (-7 * m), 0)
|
||||
assert.eq((-98 * m) % (7 * m), 0)
|
||||
assert.eq((-98 * m) % (-7 * m), 0)
|
||||
|
||||
remainder()
|
||||
|
||||
# compound assignment
|
||||
def compound():
|
||||
x = 1
|
||||
x += 1
|
||||
assert.eq(x, 2)
|
||||
x -= 3
|
||||
assert.eq(x, -1)
|
||||
x *= 39
|
||||
assert.eq(x, -39)
|
||||
x //= 4
|
||||
assert.eq(x, -10)
|
||||
x /= -2
|
||||
assert.eq(x, 5)
|
||||
x %= 3
|
||||
assert.eq(x, 2)
|
||||
|
||||
x = 2
|
||||
x &= 1
|
||||
assert.eq(x, 0)
|
||||
x |= 2
|
||||
assert.eq(x, 2)
|
||||
x ^= 3
|
||||
assert.eq(x, 1)
|
||||
x <<= 2
|
||||
assert.eq(x, 4)
|
||||
x >>= 2
|
||||
assert.eq(x, 1)
|
||||
|
||||
compound()
|
||||
|
||||
# int conversion
|
||||
# See float.star for float-to-int conversions.
|
||||
# We follow Python 3 here, but I can't see the method in its madness.
|
||||
# int from bool/int/float
|
||||
assert.fails(int, "missing argument") # int()
|
||||
assert.eq(int(False), 0)
|
||||
assert.eq(int(True), 1)
|
||||
assert.eq(int(3), 3)
|
||||
assert.eq(int(3.1), 3)
|
||||
assert.fails(lambda: int(3, base = 10), "non-string with explicit base")
|
||||
assert.fails(lambda: int(True, 10), "non-string with explicit base")
|
||||
|
||||
# int from string, base implicitly 10
|
||||
assert.eq(int("100000000000000000000"), 10000000000 * 10000000000)
|
||||
assert.eq(int("-100000000000000000000"), -10000000000 * 10000000000)
|
||||
assert.eq(int("123"), 123)
|
||||
assert.eq(int("-123"), -123)
|
||||
assert.eq(int("0123"), 123) # not octal
|
||||
assert.eq(int("-0123"), -123)
|
||||
assert.fails(lambda: int("0x12"), "invalid literal with base 10")
|
||||
assert.fails(lambda: int("-0x12"), "invalid literal with base 10")
|
||||
assert.fails(lambda: int("0o123"), "invalid literal.*base 10")
|
||||
assert.fails(lambda: int("-0o123"), "invalid literal.*base 10")
|
||||
|
||||
# int from string, explicit base
|
||||
assert.eq(int("0"), 0)
|
||||
assert.eq(int("00"), 0)
|
||||
assert.eq(int("0", base = 10), 0)
|
||||
assert.eq(int("00", base = 10), 0)
|
||||
assert.eq(int("0", base = 8), 0)
|
||||
assert.eq(int("00", base = 8), 0)
|
||||
assert.eq(int("-0"), 0)
|
||||
assert.eq(int("-00"), 0)
|
||||
assert.eq(int("-0", base = 10), 0)
|
||||
assert.eq(int("-00", base = 10), 0)
|
||||
assert.eq(int("-0", base = 8), 0)
|
||||
assert.eq(int("-00", base = 8), 0)
|
||||
assert.eq(int("+0"), 0)
|
||||
assert.eq(int("+00"), 0)
|
||||
assert.eq(int("+0", base = 10), 0)
|
||||
assert.eq(int("+00", base = 10), 0)
|
||||
assert.eq(int("+0", base = 8), 0)
|
||||
assert.eq(int("+00", base = 8), 0)
|
||||
assert.eq(int("11", base = 9), 10)
|
||||
assert.eq(int("-11", base = 9), -10)
|
||||
assert.eq(int("10011", base = 2), 19)
|
||||
assert.eq(int("-10011", base = 2), -19)
|
||||
assert.eq(int("123", 8), 83)
|
||||
assert.eq(int("-123", 8), -83)
|
||||
assert.eq(int("0123", 8), 83) # redundant zeros permitted
|
||||
assert.eq(int("-0123", 8), -83)
|
||||
assert.eq(int("00123", 8), 83)
|
||||
assert.eq(int("-00123", 8), -83)
|
||||
assert.eq(int("0o123", 8), 83)
|
||||
assert.eq(int("-0o123", 8), -83)
|
||||
assert.eq(int("123", 7), 66) # 1*7*7 + 2*7 + 3
|
||||
assert.eq(int("-123", 7), -66)
|
||||
assert.eq(int("12", 16), 18)
|
||||
assert.eq(int("-12", 16), -18)
|
||||
assert.eq(int("0x12", 16), 18)
|
||||
assert.eq(int("-0x12", 16), -18)
|
||||
assert.eq(0x1000000000000001 * 0x1000000000000001, 0x1000000000000002000000000000001)
|
||||
assert.eq(int("1010", 2), 10)
|
||||
assert.eq(int("111111101", 2), 509)
|
||||
assert.eq(int("0b0101", 0), 5)
|
||||
assert.eq(int("0b0101", 2), 5) # prefix is redundant with explicit base
|
||||
assert.eq(int("0b00000", 0), 0)
|
||||
assert.eq(1111111111111111 * 1111111111111111, 1234567901234567654320987654321)
|
||||
assert.fails(lambda: int("0x123", 8), "invalid literal.*base 8")
|
||||
assert.fails(lambda: int("-0x123", 8), "invalid literal.*base 8")
|
||||
assert.fails(lambda: int("0o123", 16), "invalid literal.*base 16")
|
||||
assert.fails(lambda: int("-0o123", 16), "invalid literal.*base 16")
|
||||
assert.fails(lambda: int("0x110", 2), "invalid literal.*base 2")
|
||||
|
||||
# Base prefix is honored only if base=0, or if the prefix matches the explicit base.
|
||||
# See https://github.com/google/starlark-go/issues/337
|
||||
assert.fails(lambda: int("0b0"), "invalid literal.*base 10")
|
||||
assert.eq(int("0b0", 0), 0)
|
||||
assert.eq(int("0b0", 2), 0)
|
||||
assert.eq(int("0b0", 16), 0xb0)
|
||||
assert.eq(int("0x0b0", 16), 0xb0)
|
||||
assert.eq(int("0x0b0", 0), 0xb0)
|
||||
assert.eq(int("0x0b0101", 16), 0x0b0101)
|
||||
|
||||
# int from string, auto detect base
|
||||
assert.eq(int("123", 0), 123)
|
||||
assert.eq(int("+123", 0), +123)
|
||||
assert.eq(int("-123", 0), -123)
|
||||
assert.eq(int("0x12", 0), 18)
|
||||
assert.eq(int("+0x12", 0), +18)
|
||||
assert.eq(int("-0x12", 0), -18)
|
||||
assert.eq(int("0o123", 0), 83)
|
||||
assert.eq(int("+0o123", 0), +83)
|
||||
assert.eq(int("-0o123", 0), -83)
|
||||
assert.fails(lambda: int("0123", 0), "invalid literal.*base 0") # valid in Python 2.7
|
||||
assert.fails(lambda: int("-0123", 0), "invalid literal.*base 0")
|
||||
|
||||
# github.com/google/starlark-go/issues/108
|
||||
assert.fails(lambda: int("0Oxa", 8), "invalid literal with base 8: 0Oxa")
|
||||
|
||||
# follow-on bugs to issue 108
|
||||
assert.fails(lambda: int("--4"), "invalid literal with base 10: --4")
|
||||
assert.fails(lambda: int("++4"), "invalid literal with base 10: \\+\\+4")
|
||||
assert.fails(lambda: int("+-4"), "invalid literal with base 10: \\+-4")
|
||||
assert.fails(lambda: int("0x-4", 16), "invalid literal with base 16: 0x-4")
|
||||
|
||||
# bitwise union (int|int), intersection (int&int), XOR (int^int), unary not (~int),
|
||||
# left shift (int<<int), and right shift (int>>int).
|
||||
# TODO(adonovan): this is not yet in the Starlark spec,
|
||||
# but there is consensus that it should be.
|
||||
assert.eq(1 | 2, 3)
|
||||
assert.eq(3 | 6, 7)
|
||||
assert.eq((1 | 2) & (2 | 4), 2)
|
||||
assert.eq(1 ^ 2, 3)
|
||||
assert.eq(2 ^ 2, 0)
|
||||
assert.eq(1 | 0 ^ 1, 1) # check | and ^ operators precedence
|
||||
assert.eq(~1, -2)
|
||||
assert.eq(~(-2), 1)
|
||||
assert.eq(~0, -1)
|
||||
assert.eq(1 << 2, 4)
|
||||
assert.eq(2 >> 1, 1)
|
||||
assert.fails(lambda: 2 << -1, "negative shift count")
|
||||
assert.fails(lambda: 1 << 512, "shift count too large")
|
||||
|
||||
# comparisons
|
||||
# TODO(adonovan): test: < > == != etc
|
||||
def comparisons():
|
||||
for m in [1, maxint32 / 2, maxint32]: # Test small/big ranges
|
||||
assert.lt(-2 * m, -1 * m)
|
||||
assert.lt(-1 * m, 0 * m)
|
||||
assert.lt(0 * m, 1 * m)
|
||||
assert.lt(1 * m, 2 * m)
|
||||
assert.true(2 * m >= 2 * m)
|
||||
assert.true(2 * m > 1 * m)
|
||||
assert.true(1 * m >= 1 * m)
|
||||
assert.true(1 * m > 0 * m)
|
||||
assert.true(0 * m >= 0 * m)
|
||||
assert.true(0 * m > -1 * m)
|
||||
assert.true(-1 * m >= -1 * m)
|
||||
assert.true(-1 * m > -2 * m)
|
||||
|
||||
comparisons()
|
||||
|
||||
# precision
|
||||
assert.eq(str(maxint64), "9223372036854775807")
|
||||
assert.eq(str(maxint64 + 1), "9223372036854775808")
|
||||
assert.eq(str(minint64), "-9223372036854775808")
|
||||
assert.eq(str(minint64 - 1), "-9223372036854775809")
|
||||
assert.eq(str(minint64 * minint64), "85070591730234615865843651857942052864")
|
||||
assert.eq(str(maxint32 + 1), "2147483648")
|
||||
assert.eq(str(minint32 - 1), "-2147483649")
|
||||
assert.eq(str(minint32 * minint32), "4611686018427387904")
|
||||
assert.eq(str(minint32 | maxint32), "-1")
|
||||
assert.eq(str(minint32 & minint32), "-2147483648")
|
||||
assert.eq(str(minint32 ^ maxint32), "-1")
|
||||
assert.eq(str(minint32 // -1), "2147483648")
|
||||
|
||||
# string formatting
|
||||
assert.eq("%o %x %d" % (0o755, 0xDEADBEEF, 42), "755 deadbeef 42")
|
||||
nums = [-95, -1, 0, +1, +95]
|
||||
assert.eq(" ".join(["%o" % x for x in nums]), "-137 -1 0 1 137")
|
||||
assert.eq(" ".join(["%d" % x for x in nums]), "-95 -1 0 1 95")
|
||||
assert.eq(" ".join(["%i" % x for x in nums]), "-95 -1 0 1 95")
|
||||
assert.eq(" ".join(["%x" % x for x in nums]), "-5f -1 0 1 5f")
|
||||
assert.eq(" ".join(["%X" % x for x in nums]), "-5F -1 0 1 5F")
|
||||
assert.eq("%o %x %d" % (123, 123, 123), "173 7b 123")
|
||||
assert.eq("%o %x %d" % (123.1, 123.1, 123.1), "173 7b 123") # non-int operands are acceptable
|
||||
assert.fails(lambda: "%d" % True, "cannot convert bool to int")
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
# Tests of json module.
|
||||
|
||||
load("assert.star", "assert")
|
||||
load("json.star", "json")
|
||||
|
||||
assert.eq(dir(json), ["decode", "encode", "indent"])
|
||||
|
||||
# Some of these cases were inspired by github.com/nst/JSONTestSuite.
|
||||
|
||||
## json.encode
|
||||
|
||||
assert.eq(json.encode(None), "null")
|
||||
assert.eq(json.encode(True), "true")
|
||||
assert.eq(json.encode(False), "false")
|
||||
assert.eq(json.encode(-123), "-123")
|
||||
assert.eq(json.encode(12345*12345*12345*12345*12345*12345), "3539537889086624823140625")
|
||||
assert.eq(json.encode(float(12345*12345*12345*12345*12345*12345)), "3.539537889086625e+24")
|
||||
assert.eq(json.encode(12.345e67), "1.2345e+68")
|
||||
assert.eq(json.encode("hello"), '"hello"')
|
||||
assert.eq(json.encode([1, 2, 3]), "[1,2,3]")
|
||||
assert.eq(json.encode((1, 2, 3)), "[1,2,3]")
|
||||
assert.eq(json.encode(range(3)), "[0,1,2]") # a built-in iterable
|
||||
assert.eq(json.encode(dict(x = 1, y = "two")), '{"x":1,"y":"two"}')
|
||||
assert.eq(json.encode(dict(y = "two", x = 1)), '{"x":1,"y":"two"}') # key, not insertion, order
|
||||
assert.eq(json.encode(struct(x = 1, y = "two")), '{"x":1,"y":"two"}') # a user-defined HasAttrs
|
||||
assert.eq(json.encode("😹"[:1]), '"\\ufffd"') # invalid UTF-8 -> replacement char
|
||||
|
||||
def encode_error(expr, error):
|
||||
assert.fails(lambda: json.encode(expr), error)
|
||||
|
||||
encode_error(float("NaN"), "json.encode: cannot encode non-finite float nan")
|
||||
encode_error({1: "two"}, "dict has int key, want string")
|
||||
encode_error(len, "cannot encode builtin_function_or_method as JSON")
|
||||
encode_error(struct(x=[1, {"x": len}]), # nested failure
|
||||
'in field .x: at list index 1: in dict key "x": cannot encode...')
|
||||
encode_error(struct(x=[1, {"x": len}]), # nested failure
|
||||
'in field .x: at list index 1: in dict key "x": cannot encode...')
|
||||
encode_error({1: 2}, 'dict has int key, want string')
|
||||
|
||||
recursive_map = {}
|
||||
recursive_map["r"] = recursive_map
|
||||
encode_error(recursive_map, 'json.encode: in dict key "r": cycle in JSON structure')
|
||||
|
||||
recursive_list = []
|
||||
recursive_list.append(recursive_list)
|
||||
encode_error(recursive_list, 'json.encode: at list index 0: cycle in JSON structure')
|
||||
|
||||
recursive_tuple = (1, 2, [])
|
||||
recursive_tuple[2].append(recursive_tuple)
|
||||
encode_error(recursive_tuple, 'json.encode: at tuple index 2: at list index 0: cycle in JSON structure')
|
||||
|
||||
## json.decode
|
||||
|
||||
assert.eq(json.decode("null"), None)
|
||||
assert.eq(json.decode("true"), True)
|
||||
assert.eq(json.decode("false"), False)
|
||||
assert.eq(json.decode("-123"), -123)
|
||||
assert.eq(json.decode("-0"), -0)
|
||||
assert.eq(json.decode("3539537889086624823140625"), 3539537889086624823140625)
|
||||
assert.eq(json.decode("3539537889086624823140625.0"), float(3539537889086624823140625))
|
||||
assert.eq(json.decode("3.539537889086625e+24"), 3.539537889086625e+24)
|
||||
assert.eq(json.decode("0e+1"), 0)
|
||||
assert.eq(json.decode("-0.0"), -0.0)
|
||||
assert.eq(json.decode(
|
||||
"-0.000000000000000000000000000000000000000000000000000000000000000000000000000001"),
|
||||
-0.000000000000000000000000000000000000000000000000000000000000000000000000000001)
|
||||
assert.eq(json.decode('[]'), [])
|
||||
assert.eq(json.decode('[1]'), [1])
|
||||
assert.eq(json.decode('[1,2,3]'), [1, 2, 3])
|
||||
assert.eq(json.decode('{"one": 1, "two": 2}'), dict(one=1, two=2))
|
||||
assert.eq(json.decode('{"foo\\u0000bar": 42}'), {"foo\x00bar": 42})
|
||||
assert.eq(json.decode('"\\ud83d\\ude39\\ud83d\\udc8d"'), "😹💍")
|
||||
assert.eq(json.decode('"\\u0123"'), 'ģ')
|
||||
assert.eq(json.decode('"\x7f"'), "\x7f")
|
||||
|
||||
def decode_error(expr, error):
|
||||
assert.fails(lambda: json.decode(expr), error)
|
||||
|
||||
decode_error('truefalse',
|
||||
"json.decode: at offset 4, unexpected character 'f' after value")
|
||||
|
||||
decode_error('"abc', "unclosed string literal")
|
||||
decode_error('"ab\\gc"', "invalid character 'g' in string escape code")
|
||||
decode_error("'abc'", "unexpected character '\\\\''")
|
||||
|
||||
decode_error("1.2.3", "invalid number: 1.2.3")
|
||||
decode_error("+1", "unexpected character '\\+'")
|
||||
decode_error("-abc", "invalid number: -")
|
||||
decode_error("-", "invalid number: -")
|
||||
decode_error("-00", "invalid number: -00")
|
||||
decode_error("00", "invalid number: 00")
|
||||
decode_error("--1", "invalid number: --1")
|
||||
decode_error("-+1", "invalid number: -\\+1")
|
||||
decode_error("1e1e1", "invalid number: 1e1e1")
|
||||
decode_error("0123", "invalid number: 0123")
|
||||
decode_error("000.123", "invalid number: 000.123")
|
||||
decode_error("-0123", "invalid number: -0123")
|
||||
decode_error("-000.123", "invalid number: -000.123")
|
||||
decode_error("0x123", "unexpected character 'x' after value")
|
||||
|
||||
decode_error('[1, 2 ', "unexpected end of file")
|
||||
decode_error('[1, 2, ', "unexpected end of file")
|
||||
decode_error('[1, 2, ]', "unexpected character ']'")
|
||||
decode_error('[1, 2, }', "unexpected character '}'")
|
||||
decode_error('[1, 2}', "got '}', want ',' or ']'")
|
||||
|
||||
decode_error('{"one": 1', "unexpected end of file")
|
||||
decode_error('{"one" 1', "after object key, got '1', want ':'")
|
||||
decode_error('{"one": 1 "two": 2', "in object, got '\"', want ',' or '}'")
|
||||
decode_error('{"one": 1,', "unexpected end of file")
|
||||
decode_error('{"one": 1, }', "unexpected character '}'")
|
||||
decode_error('{"one": 1]', "in object, got ']', want ',' or '}'")
|
||||
|
||||
## json.decode with default specified
|
||||
|
||||
assert.eq(json.decode('{"valid": "json"}', default = "default value"), {"valid": "json"})
|
||||
assert.eq(json.decode('{"valid": "json"}', "default value"), {"valid": "json"})
|
||||
assert.eq(json.decode('{"invalid": "json"', default = "default value"), "default value")
|
||||
assert.eq(json.decode('{"invalid": "json"', "default value"), "default value")
|
||||
assert.eq(json.decode('{"invalid": "json"', default = None), None)
|
||||
assert.eq(json.decode('{"invalid": "json"', None), None)
|
||||
|
||||
assert.fails(
|
||||
lambda: json.decode(x = '{"invalid": "json"', default = "default value"),
|
||||
"unexpected keyword argument x"
|
||||
)
|
||||
|
||||
def codec(x):
|
||||
return json.decode(json.encode(x))
|
||||
|
||||
# string round-tripping
|
||||
strings = [
|
||||
"😿", # U+1F63F CRYING_CAT_FACE
|
||||
"🐱👤", # CAT FACE + ZERO WIDTH JOINER + BUST IN SILHOUETTE
|
||||
]
|
||||
assert.eq(codec(strings), strings)
|
||||
|
||||
# codepoints is a string with every 16-bit code point.
|
||||
codepoints = ''.join(['%c' % c for c in range(65536)])
|
||||
assert.eq(codec(codepoints), codepoints)
|
||||
|
||||
# number round-tripping
|
||||
numbers = [
|
||||
0, 1, -1, +1, 1.23e45, -1.23e-45,
|
||||
3539537889086624823140625,
|
||||
float(3539537889086624823140625),
|
||||
]
|
||||
assert.eq(codec(numbers), numbers)
|
||||
|
||||
## json.indent
|
||||
|
||||
s = json.encode(dict(x = 1, y = ["one", "two"]))
|
||||
|
||||
assert.eq(json.indent(s), '''{
|
||||
"x": 1,
|
||||
"y": [
|
||||
"one",
|
||||
"two"
|
||||
]
|
||||
}''')
|
||||
|
||||
assert.eq(json.decode(json.indent(s)), {"x": 1, "y": ["one", "two"]})
|
||||
|
||||
assert.eq(json.indent(s, prefix='¶', indent='–––'), '''{
|
||||
¶–––"x": 1,
|
||||
¶–––"y": [
|
||||
¶––––––"one",
|
||||
¶––––––"two"
|
||||
¶–––]
|
||||
¶}''')
|
||||
|
||||
assert.fails(lambda: json.indent("!@#$%^& this is not json"), 'invalid character')
|
||||
---
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
# Tests of Starlark 'list'
|
||||
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
# literals
|
||||
assert.eq([], [])
|
||||
assert.eq([1], [1])
|
||||
assert.eq([1], [1])
|
||||
assert.eq([1, 2], [1, 2])
|
||||
assert.ne([1, 2, 3], [1, 2, 4])
|
||||
|
||||
# truth
|
||||
assert.true([0])
|
||||
assert.true(not [])
|
||||
|
||||
# indexing, x[i]
|
||||
abc = list("abc".elems())
|
||||
assert.fails(lambda: abc[-4], "list index -4 out of range \\[-3:2]")
|
||||
assert.eq(abc[-3], "a")
|
||||
assert.eq(abc[-2], "b")
|
||||
assert.eq(abc[-1], "c")
|
||||
assert.eq(abc[0], "a")
|
||||
assert.eq(abc[1], "b")
|
||||
assert.eq(abc[2], "c")
|
||||
assert.fails(lambda: abc[3], "list index 3 out of range \\[-3:2]")
|
||||
|
||||
# x[i] = ...
|
||||
x3 = [0, 1, 2]
|
||||
x3[1] = 2
|
||||
x3[2] += 3
|
||||
assert.eq(x3, [0, 2, 5])
|
||||
|
||||
def f2():
|
||||
x3[3] = 4
|
||||
|
||||
assert.fails(f2, "out of range")
|
||||
freeze(x3)
|
||||
|
||||
def f3():
|
||||
x3[0] = 0
|
||||
|
||||
assert.fails(f3, "cannot assign to element of frozen list")
|
||||
assert.fails(x3.clear, "cannot clear frozen list")
|
||||
|
||||
# list + list
|
||||
assert.eq([1, 2, 3] + [3, 4, 5], [1, 2, 3, 3, 4, 5])
|
||||
assert.fails(lambda: [1, 2] + (3, 4), "unknown.*list \\+ tuple")
|
||||
assert.fails(lambda: (1, 2) + [3, 4], "unknown.*tuple \\+ list")
|
||||
|
||||
# list * int, int * list
|
||||
assert.eq(abc * 0, [])
|
||||
assert.eq(abc * -1, [])
|
||||
assert.eq(abc * 1, abc)
|
||||
assert.eq(abc * 3, ["a", "b", "c", "a", "b", "c", "a", "b", "c"])
|
||||
assert.eq(0 * abc, [])
|
||||
assert.eq(-1 * abc, [])
|
||||
assert.eq(1 * abc, abc)
|
||||
assert.eq(3 * abc, ["a", "b", "c", "a", "b", "c", "a", "b", "c"])
|
||||
|
||||
# list comprehensions
|
||||
assert.eq([2 * x for x in [1, 2, 3]], [2, 4, 6])
|
||||
assert.eq([2 * x for x in [1, 2, 3] if x > 1], [4, 6])
|
||||
assert.eq(
|
||||
[(x, y) for x in [1, 2] for y in [3, 4]],
|
||||
[(1, 3), (1, 4), (2, 3), (2, 4)],
|
||||
)
|
||||
assert.eq([(x, y) for x in [1, 2] if x == 2 for y in [3, 4]], [(2, 3), (2, 4)])
|
||||
assert.eq([2 * x for x in (1, 2, 3)], [2, 4, 6])
|
||||
assert.eq([x for x in "abc".elems()], ["a", "b", "c"])
|
||||
assert.eq([x for x in {"a": 1, "b": 2}], ["a", "b"])
|
||||
assert.eq([(y, x) for x, y in {1: 2, 3: 4}.items()], [(2, 1), (4, 3)])
|
||||
|
||||
# corner cases of parsing:
|
||||
assert.eq([x for x in range(12) if x % 2 == 0 if x % 3 == 0], [0, 6])
|
||||
assert.eq([x for x in [1, 2] if lambda: None], [1, 2])
|
||||
assert.eq([x for x in [1, 2] if (lambda: 3 if True else 4)], [1, 2])
|
||||
|
||||
# list function
|
||||
assert.eq(list(), [])
|
||||
assert.eq(list("ab".elems()), ["a", "b"])
|
||||
|
||||
# A list comprehension defines a separate lexical block,
|
||||
# whether at top-level...
|
||||
a = [1, 2]
|
||||
b = [a for a in [3, 4]]
|
||||
assert.eq(a, [1, 2])
|
||||
assert.eq(b, [3, 4])
|
||||
|
||||
# ...or local to a function.
|
||||
def listcompblock():
|
||||
c = [1, 2]
|
||||
d = [c for c in [3, 4]]
|
||||
assert.eq(c, [1, 2])
|
||||
assert.eq(d, [3, 4])
|
||||
|
||||
listcompblock()
|
||||
|
||||
# list.pop
|
||||
x4 = [1, 2, 3, 4, 5]
|
||||
assert.fails(lambda: x4.pop(-6), "index -6 out of range \\[-5:4]")
|
||||
assert.fails(lambda: x4.pop(6), "index 6 out of range \\[-5:4]")
|
||||
assert.eq(x4.pop(), 5)
|
||||
assert.eq(x4, [1, 2, 3, 4])
|
||||
assert.eq(x4.pop(1), 2)
|
||||
assert.eq(x4, [1, 3, 4])
|
||||
assert.eq(x4.pop(0), 1)
|
||||
assert.eq(x4, [3, 4])
|
||||
assert.eq(x4.pop(-2), 3)
|
||||
assert.eq(x4, [4])
|
||||
assert.eq(x4.pop(-1), 4)
|
||||
assert.eq(x4, [])
|
||||
|
||||
# TODO(adonovan): test uses of list as sequence
|
||||
# (for loop, comprehension, library functions).
|
||||
|
||||
# x += y for lists is equivalent to x.extend(y).
|
||||
# y may be a sequence.
|
||||
# TODO: Test that side-effects of 'x' occur only once.
|
||||
def list_extend():
|
||||
a = [1, 2, 3]
|
||||
b = a
|
||||
a = a + [4] # creates a new list
|
||||
assert.eq(a, [1, 2, 3, 4])
|
||||
assert.eq(b, [1, 2, 3]) # b is unchanged
|
||||
|
||||
a = [1, 2, 3]
|
||||
b = a
|
||||
a += [4] # updates a (and thus b) in place
|
||||
assert.eq(a, [1, 2, 3, 4])
|
||||
assert.eq(b, [1, 2, 3, 4]) # alias observes the change
|
||||
|
||||
a = [1, 2, 3]
|
||||
b = a
|
||||
a.extend([4]) # updates existing list
|
||||
assert.eq(a, [1, 2, 3, 4])
|
||||
assert.eq(b, [1, 2, 3, 4]) # alias observes the change
|
||||
|
||||
list_extend()
|
||||
|
||||
# Unlike list.extend(iterable), list += iterable makes its LHS name local.
|
||||
a_list = []
|
||||
|
||||
def f4():
|
||||
a_list += [1] # binding use => a_list is a local var
|
||||
|
||||
assert.fails(f4, "local variable a_list referenced before assignment")
|
||||
|
||||
# list += <not iterable>
|
||||
def f5():
|
||||
x = []
|
||||
x += 1
|
||||
|
||||
assert.fails(f5, "unknown binary op: list \\+ int")
|
||||
|
||||
# frozen list += iterable
|
||||
def f6():
|
||||
x = []
|
||||
freeze(x)
|
||||
x += [1]
|
||||
|
||||
assert.fails(f6, "cannot apply \\+= to frozen list")
|
||||
|
||||
# list += hasfields (hasfields is not iterable but defines list+hasfields)
|
||||
def f7():
|
||||
x = []
|
||||
x += hasfields()
|
||||
return x
|
||||
|
||||
assert.eq(f7(), 42) # weird, but exercises a corner case in list+=x.
|
||||
|
||||
# append
|
||||
x5 = [1, 2, 3]
|
||||
x5.append(4)
|
||||
x5.append("abc")
|
||||
assert.eq(x5, [1, 2, 3, 4, "abc"])
|
||||
|
||||
# extend
|
||||
x5a = [1, 2, 3]
|
||||
x5a.extend("abc".elems()) # string
|
||||
x5a.extend((True, False)) # tuple
|
||||
assert.eq(x5a, [1, 2, 3, "a", "b", "c", True, False])
|
||||
|
||||
# list.insert
|
||||
def insert_at(index):
|
||||
x = list(range(3))
|
||||
x.insert(index, 42)
|
||||
return x
|
||||
|
||||
assert.eq(insert_at(-99), [42, 0, 1, 2])
|
||||
assert.eq(insert_at(-2), [0, 42, 1, 2])
|
||||
assert.eq(insert_at(-1), [0, 1, 42, 2])
|
||||
assert.eq(insert_at(0), [42, 0, 1, 2])
|
||||
assert.eq(insert_at(1), [0, 42, 1, 2])
|
||||
assert.eq(insert_at(2), [0, 1, 42, 2])
|
||||
assert.eq(insert_at(3), [0, 1, 2, 42])
|
||||
assert.eq(insert_at(4), [0, 1, 2, 42])
|
||||
|
||||
# list.remove
|
||||
def remove(v):
|
||||
x = [3, 1, 4, 1]
|
||||
x.remove(v)
|
||||
return x
|
||||
|
||||
assert.eq(remove(3), [1, 4, 1])
|
||||
assert.eq(remove(1), [3, 4, 1])
|
||||
assert.eq(remove(4), [3, 1, 1])
|
||||
assert.fails(lambda: [3, 1, 4, 1].remove(42), "remove: element not found")
|
||||
|
||||
# list.index
|
||||
bananas = list("bananas".elems())
|
||||
assert.eq(bananas.index("a"), 1) # bAnanas
|
||||
assert.fails(lambda: bananas.index("d"), "value not in list")
|
||||
|
||||
# start
|
||||
assert.eq(bananas.index("a", -1000), 1) # bAnanas
|
||||
assert.eq(bananas.index("a", 0), 1) # bAnanas
|
||||
assert.eq(bananas.index("a", 1), 1) # bAnanas
|
||||
assert.eq(bananas.index("a", 2), 3) # banAnas
|
||||
assert.eq(bananas.index("a", 3), 3) # banAnas
|
||||
assert.eq(bananas.index("b", 0), 0) # Bananas
|
||||
assert.eq(bananas.index("n", -3), 4) # banaNas
|
||||
assert.fails(lambda: bananas.index("n", -2), "value not in list")
|
||||
assert.eq(bananas.index("s", -2), 6) # bananaS
|
||||
assert.fails(lambda: bananas.index("b", 1), "value not in list")
|
||||
|
||||
# start, end
|
||||
assert.eq(bananas.index("s", -1000, 7), 6) # bananaS
|
||||
assert.fails(lambda: bananas.index("s", -1000, 6), "value not in list")
|
||||
assert.fails(lambda: bananas.index("d", -1000, 1000), "value not in list")
|
||||
|
||||
# slicing, x[i:j:k]
|
||||
assert.eq(bananas[6::-2], list("snnb".elems()))
|
||||
assert.eq(bananas[5::-2], list("aaa".elems()))
|
||||
assert.eq(bananas[4::-2], list("nnb".elems()))
|
||||
assert.eq(bananas[99::-2], list("snnb".elems()))
|
||||
assert.eq(bananas[100::-2], list("snnb".elems()))
|
||||
# TODO(adonovan): many more tests
|
||||
|
||||
# iterator invalidation
|
||||
def iterator1():
|
||||
list = [0, 1, 2]
|
||||
for x in list:
|
||||
list[x] = 2 * x
|
||||
return list
|
||||
|
||||
assert.fails(iterator1, "assign to element.* during iteration")
|
||||
|
||||
def iterator2():
|
||||
list = [0, 1, 2]
|
||||
for x in list:
|
||||
list.remove(x)
|
||||
|
||||
assert.fails(iterator2, "remove.*during iteration")
|
||||
|
||||
def iterator3():
|
||||
list = [0, 1, 2]
|
||||
for x in list:
|
||||
list.append(3)
|
||||
|
||||
assert.fails(iterator3, "append.*during iteration")
|
||||
|
||||
def iterator4():
|
||||
list = [0, 1, 2]
|
||||
for x in list:
|
||||
list.extend([3, 4])
|
||||
|
||||
assert.fails(iterator4, "extend.*during iteration")
|
||||
|
||||
def iterator5():
|
||||
def f(x):
|
||||
x.append(4)
|
||||
|
||||
list = [1, 2, 3]
|
||||
_ = [f(list) for x in list]
|
||||
|
||||
assert.fails(iterator5, "append.*during iteration")
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
# Tests of math module.
|
||||
|
||||
load('math.star', 'math')
|
||||
load('assert.star', 'assert')
|
||||
|
||||
def near(got, want, threshold):
|
||||
return math.fabs(got-want) < threshold
|
||||
|
||||
inf, nan = float("inf"), float("nan")
|
||||
|
||||
# ceil
|
||||
assert.eq(math.ceil(0.0), 0.0)
|
||||
assert.eq(math.ceil(0.4), 1.0)
|
||||
assert.eq(math.ceil(0.5), 1.0)
|
||||
assert.eq(math.ceil(1.0), 1.0)
|
||||
assert.eq(math.ceil(10.0), 10.0)
|
||||
assert.eq(math.ceil(0), 0.0)
|
||||
assert.eq(math.ceil(1), 1.0)
|
||||
assert.eq(math.ceil(10), 10.0)
|
||||
assert.eq(math.ceil(-0.0), 0.0)
|
||||
assert.eq(math.ceil(-0.4), 0.0)
|
||||
assert.eq(math.ceil(-0.5), 0.0)
|
||||
assert.eq(math.ceil(-1.0), -1.0)
|
||||
assert.eq(math.ceil(-10.0), -10.0)
|
||||
assert.eq(math.ceil(-1), -1.0)
|
||||
assert.eq(math.ceil(-10), -10.0)
|
||||
assert.eq(type(math.ceil(0)), "int")
|
||||
assert.eq(type(math.ceil(0.4)), "int")
|
||||
assert.eq(type(math.ceil(10)), "int")
|
||||
assert.eq(type(math.ceil(-10.0)), "int")
|
||||
assert.eq(type(math.ceil(-0.5)), "int")
|
||||
assert.eq(math.ceil((1<<63) + 0.5), int(float((1<<63) + 1)))
|
||||
assert.fails(
|
||||
lambda: math.ceil(inf), "cannot convert float infinity to integer")
|
||||
assert.fails(
|
||||
lambda: math.ceil(-inf), "cannot convert float infinity to integer")
|
||||
assert.fails(
|
||||
lambda: math.ceil(nan), "cannot convert float NaN to integer")
|
||||
assert.fails(lambda: math.ceil("0"), "got string, want float or int")
|
||||
# fabs
|
||||
assert.eq(math.fabs(2.0), 2.0)
|
||||
assert.eq(math.fabs(0.0), 0.0)
|
||||
assert.eq(math.fabs(-2.0), 2.0)
|
||||
assert.eq(math.fabs(2), 2)
|
||||
assert.eq(math.fabs(0), 0)
|
||||
assert.eq(math.fabs(-2), 2)
|
||||
assert.eq(math.fabs(inf), inf)
|
||||
assert.eq(math.fabs(-inf), inf)
|
||||
assert.eq(math.fabs(nan), nan)
|
||||
assert.fails(lambda: math.fabs("0"), "got string, want float or int")
|
||||
# floor
|
||||
assert.eq(math.floor(0.0), 0.0)
|
||||
assert.eq(math.floor(0.4), 0.0)
|
||||
assert.eq(math.floor(0.5), 0.0)
|
||||
assert.eq(math.floor(1.0), 1.0)
|
||||
assert.eq(math.floor(10.0), 10.0)
|
||||
assert.eq(math.floor(-0.0), 0.0)
|
||||
assert.eq(math.floor(-0.4), -1.0)
|
||||
assert.eq(math.floor(-0.5), -1.0)
|
||||
assert.eq(math.floor(-1.0), -1.0)
|
||||
assert.eq(math.floor(-10.0), -10.0)
|
||||
assert.eq(type(math.floor(0)), "int")
|
||||
assert.eq(type(math.floor(0.4)), "int")
|
||||
assert.eq(type(math.floor(10)), "int")
|
||||
assert.eq(type(math.floor(-10.0)), "int")
|
||||
assert.eq(type(math.floor(-0.5)), "int")
|
||||
assert.eq(math.floor((1<<63) + 0.5), int(float(1<<63)))
|
||||
assert.fails(
|
||||
lambda: math.floor(inf), "cannot convert float infinity to integer")
|
||||
assert.fails(
|
||||
lambda: math.floor(-inf), "cannot convert float infinity to integer")
|
||||
assert.fails(
|
||||
lambda: math.floor(nan), "cannot convert float NaN to integer")
|
||||
assert.fails(lambda: math.floor("0"), "got string, want float or int")
|
||||
# mod
|
||||
assert.eq(math.mod(5, 3), 2)
|
||||
assert.eq(math.mod(inf, 1), nan)
|
||||
assert.eq(math.mod(-inf, 1.0), nan)
|
||||
assert.eq(math.mod(nan, 1.0), nan)
|
||||
assert.eq(math.mod(1.0, 0.0), nan)
|
||||
assert.eq(math.mod(1.0, inf), 1)
|
||||
assert.eq(math.mod(1.0, -inf), 1)
|
||||
assert.eq(math.mod(1.0, nan), nan)
|
||||
assert.fails(lambda: math.mod("0", 1.0), "got string, want float or int")
|
||||
assert.fails(lambda: math.mod(1.0, "0"), "got string, want float or int")
|
||||
# pow
|
||||
assert.eq(math.pow(5, 3), 125)
|
||||
assert.eq(math.pow(5, 0), 1)
|
||||
assert.eq(math.pow(5, 1), 5)
|
||||
assert.eq(math.pow(1, 5), 1)
|
||||
assert.eq(math.pow(inf, 1), inf)
|
||||
assert.eq(math.pow(-inf, 1.0), -inf)
|
||||
assert.eq(math.pow(nan, 1.0), nan)
|
||||
assert.eq(math.pow(1.1, inf), inf)
|
||||
assert.eq(math.pow(1.1, -inf), 0)
|
||||
assert.eq(math.pow(2.0, nan), nan)
|
||||
assert.fails(lambda: math.pow("0", 1.0), "got string, want float or int")
|
||||
assert.fails(lambda: math.pow(1.0, "0"), "got string, want float or int")
|
||||
# copysign
|
||||
assert.eq(math.copysign(3.2, -1), -3.2)
|
||||
assert.eq(math.copysign(inf, -1.0),-inf)
|
||||
assert.eq(math.copysign(-inf, -1), -inf)
|
||||
assert.eq(math.copysign(nan, -1), nan)
|
||||
assert.eq(math.copysign(-1, nan), 1)
|
||||
assert.fails(lambda: math.copysign("0", 1.0), "got string, want float or int")
|
||||
assert.fails(lambda: math.copysign(1.0, "0"), "got string, want float or int")
|
||||
# remainder
|
||||
assert.eq(math.remainder(3, 5), -2)
|
||||
assert.eq(math.remainder(1, 0), nan)
|
||||
assert.eq(math.remainder(2, inf), 2)
|
||||
assert.eq(math.remainder(2, -inf), 2)
|
||||
assert.eq(math.remainder(inf, -1.0), nan)
|
||||
assert.eq(math.remainder(-inf, -1), nan)
|
||||
assert.eq(math.remainder(nan, -1), nan)
|
||||
assert.eq(math.remainder(-1, nan), nan)
|
||||
assert.fails(lambda: math.remainder("0", 1.0), "got string, want float or int")
|
||||
assert.fails(lambda: math.remainder(1.0, "0"), "got string, want float or int")
|
||||
# round
|
||||
assert.eq(math.round(0.0), 0.0)
|
||||
assert.eq(math.round(0.4), 0.0)
|
||||
assert.eq(math.round(0.5), 1.0)
|
||||
assert.eq(math.round(0.6), 1.0)
|
||||
assert.eq(math.round(1.0), 1.0)
|
||||
assert.eq(math.round(10.0), 10.0)
|
||||
assert.eq(math.round(inf), inf)
|
||||
assert.eq(math.round(nan), nan)
|
||||
assert.eq(math.round(-0.4), 0.0)
|
||||
assert.eq(math.round(-0.5), -1.0)
|
||||
assert.eq(math.round(-0.6), -1.0)
|
||||
assert.eq(math.round(-1.0), -1.0)
|
||||
assert.eq(math.round(-10.0), -10.0)
|
||||
assert.eq(math.round(-inf), -inf)
|
||||
assert.fails(lambda: math.round("0"), "got string, want float or int")
|
||||
# exp
|
||||
assert.eq(math.exp(0.0), 1)
|
||||
assert.eq(math.exp(1.0), math.e)
|
||||
assert.true(near(math.exp(2.0), math.e * math.e, 0.00000000000001))
|
||||
assert.eq(math.exp(-1.0), 1 / math.e)
|
||||
assert.eq(math.exp(0), 1)
|
||||
assert.eq(math.exp(1), math.e)
|
||||
assert.true(near(math.exp(2), math.e * math.e, 0.00000000000001))
|
||||
assert.eq(math.exp(-1), 1 / math.e)
|
||||
assert.eq(math.exp(inf), inf)
|
||||
assert.eq(math.exp(-inf), 0)
|
||||
assert.eq(math.exp(nan), nan)
|
||||
assert.fails(lambda: math.exp("0"), "got string, want float or int")
|
||||
# sqrt
|
||||
assert.eq(math.sqrt(0.0), 0.0)
|
||||
assert.eq(math.sqrt(4.0), 2.0)
|
||||
assert.eq(math.sqrt(-4.0), nan)
|
||||
assert.eq(math.sqrt(0), 0)
|
||||
assert.eq(math.sqrt(4), 2)
|
||||
assert.eq(math.sqrt(-4), nan)
|
||||
assert.eq(math.sqrt(nan), nan)
|
||||
assert.eq(math.sqrt(inf), inf)
|
||||
assert.eq(math.sqrt(-inf), nan)
|
||||
assert.fails(lambda: math.sqrt("0"), "got string, want float or int")
|
||||
# acos
|
||||
assert.eq(math.acos(1.0), 0)
|
||||
assert.eq(math.acos(1), 0)
|
||||
assert.eq(math.acos(0.0), math.pi / 2)
|
||||
assert.eq(math.acos(0), math.pi / 2)
|
||||
assert.eq(math.acos(-1.0), math.pi)
|
||||
assert.eq(math.acos(-1), math.pi)
|
||||
assert.eq(math.acos(1.01), nan)
|
||||
assert.eq(math.acos(-1.01), nan)
|
||||
assert.eq(math.acos(inf), nan)
|
||||
assert.eq(math.acos(-inf), nan)
|
||||
assert.eq(math.acos(nan), nan)
|
||||
assert.fails(lambda: math.acos("0"), "got string, want float or int")
|
||||
# asin
|
||||
assert.eq(math.asin(0.0), 0)
|
||||
assert.eq(math.asin(1.0), math.pi / 2)
|
||||
assert.eq(math.asin(-1.0), -math.pi / 2)
|
||||
assert.eq(math.asin(0), 0)
|
||||
assert.eq(math.asin(1), math.pi / 2)
|
||||
assert.eq(math.asin(-1), -math.pi / 2)
|
||||
assert.eq(math.asin(1.01), nan)
|
||||
assert.eq(math.asin(-1.01), nan)
|
||||
assert.eq(math.asin(inf), nan)
|
||||
assert.eq(math.asin(-inf), nan)
|
||||
assert.eq(math.asin(nan), nan)
|
||||
assert.fails(lambda: math.asin("0"), "got string, want float or int")
|
||||
# atan
|
||||
assert.eq(math.atan(0.0), 0)
|
||||
assert.eq(math.atan(1.0), math.pi / 4)
|
||||
assert.eq(math.atan(-1.0), -math.pi / 4)
|
||||
assert.eq(math.atan(1), math.pi / 4)
|
||||
assert.eq(math.atan(-1), -math.pi / 4)
|
||||
assert.eq(math.atan(inf), math.pi / 2)
|
||||
assert.eq(math.atan(-inf), -math.pi / 2)
|
||||
assert.eq(math.atan(nan), nan)
|
||||
assert.fails(lambda: math.atan("0"), "got string, want float or int")
|
||||
# atan2
|
||||
assert.eq(math.atan2(1.0, 1.0), math.pi / 4)
|
||||
assert.eq(math.atan2(-1.0, 1.0), -math.pi / 4)
|
||||
assert.eq(math.atan2(0.0, 10.0), 0)
|
||||
assert.eq(math.atan2(0.0, -10.0), math.pi)
|
||||
assert.eq(math.atan2(-0.0, -10.0), -math.pi)
|
||||
assert.eq(math.atan2(10.0, 0.0), math.pi / 2)
|
||||
assert.eq(math.atan2(-10.0, 0.0), -math.pi / 2)
|
||||
assert.eq(math.atan2(1, 1), math.pi / 4)
|
||||
assert.eq(math.atan2(-1, 1), -math.pi / 4)
|
||||
assert.eq(math.atan2(0, 10.0), 0)
|
||||
assert.eq(math.atan2(0.0, -10), math.pi)
|
||||
assert.eq(math.atan2(-0.0, -10), -math.pi)
|
||||
assert.eq(math.atan2(10.0, 0), math.pi / 2)
|
||||
assert.eq(math.atan2(-10.0, 0), -math.pi / 2)
|
||||
assert.eq(math.atan2(1.0, nan), nan)
|
||||
assert.eq(math.atan2(nan, 1.0), nan)
|
||||
assert.eq(math.atan2(10.0, inf), 0)
|
||||
assert.eq(math.atan2(-10.0, inf), 0)
|
||||
assert.eq(math.atan2(10.0, -inf), math.pi)
|
||||
assert.eq(math.atan2(-10.0, -inf), -math.pi)
|
||||
assert.eq(math.atan2(inf, 10.0), math.pi / 2)
|
||||
assert.eq(math.atan2(inf, -10.0), math.pi / 2)
|
||||
assert.eq(math.atan2(-inf, 10.0), -math.pi / 2)
|
||||
assert.eq(math.atan2(-inf, -10.0), -math.pi / 2)
|
||||
assert.eq(math.atan2(inf, inf), math.pi / 4)
|
||||
assert.eq(math.atan2(-inf, inf), -math.pi / 4)
|
||||
assert.eq(math.atan2(inf, -inf), 3 * math.pi / 4)
|
||||
assert.eq(math.atan2(-inf, -inf), -3 * math.pi / 4)
|
||||
assert.fails(lambda: math.atan2("0", 1.0), "got string, want float or int")
|
||||
assert.fails(lambda: math.atan2(1.0, "0"), "got string, want float or int")
|
||||
# cos
|
||||
assert.eq(math.cos(0.0), 1)
|
||||
assert.true(near(math.cos(math.pi / 2), 0, 0.00000000000001))
|
||||
assert.eq(math.cos(math.pi), -1)
|
||||
assert.true(near(math.cos(-math.pi / 2), 0, 0.00000000000001))
|
||||
assert.eq(math.cos(-math.pi), -1)
|
||||
assert.eq(math.cos(inf), nan)
|
||||
assert.eq(math.cos(-inf), nan)
|
||||
assert.eq(math.cos(nan), nan)
|
||||
assert.fails(lambda: math.cos("0"), "got string, want float or int")
|
||||
# hypot
|
||||
assert.eq(math.hypot(4.0, 3.0), 5.0)
|
||||
assert.eq(math.hypot(4, 3), 5.0)
|
||||
assert.eq(math.hypot(inf, 3.0), inf)
|
||||
assert.eq(math.hypot(-inf, 3.0), inf)
|
||||
assert.eq(math.hypot(3.0, inf), inf)
|
||||
assert.eq(math.hypot(3.0, -inf), inf)
|
||||
assert.eq(math.hypot(nan, 3.0), nan)
|
||||
assert.eq(math.hypot(3.0, nan), nan)
|
||||
assert.fails(lambda: math.hypot("0", 1.0), "got string, want float or int")
|
||||
assert.fails(lambda: math.hypot(1.0, "0"), "got string, want float or int")
|
||||
# sin
|
||||
assert.eq(math.sin(0.0), 0)
|
||||
assert.eq(math.sin(0), 0)
|
||||
assert.eq(math.sin(math.pi / 2), 1)
|
||||
assert.eq(math.sin(-math.pi / 2), -1)
|
||||
assert.eq(math.sin(inf), nan)
|
||||
assert.eq(math.sin(-inf), nan)
|
||||
assert.eq(math.sin(nan), nan)
|
||||
assert.fails(lambda: math.sin("0"), "got string, want float or int")
|
||||
# tan
|
||||
assert.eq(math.tan(0.0), 0)
|
||||
assert.eq(math.tan(0), 0)
|
||||
assert.true(near(math.tan(math.pi / 4), 1, 0.00000000000001))
|
||||
assert.true(near(math.tan(-math.pi / 4), -1, 0.00000000000001))
|
||||
assert.eq(math.tan(inf), nan)
|
||||
assert.eq(math.tan(-inf), nan)
|
||||
assert.eq(math.tan(nan), nan)
|
||||
assert.fails(lambda: math.tan("0"), "got string, want float or int")
|
||||
# degrees
|
||||
oneDeg = 57.29577951308232
|
||||
assert.eq(math.degrees(1.0), oneDeg)
|
||||
assert.eq(math.degrees(1), oneDeg)
|
||||
assert.eq(math.degrees(-1.0), -oneDeg)
|
||||
assert.eq(math.degrees(-1), -oneDeg)
|
||||
assert.eq(math.degrees(inf), inf)
|
||||
assert.eq(math.degrees(-inf), -inf)
|
||||
assert.eq(math.degrees(nan), nan)
|
||||
assert.fails(lambda: math.degrees("0"), "got string, want float or int")
|
||||
# radians
|
||||
oneRad = 0.017453292519943295
|
||||
assert.eq(math.radians(1.0), oneRad)
|
||||
assert.eq(math.radians(-1.0), -oneRad)
|
||||
assert.eq(math.radians(1), oneRad)
|
||||
assert.eq(math.radians(-1), -oneRad)
|
||||
assert.eq(math.radians(inf), inf)
|
||||
assert.eq(math.radians(-inf), -inf)
|
||||
assert.eq(math.radians(nan), nan)
|
||||
assert.fails(lambda: math.radians("0"), "got string, want float or int")
|
||||
# acosh
|
||||
assert.eq(math.acosh(1.0), 0)
|
||||
assert.eq(math.acosh(1), 0)
|
||||
assert.eq(math.acosh(0.99), nan)
|
||||
assert.eq(math.acosh(0), nan)
|
||||
assert.eq(math.acosh(-0.99), nan)
|
||||
assert.eq(math.acosh(-inf), nan)
|
||||
assert.eq(math.acosh(inf), inf)
|
||||
assert.eq(math.acosh(nan), nan)
|
||||
assert.fails(lambda: math.acosh("0"), "got string, want float or int")
|
||||
# asinh
|
||||
asinhOne = 0.8813735870195432
|
||||
assert.eq(math.asinh(0.0), 0)
|
||||
assert.eq(math.asinh(0), 0)
|
||||
assert.true(near(math.asinh(1.0), asinhOne, 0.00000001))
|
||||
assert.true(near(math.asinh(1), asinhOne, 0.00000001))
|
||||
assert.true(near(math.asinh(-1.0), -asinhOne, 0.00000001))
|
||||
assert.true(near(math.asinh(-1), -asinhOne, 0.00000001))
|
||||
assert.eq(math.asinh(inf), inf)
|
||||
assert.eq(math.asinh(-inf), -inf)
|
||||
assert.eq(math.asinh(nan), nan)
|
||||
assert.fails(lambda: math.asinh("0"), "got string, want float or int")
|
||||
# atanh
|
||||
atanhHalf = 0.5493061443340548
|
||||
assert.eq(math.atanh(0.0), 0)
|
||||
assert.eq(math.atanh(0), 0)
|
||||
assert.eq(math.atanh(0.5), atanhHalf)
|
||||
assert.eq(math.atanh(-0.5), -atanhHalf)
|
||||
assert.eq(math.atanh(1), inf)
|
||||
assert.eq(math.atanh(-1), -inf)
|
||||
assert.eq(math.atanh(1.1), nan)
|
||||
assert.eq(math.atanh(-1.1), nan)
|
||||
assert.eq(math.atanh(inf), nan)
|
||||
assert.eq(math.atanh(-inf), nan)
|
||||
assert.eq(math.atanh(nan), nan)
|
||||
assert.fails(lambda: math.atanh("0"), "got string, want float or int")
|
||||
# cosh
|
||||
coshOne = 1.5430806348152437
|
||||
assert.eq(math.cosh(1.0), coshOne)
|
||||
assert.eq(math.cosh(1), coshOne)
|
||||
assert.eq(math.cosh(0.0), 1)
|
||||
assert.eq(math.cosh(0), 1)
|
||||
assert.eq(math.cosh(-inf), inf)
|
||||
assert.eq(math.cosh(inf), inf)
|
||||
assert.eq(math.cosh(nan), nan)
|
||||
assert.fails(lambda: math.cosh("0"), "got string, want float or int")
|
||||
# sinh
|
||||
sinhOne = 1.1752011936438014
|
||||
assert.eq(math.sinh(0.0), 0)
|
||||
assert.eq(math.sinh(0), 0)
|
||||
assert.eq(math.sinh(1.0), sinhOne)
|
||||
assert.eq(math.sinh(1), sinhOne)
|
||||
assert.eq(math.sinh(-1.0), -sinhOne)
|
||||
assert.eq(math.sinh(-1), -sinhOne)
|
||||
assert.eq(math.sinh(-inf), -inf)
|
||||
assert.eq(math.sinh(inf), inf)
|
||||
assert.eq(math.sinh(nan), nan)
|
||||
assert.fails(lambda: math.sinh("0"), "got string, want float or int")
|
||||
# tanh
|
||||
tanhOne = 0.7615941559557649
|
||||
assert.eq(math.tanh(0.0), 0)
|
||||
assert.eq(math.tanh(0), 0)
|
||||
assert.eq(math.tanh(1.0), tanhOne)
|
||||
assert.eq(math.tanh(1), tanhOne)
|
||||
assert.eq(math.tanh(-1.0), -tanhOne)
|
||||
assert.eq(math.tanh(-1), -tanhOne)
|
||||
assert.eq(math.tanh(-inf), -1)
|
||||
assert.eq(math.tanh(inf), 1)
|
||||
assert.eq(math.tanh(nan), nan)
|
||||
assert.fails(lambda: math.tanh("0"), "got string, want float or int")
|
||||
# log
|
||||
assert.eq(math.log(math.e), 1)
|
||||
assert.eq(math.log(10, 10), 1)
|
||||
assert.eq(math.log(10.0, 10.0), 1)
|
||||
assert.eq(math.log(2, 2.0), 1)
|
||||
assert.fails(lambda: math.log(2, 1), "division by zero")
|
||||
assert.fails(lambda: math.log(0.99, 1.0), "division by zero")
|
||||
assert.eq(math.log(0.0), -inf)
|
||||
assert.eq(math.log(0), -inf)
|
||||
assert.eq(math.log(-1.0), nan)
|
||||
assert.eq(math.log(-1), nan)
|
||||
assert.eq(math.log(nan), nan)
|
||||
assert.fails(lambda: math.log("0"), "got string, want float or int")
|
||||
assert.fails(lambda: math.log(10, "10"), "got string, want float or int")
|
||||
# gamma
|
||||
assert.eq(math.gamma(1.0), 1)
|
||||
assert.eq(math.gamma(1), 1)
|
||||
assert.eq(math.gamma(-1), nan)
|
||||
assert.eq(math.gamma(0), inf)
|
||||
assert.eq(math.gamma(-inf), nan)
|
||||
assert.eq(math.gamma(inf), inf)
|
||||
assert.eq(math.gamma(nan), nan)
|
||||
assert.fails(lambda: math.gamma("0"), "got string, want float or int")
|
||||
# Constants
|
||||
assert.eq(math.e, 2.7182818284590452)
|
||||
assert.eq(math.pi, 3.1415926535897932)
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
# Miscellaneous tests of Starlark evaluation.
|
||||
# This is a "chunked" file: each "---" effectively starts a new file.
|
||||
|
||||
# TODO(adonovan): move these tests into more appropriate files.
|
||||
# TODO(adonovan): test coverage:
|
||||
# - stmts: pass; if cond fail; += and failures;
|
||||
# for x fail; for x not iterable; for can't assign; for
|
||||
# error in loop body
|
||||
# - subassign fail
|
||||
# - x[i]=x fail in both operands; frozen x; list index not int; boundscheck
|
||||
# - x.f = ...
|
||||
# - failure in list expr [...]; tuple expr; dict expr (bad key)
|
||||
# - cond expr semantics; failures
|
||||
# - x[i] failures in both args; dict and iterator key and range checks;
|
||||
# unhandled operand types
|
||||
# - +: list/list, int/int, string/string, tuple+tuple, dict/dict;
|
||||
# - * and ** calls: various errors
|
||||
# - call of non-function
|
||||
# - slice x[ijk]
|
||||
# - comprehension: unhashable dict key;
|
||||
# scope of vars (local and toplevel); noniterable for clause
|
||||
# - unknown unary op
|
||||
# - ordering of values
|
||||
# - freeze, transitivity of its effect.
|
||||
# - add an application-defined type to the environment so we can test it.
|
||||
# - even more:
|
||||
#
|
||||
# eval
|
||||
# pass statement
|
||||
# assign to tuple l-value -- illegal
|
||||
# assign to list l-value -- illegal
|
||||
# assign to field
|
||||
# tuple + tuple
|
||||
# call with *args, **kwargs
|
||||
# slice with step
|
||||
# tuple slice
|
||||
# interpolate with %c, %%
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# Ordered comparisons require values of the same type.
|
||||
assert.fails(lambda: None < None, "not impl")
|
||||
assert.fails(lambda: None < False, "not impl")
|
||||
assert.fails(lambda: False < list, "not impl")
|
||||
assert.fails(lambda: list < {}, "not impl")
|
||||
assert.fails(lambda: {} < (lambda: None), "not impl")
|
||||
assert.fails(lambda: (lambda: None) < 0, "not impl")
|
||||
assert.fails(lambda: 0 < [], "not impl")
|
||||
assert.fails(lambda: [] < "", "not impl")
|
||||
assert.fails(lambda: "" < (), "not impl")
|
||||
# Except int < float:
|
||||
assert.lt(1, 2.0)
|
||||
assert.lt(2.0, 3)
|
||||
|
||||
---
|
||||
# cyclic data structures
|
||||
load("assert.star", "assert")
|
||||
|
||||
cyclic = [1, 2, 3] # list cycle
|
||||
cyclic[1] = cyclic
|
||||
assert.eq(str(cyclic), "[1, [...], 3]")
|
||||
assert.fails(lambda: cyclic < cyclic, "maximum recursion")
|
||||
assert.fails(lambda: cyclic == cyclic, "maximum recursion")
|
||||
cyclic2 = [1, 2, 3]
|
||||
cyclic2[1] = cyclic2
|
||||
assert.fails(lambda: cyclic2 == cyclic, "maximum recursion")
|
||||
|
||||
cyclic3 = [1, [2, 3]] # list-list cycle
|
||||
cyclic3[1][0] = cyclic3
|
||||
assert.eq(str(cyclic3), "[1, [[...], 3]]")
|
||||
cyclic4 = {"x": 1}
|
||||
cyclic4["x"] = cyclic4
|
||||
assert.eq(str(cyclic4), "{\"x\": {...}}")
|
||||
cyclic5 = [0, {"x": 1}] # list-dict cycle
|
||||
cyclic5[1]["x"] = cyclic5
|
||||
assert.eq(str(cyclic5), "[0, {\"x\": [...]}]")
|
||||
assert.eq(str(cyclic5), "[0, {\"x\": [...]}]")
|
||||
assert.fails(lambda: cyclic5 == cyclic5 ,"maximum recursion")
|
||||
cyclic6 = [0, {"x": 1}]
|
||||
cyclic6[1]["x"] = cyclic6
|
||||
assert.fails(lambda: cyclic5 == cyclic6, "maximum recursion")
|
||||
|
||||
---
|
||||
# regression
|
||||
load("assert.star", "assert")
|
||||
|
||||
# was a parse error:
|
||||
assert.eq(("ababab"[2:]).replace("b", "c"), "acac")
|
||||
assert.eq("ababab"[2:].replace("b", "c"), "acac")
|
||||
|
||||
# test parsing of line continuation, at toplevel and in expression.
|
||||
three = 1 + \
|
||||
2
|
||||
assert.eq(1 + \
|
||||
2, three)
|
||||
|
||||
---
|
||||
# A regression test for error position information.
|
||||
|
||||
_ = {}.get(1, default=2) ### "get: unexpected keyword arguments"
|
||||
|
||||
---
|
||||
# Load exposes explicitly declared globals from other modules.
|
||||
load('assert.star', 'assert', 'freeze')
|
||||
assert.eq(str(freeze), '<built-in function freeze>')
|
||||
|
||||
---
|
||||
# Load does not expose pre-declared globals from other modules.
|
||||
# See github.com/google/skylark/issues/75.
|
||||
load('assert.star', 'assert', 'matches') ### "matches not found in module"
|
||||
|
||||
---
|
||||
# Load does not expose universals accessible in other modules.
|
||||
load('assert.star', 'len') ### "len not found in module"
|
||||
|
||||
|
||||
---
|
||||
# Test plus folding optimization.
|
||||
load('assert.star', 'assert')
|
||||
|
||||
s = "s"
|
||||
l = [4]
|
||||
t = (4,)
|
||||
|
||||
assert.eq("a" + "b" + "c", "abc")
|
||||
assert.eq("a" + "b" + s + "c", "absc")
|
||||
assert.eq(() + (1,) + (2, 3), (1, 2, 3))
|
||||
assert.eq(() + (1,) + t + (2, 3), (1, 4, 2, 3))
|
||||
assert.eq([] + [1] + [2, 3], [1, 2, 3])
|
||||
assert.eq([] + [1] + l + [2, 3], [1, 4, 2, 3])
|
||||
|
||||
assert.fails(lambda: "a" + "b" + 1 + "c", "unknown binary op: string \\+ int")
|
||||
assert.fails(lambda: () + () + 1 + (), "unknown binary op: tuple \\+ int")
|
||||
assert.fails(lambda: [] + [] + 1 + [], "unknown binary op: list \\+ int")
|
||||
|
||||
|
||||
|
||||
---
|
||||
load('assert.star', 'froze') ### `name froze not found .*did you mean freeze`
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# Tests of Module.
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
assert.eq(type(assert), "module")
|
||||
assert.eq(str(assert), '<module "assert">')
|
||||
assert.eq(dir(assert), ["contains", "eq", "fail", "fails", "lt", "ne", "true"])
|
||||
assert.fails(lambda : {assert: None}, "unhashable: module")
|
||||
|
||||
def assignfield():
|
||||
assert.foo = None
|
||||
|
||||
assert.fails(assignfield, "can't assign to .foo field of module")
|
||||
|
||||
# no such field
|
||||
assert.fails(lambda : assert.nonesuch, "module has no .nonesuch field or method$")
|
||||
assert.fails(lambda : assert.falls, "module has no .falls field or method .did you mean .fails\\?")
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
# Copyright 2017 The Bazel Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Skylib module containing file path manipulation functions.
|
||||
|
||||
NOTE: The functions in this module currently only support paths with Unix-style
|
||||
path separators (forward slash, "/"); they do not handle Windows-style paths
|
||||
with backslash separators or drive letters.
|
||||
"""
|
||||
|
||||
# This file is in the Bazel build language dialect of Starlark,
|
||||
# so declarations of 'fail' and 'struct' are required to make
|
||||
# it compile in the core language.
|
||||
def fail(msg):
|
||||
print(msg)
|
||||
|
||||
struct = dict
|
||||
|
||||
def _basename(p):
|
||||
"""Returns the basename (i.e., the file portion) of a path.
|
||||
|
||||
Note that if `p` ends with a slash, this function returns an empty string.
|
||||
This matches the behavior of Python's `os.path.basename`, but differs from
|
||||
the Unix `basename` command (which would return the path segment preceding
|
||||
the final slash).
|
||||
|
||||
Args:
|
||||
p: The path whose basename should be returned.
|
||||
|
||||
Returns:
|
||||
The basename of the path, which includes the extension.
|
||||
"""
|
||||
return p.rpartition("/")[-1]
|
||||
|
||||
def _dirname(p):
|
||||
"""Returns the dirname of a path.
|
||||
|
||||
The dirname is the portion of `p` up to but not including the file portion
|
||||
(i.e., the basename). Any slashes immediately preceding the basename are not
|
||||
included, unless omitting them would make the dirname empty.
|
||||
|
||||
Args:
|
||||
p: The path whose dirname should be returned.
|
||||
|
||||
Returns:
|
||||
The dirname of the path.
|
||||
"""
|
||||
prefix, sep, _ = p.rpartition("/")
|
||||
if not prefix:
|
||||
return sep
|
||||
else:
|
||||
# If there are multiple consecutive slashes, strip them all out as Python's
|
||||
# os.path.dirname does.
|
||||
return prefix.rstrip("/")
|
||||
|
||||
def _is_absolute(path):
|
||||
"""Returns `True` if `path` is an absolute path.
|
||||
|
||||
Args:
|
||||
path: A path (which is a string).
|
||||
|
||||
Returns:
|
||||
`True` if `path` is an absolute path.
|
||||
"""
|
||||
return path.startswith("/") or (len(path) > 2 and path[1] == ":")
|
||||
|
||||
def _join(path, *others):
|
||||
"""Joins one or more path components intelligently.
|
||||
|
||||
This function mimics the behavior of Python's `os.path.join` function on POSIX
|
||||
platform. It returns the concatenation of `path` and any members of `others`,
|
||||
inserting directory separators before each component except the first. The
|
||||
separator is not inserted if the path up until that point is either empty or
|
||||
already ends in a separator.
|
||||
|
||||
If any component is an absolute path, all previous components are discarded.
|
||||
|
||||
Args:
|
||||
path: A path segment.
|
||||
*others: Additional path segments.
|
||||
|
||||
Returns:
|
||||
A string containing the joined paths.
|
||||
"""
|
||||
result = path
|
||||
|
||||
for p in others:
|
||||
if _is_absolute(p):
|
||||
result = p
|
||||
elif not result or result.endswith("/"):
|
||||
result += p
|
||||
else:
|
||||
result += "/" + p
|
||||
|
||||
return result
|
||||
|
||||
def _normalize(path):
|
||||
"""Normalizes a path, eliminating double slashes and other redundant segments.
|
||||
|
||||
This function mimics the behavior of Python's `os.path.normpath` function on
|
||||
POSIX platforms; specifically:
|
||||
|
||||
- If the entire path is empty, "." is returned.
|
||||
- All "." segments are removed, unless the path consists solely of a single
|
||||
"." segment.
|
||||
- Trailing slashes are removed, unless the path consists solely of slashes.
|
||||
- ".." segments are removed as long as there are corresponding segments
|
||||
earlier in the path to remove; otherwise, they are retained as leading ".."
|
||||
segments.
|
||||
- Single and double leading slashes are preserved, but three or more leading
|
||||
slashes are collapsed into a single leading slash.
|
||||
- Multiple adjacent internal slashes are collapsed into a single slash.
|
||||
|
||||
Args:
|
||||
path: A path.
|
||||
|
||||
Returns:
|
||||
The normalized path.
|
||||
"""
|
||||
if not path:
|
||||
return "."
|
||||
|
||||
if path.startswith("//") and not path.startswith("///"):
|
||||
initial_slashes = 2
|
||||
elif path.startswith("/"):
|
||||
initial_slashes = 1
|
||||
else:
|
||||
initial_slashes = 0
|
||||
is_relative = (initial_slashes == 0)
|
||||
|
||||
components = path.split("/")
|
||||
new_components = []
|
||||
|
||||
for component in components:
|
||||
if component in ("", "."):
|
||||
continue
|
||||
if component == "..":
|
||||
if new_components and new_components[-1] != "..":
|
||||
# Only pop the last segment if it isn't another "..".
|
||||
new_components.pop()
|
||||
elif is_relative:
|
||||
# Preserve leading ".." segments for relative paths.
|
||||
new_components.append(component)
|
||||
else:
|
||||
new_components.append(component)
|
||||
|
||||
path = "/".join(new_components)
|
||||
if not is_relative:
|
||||
path = ("/" * initial_slashes) + path
|
||||
|
||||
return path or "."
|
||||
|
||||
def _relativize(path, start):
|
||||
"""Returns the portion of `path` that is relative to `start`.
|
||||
|
||||
Because we do not have access to the underlying file system, this
|
||||
implementation differs slightly from Python's `os.path.relpath` in that it
|
||||
will fail if `path` is not beneath `start` (rather than use parent segments to
|
||||
walk up to the common file system root).
|
||||
|
||||
Relativizing paths that start with parent directory references only works if
|
||||
the path both start with the same initial parent references.
|
||||
|
||||
Args:
|
||||
path: The path to relativize.
|
||||
start: The ancestor path against which to relativize.
|
||||
|
||||
Returns:
|
||||
The portion of `path` that is relative to `start`.
|
||||
"""
|
||||
segments = _normalize(path).split("/")
|
||||
start_segments = _normalize(start).split("/")
|
||||
if start_segments == ["."]:
|
||||
start_segments = []
|
||||
start_length = len(start_segments)
|
||||
|
||||
if (path.startswith("/") != start.startswith("/") or
|
||||
len(segments) < start_length):
|
||||
fail("Path '%s' is not beneath '%s'" % (path, start))
|
||||
|
||||
for ancestor_segment, segment in zip(start_segments, segments):
|
||||
if ancestor_segment != segment:
|
||||
fail("Path '%s' is not beneath '%s'" % (path, start))
|
||||
|
||||
length = len(segments) - start_length
|
||||
result_segments = segments[-length:]
|
||||
return "/".join(result_segments)
|
||||
|
||||
def _replace_extension(p, new_extension):
|
||||
"""Replaces the extension of the file at the end of a path.
|
||||
|
||||
If the path has no extension, the new extension is added to it.
|
||||
|
||||
Args:
|
||||
p: The path whose extension should be replaced.
|
||||
new_extension: The new extension for the file. The new extension should
|
||||
begin with a dot if you want the new filename to have one.
|
||||
|
||||
Returns:
|
||||
The path with the extension replaced (or added, if it did not have one).
|
||||
"""
|
||||
return _split_extension(p)[0] + new_extension
|
||||
|
||||
def _split_extension(p):
|
||||
"""Splits the path `p` into a tuple containing the root and extension.
|
||||
|
||||
Leading periods on the basename are ignored, so
|
||||
`path.split_extension(".bashrc")` returns `(".bashrc", "")`.
|
||||
|
||||
Args:
|
||||
p: The path whose root and extension should be split.
|
||||
|
||||
Returns:
|
||||
A tuple `(root, ext)` such that the root is the path without the file
|
||||
extension, and `ext` is the file extension (which, if non-empty, contains
|
||||
the leading dot). The returned tuple always satisfies the relationship
|
||||
`root + ext == p`.
|
||||
"""
|
||||
b = _basename(p)
|
||||
last_dot_in_basename = b.rfind(".")
|
||||
|
||||
# If there is no dot or the only dot in the basename is at the front, then
|
||||
# there is no extension.
|
||||
if last_dot_in_basename <= 0:
|
||||
return (p, "")
|
||||
|
||||
dot_distance_from_end = len(b) - last_dot_in_basename
|
||||
return (p[:-dot_distance_from_end], p[-dot_distance_from_end:])
|
||||
|
||||
paths = struct(
|
||||
basename = _basename,
|
||||
dirname = _dirname,
|
||||
is_absolute = _is_absolute,
|
||||
join = _join,
|
||||
normalize = _normalize,
|
||||
relativize = _relativize,
|
||||
replace_extension = _replace_extension,
|
||||
split_extension = _split_extension,
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
# Tests of the experimental 'lib/proto' module.
|
||||
|
||||
load("assert.star", "assert")
|
||||
load("proto.star", "proto")
|
||||
|
||||
schema = proto.file("google/protobuf/descriptor.proto")
|
||||
|
||||
m = schema.FileDescriptorProto(name = "somename.proto", dependency = ["a", "b", "c"])
|
||||
assert.eq(type(m), "proto.Message")
|
||||
assert.eq(m.name, "somename.proto")
|
||||
assert.eq(list(m.dependency), ["a", "b", "c"])
|
||||
m.dependency = ["d", "e"]
|
||||
assert.eq(list(m.dependency), ["d", "e"])
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
# Tests of Starlark recursion and while statement.
|
||||
|
||||
# This is a "chunked" file: each "---" effectively starts a new file.
|
||||
|
||||
# option:recursion
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def fib(n):
|
||||
if n <= 1:
|
||||
return 1
|
||||
return fib(n-1) + fib(n-2)
|
||||
|
||||
assert.eq(fib(5), 8)
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
# Tests of Starlark 'set'
|
||||
# option:set option:globalreassign
|
||||
|
||||
# Sets are not a standard part of Starlark, so the features
|
||||
# tested in this file must be enabled in the application by setting
|
||||
# resolve.AllowSet. (All sets are created by calls to the 'set'
|
||||
# built-in or derived from operations on existing sets.)
|
||||
# The semantics are subject to change as the spec evolves.
|
||||
|
||||
# TODO(adonovan): support set mutation:
|
||||
# - del set[k]
|
||||
# - set.update
|
||||
# - set += iterable, perhaps?
|
||||
# Test iterator invalidation.
|
||||
|
||||
load("assert.star", "assert", "freeze")
|
||||
|
||||
# literals
|
||||
# Parser does not currently support {1, 2, 3}.
|
||||
# TODO(adonovan): add test to syntax/testdata/errors.star.
|
||||
|
||||
# set comprehensions
|
||||
# Parser does not currently support {x for x in y}.
|
||||
# See syntax/testdata/errors.star.
|
||||
|
||||
# set constructor
|
||||
assert.eq(type(set()), "set")
|
||||
assert.eq(list(set()), [])
|
||||
assert.eq(type(set([1, 3, 2, 3])), "set")
|
||||
assert.eq(list(set([1, 3, 2, 3])), [1, 3, 2])
|
||||
assert.eq(type(set("hello".elems())), "set")
|
||||
assert.eq(list(set("hello".elems())), ["h", "e", "l", "o"])
|
||||
assert.eq(list(set(range(3))), [0, 1, 2])
|
||||
assert.fails(lambda : set(1), "got int, want iterable")
|
||||
assert.fails(lambda : set(1, 2, 3), "got 3 arguments")
|
||||
assert.fails(lambda : set([1, 2, {}]), "unhashable type: dict")
|
||||
|
||||
# truth
|
||||
assert.true(not set())
|
||||
assert.true(set([False]))
|
||||
assert.true(set([1, 2, 3]))
|
||||
|
||||
x = set([1, 2, 3])
|
||||
y = set([3, 4, 5])
|
||||
|
||||
# set + any is not defined
|
||||
assert.fails(lambda : x + y, "unknown.*: set \\+ set")
|
||||
|
||||
# set | set
|
||||
assert.eq(list(set("a".elems()) | set("b".elems())), ["a", "b"])
|
||||
assert.eq(list(set("ab".elems()) | set("bc".elems())), ["a", "b", "c"])
|
||||
assert.fails(lambda : set() | [], "unknown binary op: set | list")
|
||||
assert.eq(type(x | y), "set")
|
||||
assert.eq(list(x | y), [1, 2, 3, 4, 5])
|
||||
assert.eq(list(x | set([5, 1])), [1, 2, 3, 5])
|
||||
assert.eq(list(x | set((6, 5, 4))), [1, 2, 3, 6, 5, 4])
|
||||
|
||||
# set.union (allows any iterable for right operand)
|
||||
assert.eq(list(set("a".elems()).union("b".elems())), ["a", "b"])
|
||||
assert.eq(list(set("ab".elems()).union("bc".elems())), ["a", "b", "c"])
|
||||
assert.eq(set().union([]), set())
|
||||
assert.eq(type(x.union(y)), "set")
|
||||
assert.eq(list(x.union(y)), [1, 2, 3, 4, 5])
|
||||
assert.eq(list(x.union([5, 1])), [1, 2, 3, 5])
|
||||
assert.eq(list(x.union((6, 5, 4))), [1, 2, 3, 6, 5, 4])
|
||||
assert.fails(lambda : x.union([1, 2, {}]), "unhashable type: dict")
|
||||
|
||||
# intersection, set & set or set.intersection(iterable)
|
||||
assert.eq(list(set("a".elems()) & set("b".elems())), [])
|
||||
assert.eq(list(set("ab".elems()) & set("bc".elems())), ["b"])
|
||||
assert.eq(list(set("a".elems()).intersection("b".elems())), [])
|
||||
assert.eq(list(set("ab".elems()).intersection("bc".elems())), ["b"])
|
||||
|
||||
# symmetric difference, set ^ set or set.symmetric_difference(iterable)
|
||||
assert.eq(set([1, 2, 3]) ^ set([4, 5, 3]), set([1, 2, 4, 5]))
|
||||
assert.eq(set([1,2,3,4]).symmetric_difference([3,4,5,6]), set([1,2,5,6]))
|
||||
assert.eq(set([1,2,3,4]).symmetric_difference(set([])), set([1,2,3,4]))
|
||||
|
||||
def test_set_augmented_assign():
|
||||
x = set([1, 2, 3])
|
||||
x &= set([2, 3])
|
||||
assert.eq(x, set([2, 3]))
|
||||
x |= set([1])
|
||||
assert.eq(x, set([1, 2, 3]))
|
||||
x ^= set([4, 5, 3])
|
||||
assert.eq(x, set([1, 2, 4, 5]))
|
||||
|
||||
test_set_augmented_assign()
|
||||
|
||||
# len
|
||||
assert.eq(len(x), 3)
|
||||
assert.eq(len(y), 3)
|
||||
assert.eq(len(x | y), 5)
|
||||
|
||||
# str
|
||||
assert.eq(str(set([1])), "set([1])")
|
||||
assert.eq(str(set([2, 3])), "set([2, 3])")
|
||||
assert.eq(str(set([3, 2])), "set([3, 2])")
|
||||
|
||||
# comparison
|
||||
assert.eq(x, x)
|
||||
assert.eq(y, y)
|
||||
assert.true(x != y)
|
||||
assert.eq(set([1, 2, 3]), set([3, 2, 1]))
|
||||
|
||||
# iteration
|
||||
assert.true(type([elem for elem in x]), "list")
|
||||
assert.true(list([elem for elem in x]), [1, 2, 3])
|
||||
|
||||
def iter():
|
||||
list = []
|
||||
for elem in x:
|
||||
list.append(elem)
|
||||
return list
|
||||
|
||||
assert.eq(iter(), [1, 2, 3])
|
||||
|
||||
# sets are not indexable
|
||||
assert.fails(lambda : x[0], "unhandled.*operation")
|
||||
|
||||
# adding and removing
|
||||
add_set = set([1,2,3])
|
||||
add_set.add(4)
|
||||
assert.true(4 in add_set)
|
||||
freeze(add_set) # no mutation of frozen set because key already present
|
||||
add_set.add(4)
|
||||
assert.fails(lambda: add_set.add(5), "add: cannot insert into frozen hash table")
|
||||
|
||||
# remove
|
||||
remove_set = set([1,2,3])
|
||||
remove_set.remove(3)
|
||||
assert.true(3 not in remove_set)
|
||||
assert.fails(lambda: remove_set.remove(3), "remove: missing key")
|
||||
freeze(remove_set)
|
||||
assert.fails(lambda: remove_set.remove(3), "remove: cannot delete from frozen hash table")
|
||||
|
||||
# discard
|
||||
discard_set = set([1,2,3])
|
||||
discard_set.discard(3)
|
||||
assert.true(3 not in discard_set)
|
||||
assert.eq(discard_set.discard(3), None)
|
||||
freeze(discard_set)
|
||||
assert.eq(discard_set.discard(3), None) # no mutation of frozen set because key doesn't exist
|
||||
assert.fails(lambda: discard_set.discard(1), "discard: cannot delete from frozen hash table")
|
||||
|
||||
|
||||
# pop
|
||||
pop_set = set([1,2,3])
|
||||
assert.eq(pop_set.pop(), 1)
|
||||
assert.eq(pop_set.pop(), 2)
|
||||
assert.eq(pop_set.pop(), 3)
|
||||
assert.fails(lambda: pop_set.pop(), "pop: empty set")
|
||||
pop_set.add(1)
|
||||
pop_set.add(2)
|
||||
freeze(pop_set)
|
||||
assert.fails(lambda: pop_set.pop(), "pop: cannot delete from frozen hash table")
|
||||
|
||||
# clear
|
||||
clear_set = set([1,2,3])
|
||||
clear_set.clear()
|
||||
assert.eq(len(clear_set), 0)
|
||||
freeze(clear_set) # no mutation of frozen set because its already empty
|
||||
assert.eq(clear_set.clear(), None)
|
||||
|
||||
other_clear_set = set([1,2,3])
|
||||
freeze(other_clear_set)
|
||||
assert.fails(lambda: other_clear_set.clear(), "clear: cannot clear frozen hash table")
|
||||
|
||||
# difference: set - set or set.difference(iterable)
|
||||
assert.eq(set([1,2,3,4]).difference([1,2,3,4]), set([]))
|
||||
assert.eq(set([1,2,3,4]).difference([1,2]), set([3,4]))
|
||||
assert.eq(set([1,2,3,4]).difference([]), set([1,2,3,4]))
|
||||
assert.eq(set([1,2,3,4]).difference(set([1,2,3])), set([4]))
|
||||
|
||||
assert.eq(set([1,2,3,4]) - set([1,2,3,4]), set())
|
||||
assert.eq(set([1,2,3,4]) - set([1,2]), set([3,4]))
|
||||
|
||||
# issuperset: set >= set or set.issuperset(iterable)
|
||||
assert.true(set([1,2,3]).issuperset([1,2]))
|
||||
assert.true(not set([1,2,3]).issuperset(set([1,2,4])))
|
||||
assert.true(set([1,2,3]) >= set([1,2,3]))
|
||||
assert.true(set([1,2,3]) >= set([1,2]))
|
||||
assert.true(not set([1,2,3]) >= set([1,2,4]))
|
||||
|
||||
# proper superset: set > set
|
||||
assert.true(set([1, 2, 3]) > set([1, 2]))
|
||||
assert.true(not set([1,2, 3]) > set([1, 2, 3]))
|
||||
|
||||
# issubset: set <= set or set.issubset(iterable)
|
||||
assert.true(set([1,2]).issubset([1,2,3]))
|
||||
assert.true(not set([1,2,3]).issubset(set([1,2,4])))
|
||||
assert.true(set([1,2,3]) <= set([1,2,3]))
|
||||
assert.true(set([1,2]) <= set([1,2,3]))
|
||||
assert.true(not set([1,2,3]) <= set([1,2,4]))
|
||||
|
||||
# proper subset: set < set
|
||||
assert.true(set([1,2]) < set([1,2,3]))
|
||||
assert.true(not set([1,2,3]) < set([1,2,3]))
|
||||
Vendored
+493
@@ -0,0 +1,493 @@
|
||||
# Tests of Starlark 'string'
|
||||
# option:set
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# raw string literals:
|
||||
assert.eq(r"a\bc", "a\\bc")
|
||||
|
||||
# truth
|
||||
assert.true("abc")
|
||||
assert.true(chr(0))
|
||||
assert.true(not "")
|
||||
|
||||
# str + str
|
||||
assert.eq("a" + "b" + "c", "abc")
|
||||
|
||||
# str * int, int * str
|
||||
assert.eq("abc" * 0, "")
|
||||
assert.eq("abc" * -1, "")
|
||||
assert.eq("abc" * 1, "abc")
|
||||
assert.eq("abc" * 5, "abcabcabcabcabc")
|
||||
assert.eq(0 * "abc", "")
|
||||
assert.eq(-1 * "abc", "")
|
||||
assert.eq(1 * "abc", "abc")
|
||||
assert.eq(5 * "abc", "abcabcabcabcabc")
|
||||
assert.fails(lambda: 1.0 * "abc", "unknown.*float \\* str")
|
||||
assert.fails(lambda: "abc" * (1000000 * 1000000), "repeat count 1000000000000 too large")
|
||||
assert.fails(lambda: "abc" * 1000000 * 1000000, "excessive repeat \\(3000000 \\* 1000000 elements")
|
||||
|
||||
# len
|
||||
assert.eq(len("Hello, 世界!"), 14)
|
||||
assert.eq(len("𐐷"), 4) # U+10437 has a 4-byte UTF-8 encoding (and a 2-code UTF-16 encoding)
|
||||
|
||||
# chr & ord
|
||||
assert.eq(chr(65), "A") # 1-byte UTF-8 encoding
|
||||
assert.eq(chr(1049), "Й") # 2-byte UTF-8 encoding
|
||||
assert.eq(chr(0x1F63F), "😿") # 4-byte UTF-8 encoding
|
||||
assert.fails(lambda: chr(-1), "Unicode code point -1 out of range \\(<0\\)")
|
||||
assert.fails(lambda: chr(0x110000), "Unicode code point U\\+110000 out of range \\(>0x10FFFF\\)")
|
||||
assert.eq(ord("A"), 0x41)
|
||||
assert.eq(ord("Й"), 0x419)
|
||||
assert.eq(ord("世"), 0x4e16)
|
||||
assert.eq(ord("😿"), 0x1F63F)
|
||||
assert.eq(ord("Й"[1:]), 0xFFFD) # = Unicode replacement character
|
||||
assert.fails(lambda: ord("abc"), "string encodes 3 Unicode code points, want 1")
|
||||
assert.fails(lambda: ord(""), "string encodes 0 Unicode code points, want 1")
|
||||
assert.fails(lambda: ord("😿"[1:]), "string encodes 3 Unicode code points, want 1") # 3 x 0xFFFD
|
||||
|
||||
# string.codepoint_ords
|
||||
assert.eq(type("abcЙ😿".codepoint_ords()), "string.codepoints")
|
||||
assert.eq(str("abcЙ😿".codepoint_ords()), '"abcЙ😿".codepoint_ords()')
|
||||
assert.eq(list("abcЙ😿".codepoint_ords()), [97, 98, 99, 1049, 128575])
|
||||
assert.eq(list(("A" + "😿Z"[1:]).codepoint_ords()), [ord("A"), 0xFFFD, 0xFFFD, 0xFFFD, ord("Z")])
|
||||
assert.eq(list("".codepoint_ords()), [])
|
||||
assert.fails(lambda: "abcЙ😿".codepoint_ords()[2], "unhandled index") # not indexable
|
||||
assert.fails(lambda: len("abcЙ😿".codepoint_ords()), "no len") # unknown length
|
||||
|
||||
# string.codepoints
|
||||
assert.eq(type("abcЙ😿".codepoints()), "string.codepoints")
|
||||
assert.eq(str("abcЙ😿".codepoints()), '"abcЙ😿".codepoints()')
|
||||
assert.eq(list("abcЙ😿".codepoints()), ["a", "b", "c", "Й", "😿"])
|
||||
assert.eq(list(("A" + "😿Z"[1:]).codepoints()), ["A", "�", "�", "�", "Z"])
|
||||
assert.eq(list("".codepoints()), [])
|
||||
assert.fails(lambda: "abcЙ😿".codepoints()[2], "unhandled index") # not indexable
|
||||
assert.fails(lambda: len("abcЙ😿".codepoints()), "no len") # unknown length
|
||||
|
||||
# string.elem_ords
|
||||
assert.eq(type("abcЙ😿".elem_ords()), "string.elems")
|
||||
assert.eq(str("abcЙ😿".elem_ords()), '"abcЙ😿".elem_ords()')
|
||||
assert.eq(list("abcЙ😿".elem_ords()), [97, 98, 99, 208, 153, 240, 159, 152, 191])
|
||||
assert.eq(list(("A" + "😿Z"[1:]).elem_ords()), [65, 159, 152, 191, 90])
|
||||
assert.eq(list("".elem_ords()), [])
|
||||
assert.eq("abcЙ😿".elem_ords()[2], 99) # indexable
|
||||
assert.eq(len("abcЙ😿".elem_ords()), 9) # known length
|
||||
|
||||
# string.elems (1-byte substrings, which are invalid text)
|
||||
assert.eq(type("abcЙ😿".elems()), "string.elems")
|
||||
assert.eq(str("abcЙ😿".elems()), '"abcЙ😿".elems()')
|
||||
assert.eq(
|
||||
repr(list("abcЙ😿".elems())),
|
||||
r'["a", "b", "c", "\xd0", "\x99", "\xf0", "\x9f", "\x98", "\xbf"]',
|
||||
)
|
||||
assert.eq(
|
||||
repr(list(("A" + "😿Z"[1:]).elems())),
|
||||
r'["A", "\x9f", "\x98", "\xbf", "Z"]',
|
||||
)
|
||||
assert.eq(list("".elems()), [])
|
||||
assert.eq("abcЙ😿".elems()[2], "c") # indexable
|
||||
assert.eq(len("abcЙ😿".elems()), 9) # known length
|
||||
|
||||
# indexing, x[i]
|
||||
assert.eq("Hello, 世界!"[0], "H")
|
||||
assert.eq(repr("Hello, 世界!"[7]), r'"\xe4"') # (invalid text)
|
||||
assert.eq("Hello, 世界!"[13], "!")
|
||||
assert.fails(lambda: "abc"[-4], "out of range")
|
||||
assert.eq("abc"[-3], "a")
|
||||
assert.eq("abc"[-2], "b")
|
||||
assert.eq("abc"[-1], "c")
|
||||
assert.eq("abc"[0], "a")
|
||||
assert.eq("abc"[1], "b")
|
||||
assert.eq("abc"[2], "c")
|
||||
assert.fails(lambda: "abc"[4], "out of range")
|
||||
|
||||
# x[i] = ...
|
||||
def f():
|
||||
"abc"[1] = "B"
|
||||
|
||||
assert.fails(f, "string.*does not support.*assignment")
|
||||
|
||||
# slicing, x[i:j]
|
||||
assert.eq("abc"[:], "abc")
|
||||
assert.eq("abc"[-4:], "abc")
|
||||
assert.eq("abc"[-3:], "abc")
|
||||
assert.eq("abc"[-2:], "bc")
|
||||
assert.eq("abc"[-1:], "c")
|
||||
assert.eq("abc"[0:], "abc")
|
||||
assert.eq("abc"[1:], "bc")
|
||||
assert.eq("abc"[2:], "c")
|
||||
assert.eq("abc"[3:], "")
|
||||
assert.eq("abc"[4:], "")
|
||||
assert.eq("abc"[:-4], "")
|
||||
assert.eq("abc"[:-3], "")
|
||||
assert.eq("abc"[:-2], "a")
|
||||
assert.eq("abc"[:-1], "ab")
|
||||
assert.eq("abc"[:0], "")
|
||||
assert.eq("abc"[:1], "a")
|
||||
assert.eq("abc"[:2], "ab")
|
||||
assert.eq("abc"[:3], "abc")
|
||||
assert.eq("abc"[:4], "abc")
|
||||
assert.eq("abc"[1:2], "b")
|
||||
assert.eq("abc"[2:1], "")
|
||||
assert.eq(repr("😿"[:1]), r'"\xf0"') # (invalid text)
|
||||
|
||||
# non-unit strides
|
||||
assert.eq("abcd"[0:4:1], "abcd")
|
||||
assert.eq("abcd"[::2], "ac")
|
||||
assert.eq("abcd"[1::2], "bd")
|
||||
assert.eq("abcd"[4:0:-1], "dcb")
|
||||
assert.eq("banana"[7::-2], "aaa")
|
||||
assert.eq("banana"[6::-2], "aaa")
|
||||
assert.eq("banana"[5::-2], "aaa")
|
||||
assert.eq("banana"[4::-2], "nnb")
|
||||
assert.eq("banana"[::-1], "ananab")
|
||||
assert.eq("banana"[None:None:-2], "aaa")
|
||||
assert.fails(lambda: "banana"[1.0::], "invalid start index: got float, want int")
|
||||
assert.fails(lambda: "banana"[:"":], "invalid end index: got string, want int")
|
||||
assert.fails(lambda: "banana"[:"":True], "invalid slice step: got bool, want int")
|
||||
|
||||
# in, not in
|
||||
assert.true("oo" in "food")
|
||||
assert.true("ox" not in "food")
|
||||
assert.true("" in "food")
|
||||
assert.true("" in "")
|
||||
assert.fails(lambda: 1 in "", "requires string as left operand")
|
||||
assert.fails(lambda: "" in 1, "unknown binary op: string in int")
|
||||
|
||||
# ==, !=
|
||||
assert.eq("hello", "he" + "llo")
|
||||
assert.ne("hello", "Hello")
|
||||
|
||||
# hash must follow java.lang.String.hashCode.
|
||||
wanthash = {
|
||||
"": 0,
|
||||
"\0" * 100: 0,
|
||||
"hello": 99162322,
|
||||
"world": 113318802,
|
||||
"Hello, 世界!": 417292677,
|
||||
}
|
||||
gothash = {s: hash(s) for s in wanthash}
|
||||
assert.eq(gothash, wanthash)
|
||||
|
||||
# TODO(adonovan): ordered comparisons
|
||||
|
||||
# string % tuple formatting
|
||||
assert.eq("A %d %x Z" % (123, 456), "A 123 1c8 Z")
|
||||
assert.eq("A %(foo)d %(bar)s Z" % {"foo": 123, "bar": "hi"}, "A 123 hi Z")
|
||||
assert.eq("%s %r" % ("hi", "hi"), 'hi "hi"') # TODO(adonovan): use ''-quotation
|
||||
assert.eq("%%d %d" % 1, "%d 1")
|
||||
assert.fails(lambda: "%d %d" % 1, "not enough arguments for format string")
|
||||
assert.fails(lambda: "%d %d" % (1, 2, 3), "too many arguments for format string")
|
||||
assert.fails(lambda: "" % 1, "too many arguments for format string")
|
||||
|
||||
# %c
|
||||
assert.eq("%c" % 65, "A")
|
||||
assert.eq("%c" % 0x3b1, "α")
|
||||
assert.eq("%c" % "A", "A")
|
||||
assert.eq("%c" % "α", "α")
|
||||
assert.fails(lambda: "%c" % "abc", "requires a single-character string")
|
||||
assert.fails(lambda: "%c" % "", "requires a single-character string")
|
||||
assert.fails(lambda: "%c" % 65.0, "requires int or single-character string")
|
||||
assert.fails(lambda: "%c" % 10000000, "requires a valid Unicode code point")
|
||||
assert.fails(lambda: "%c" % -1, "requires a valid Unicode code point")
|
||||
# TODO(adonovan): more tests
|
||||
|
||||
# str.format
|
||||
assert.eq("a{}b".format(123), "a123b")
|
||||
assert.eq("a{}b{}c{}d{}".format(1, 2, 3, 4), "a1b2c3d4")
|
||||
assert.eq("a{{b".format(), "a{b")
|
||||
assert.eq("a}}b".format(), "a}b")
|
||||
assert.eq("a{{b}}c".format(), "a{b}c")
|
||||
assert.eq("a{x}b{y}c{}".format(1, x = 2, y = 3), "a2b3c1")
|
||||
assert.fails(lambda: "a{z}b".format(x = 1), "keyword z not found")
|
||||
assert.fails(lambda: "{-1}".format(1), "keyword -1 not found")
|
||||
assert.fails(lambda: "{-0}".format(1), "keyword -0 not found")
|
||||
assert.fails(lambda: "{+0}".format(1), "keyword \\+0 not found")
|
||||
assert.fails(lambda: "{+1}".format(1), "keyword \\+1 not found") # starlark-go/issues/114
|
||||
assert.eq("{0000000000001}".format(0, 1), "1")
|
||||
assert.eq("{012}".format(*range(100)), "12") # decimal, despite leading zeros
|
||||
assert.fails(lambda: "{0,1} and {1}".format(1, 2), "keyword 0,1 not found")
|
||||
assert.fails(lambda: "a{123}b".format(), "tuple index out of range")
|
||||
assert.fails(lambda: "a{}b{}c".format(1), "tuple index out of range")
|
||||
assert.eq("a{010}b".format(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), "a10b") # index is decimal
|
||||
assert.fails(lambda: "a{}b{1}c".format(1, 2), "cannot switch from automatic field numbering to manual")
|
||||
assert.eq("a{!s}c".format("b"), "abc")
|
||||
assert.eq("a{!r}c".format("b"), r'a"b"c')
|
||||
assert.eq("a{x!r}c".format(x = "b"), r'a"b"c')
|
||||
assert.fails(lambda: "{x!}".format(x = 1), "unknown conversion")
|
||||
assert.fails(lambda: "{x!:}".format(x = 1), "unknown conversion")
|
||||
assert.fails(lambda: "{a.b}".format(1), "syntax x.y is not supported")
|
||||
assert.fails(lambda: "{a[0]}".format(1), "syntax a\\[i\\] is not supported")
|
||||
assert.fails(lambda: "{ {} }".format(1), "nested replacement fields not supported")
|
||||
assert.fails(lambda: "{{}".format(1), "single '}' in format")
|
||||
assert.fails(lambda: "{}}".format(1), "single '}' in format")
|
||||
assert.fails(lambda: "}}{".format(1), "unmatched '{' in format")
|
||||
assert.fails(lambda: "}{{".format(1), "single '}' in format")
|
||||
|
||||
# str.split, str.rsplit
|
||||
assert.eq("a.b.c.d".split("."), ["a", "b", "c", "d"])
|
||||
assert.eq("a.b.c.d".rsplit("."), ["a", "b", "c", "d"])
|
||||
assert.eq("a.b.c.d".split(".", -1), ["a", "b", "c", "d"])
|
||||
assert.eq("a.b.c.d".rsplit(".", -1), ["a", "b", "c", "d"])
|
||||
assert.eq("a.b.c.d".split(".", 0), ["a.b.c.d"])
|
||||
assert.eq("a.b.c.d".rsplit(".", 0), ["a.b.c.d"])
|
||||
assert.eq("a.b.c.d".split(".", 1), ["a", "b.c.d"])
|
||||
assert.eq("a.b.c.d".rsplit(".", 1), ["a.b.c", "d"])
|
||||
assert.eq("a.b.c.d".split(".", 2), ["a", "b", "c.d"])
|
||||
assert.eq("a.b.c.d".rsplit(".", 2), ["a.b", "c", "d"])
|
||||
assert.eq(" ".split("."), [" "])
|
||||
assert.eq(" ".rsplit("."), [" "])
|
||||
|
||||
# {,r}split on white space:
|
||||
assert.eq(" a bc\n def \t ghi".split(), ["a", "bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".split(None), ["a", "bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".split(None, 0), ["a bc\n def \t ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".rsplit(None, 0), [" a bc\n def \t ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".split(None, 1), ["a", "bc\n def \t ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".rsplit(None, 1), [" a bc\n def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".split(None, 2), ["a", "bc", "def \t ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".rsplit(None, 2), [" a bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".split(None, 3), ["a", "bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".rsplit(None, 3), [" a", "bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".split(None, 4), ["a", "bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".rsplit(None, 4), ["a", "bc", "def", "ghi"])
|
||||
assert.eq(" a bc\n def \t ghi".rsplit(None, 5), ["a", "bc", "def", "ghi"])
|
||||
|
||||
assert.eq(" a bc\n def \t ghi ".split(None, 0), ["a bc\n def \t ghi "])
|
||||
assert.eq(" a bc\n def \t ghi ".rsplit(None, 0), [" a bc\n def \t ghi"])
|
||||
assert.eq(" a bc\n def \t ghi ".split(None, 1), ["a", "bc\n def \t ghi "])
|
||||
assert.eq(" a bc\n def \t ghi ".rsplit(None, 1), [" a bc\n def", "ghi"])
|
||||
|
||||
# Observe the algorithmic difference when splitting on spaces versus other delimiters.
|
||||
assert.eq("--aa--bb--cc--".split("-", 0), ["--aa--bb--cc--"]) # contrast this
|
||||
assert.eq(" aa bb cc ".split(None, 0), ["aa bb cc "]) # with this
|
||||
assert.eq("--aa--bb--cc--".rsplit("-", 0), ["--aa--bb--cc--"]) # ditto this
|
||||
assert.eq(" aa bb cc ".rsplit(None, 0), [" aa bb cc"]) # and this
|
||||
|
||||
#
|
||||
assert.eq("--aa--bb--cc--".split("-", 1), ["", "-aa--bb--cc--"])
|
||||
assert.eq("--aa--bb--cc--".rsplit("-", 1), ["--aa--bb--cc-", ""])
|
||||
assert.eq(" aa bb cc ".split(None, 1), ["aa", "bb cc "])
|
||||
assert.eq(" aa bb cc ".rsplit(None, 1), [" aa bb", "cc"])
|
||||
|
||||
#
|
||||
assert.eq("--aa--bb--cc--".split("-", -1), ["", "", "aa", "", "bb", "", "cc", "", ""])
|
||||
assert.eq("--aa--bb--cc--".rsplit("-", -1), ["", "", "aa", "", "bb", "", "cc", "", ""])
|
||||
assert.eq(" aa bb cc ".split(None, -1), ["aa", "bb", "cc"])
|
||||
assert.eq(" aa bb cc ".rsplit(None, -1), ["aa", "bb", "cc"])
|
||||
assert.eq(" ".split(None), [])
|
||||
assert.eq(" ".rsplit(None), [])
|
||||
|
||||
assert.eq("localhost:80".rsplit(":", 1)[-1], "80")
|
||||
|
||||
# str.splitlines
|
||||
assert.eq("\nabc\ndef".splitlines(), ["", "abc", "def"])
|
||||
assert.eq("\nabc\ndef".splitlines(True), ["\n", "abc\n", "def"])
|
||||
assert.eq("\nabc\ndef\n".splitlines(), ["", "abc", "def"])
|
||||
assert.eq("\nabc\ndef\n".splitlines(True), ["\n", "abc\n", "def\n"])
|
||||
assert.eq("".splitlines(), []) #
|
||||
assert.eq("".splitlines(True), []) #
|
||||
assert.eq("a".splitlines(), ["a"])
|
||||
assert.eq("a".splitlines(True), ["a"])
|
||||
assert.eq("\n".splitlines(), [""])
|
||||
assert.eq("\n".splitlines(True), ["\n"])
|
||||
assert.eq("a\n".splitlines(), ["a"])
|
||||
assert.eq("a\n".splitlines(True), ["a\n"])
|
||||
assert.eq("a\n\nb".splitlines(), ["a", "", "b"])
|
||||
assert.eq("a\n\nb".splitlines(True), ["a\n", "\n", "b"])
|
||||
assert.eq("a\nb\nc".splitlines(), ["a", "b", "c"])
|
||||
assert.eq("a\nb\nc".splitlines(True), ["a\n", "b\n", "c"])
|
||||
assert.eq("a\nb\nc\n".splitlines(), ["a", "b", "c"])
|
||||
assert.eq("a\nb\nc\n".splitlines(True), ["a\n", "b\n", "c\n"])
|
||||
|
||||
# str.{,l,r}strip
|
||||
assert.eq(" \tfoo\n ".strip(), "foo")
|
||||
assert.eq(" \tfoo\n ".lstrip(), "foo\n ")
|
||||
assert.eq(" \tfoo\n ".rstrip(), " \tfoo")
|
||||
assert.eq(" \tfoo\n ".strip(""), "foo")
|
||||
assert.eq(" \tfoo\n ".lstrip(""), "foo\n ")
|
||||
assert.eq(" \tfoo\n ".rstrip(""), " \tfoo")
|
||||
assert.eq("blah.h".strip("b.h"), "la")
|
||||
assert.eq("blah.h".lstrip("b.h"), "lah.h")
|
||||
assert.eq("blah.h".rstrip("b.h"), "bla")
|
||||
|
||||
# str.count
|
||||
assert.eq("banana".count("a"), 3)
|
||||
assert.eq("banana".count("a", 2), 2)
|
||||
assert.eq("banana".count("a", -4, -2), 1)
|
||||
assert.eq("banana".count("a", 1, 4), 2)
|
||||
assert.eq("banana".count("a", 0, -100), 0)
|
||||
|
||||
# str.{starts,ends}with
|
||||
assert.true("foo".endswith("oo"))
|
||||
assert.true(not "foo".endswith("x"))
|
||||
assert.true("foo".startswith("fo"))
|
||||
assert.true(not "foo".startswith("x"))
|
||||
assert.fails(lambda: "foo".startswith(1), "got int.*want string")
|
||||
|
||||
#
|
||||
assert.true("abc".startswith(("a", "A")))
|
||||
assert.true("ABC".startswith(("a", "A")))
|
||||
assert.true(not "ABC".startswith(("b", "B")))
|
||||
assert.fails(lambda: "123".startswith((1, 2)), "got int, for element 0")
|
||||
assert.fails(lambda: "123".startswith(["3"]), "got list")
|
||||
|
||||
#
|
||||
assert.true("abc".endswith(("c", "C")))
|
||||
assert.true("ABC".endswith(("c", "C")))
|
||||
assert.true(not "ABC".endswith(("b", "B")))
|
||||
assert.fails(lambda: "123".endswith((1, 2)), "got int, for element 0")
|
||||
assert.fails(lambda: "123".endswith(["3"]), "got list")
|
||||
|
||||
# start/end
|
||||
assert.true("abc".startswith("bc", 1))
|
||||
assert.true(not "abc".startswith("b", 999))
|
||||
assert.true("abc".endswith("ab", None, -1))
|
||||
assert.true(not "abc".endswith("b", None, -999))
|
||||
|
||||
# str.replace
|
||||
assert.eq("banana".replace("a", "o", 1), "bonana")
|
||||
assert.eq("banana".replace("a", "o"), "bonono")
|
||||
# TODO(adonovan): more tests
|
||||
|
||||
# str.{,r}find
|
||||
assert.eq("foofoo".find("oo"), 1)
|
||||
assert.eq("foofoo".find("ox"), -1)
|
||||
assert.eq("foofoo".find("oo", 2), 4)
|
||||
assert.eq("foofoo".rfind("oo"), 4)
|
||||
assert.eq("foofoo".rfind("ox"), -1)
|
||||
assert.eq("foofoo".rfind("oo", 1, 4), 1)
|
||||
assert.eq("foofoo".find(""), 0)
|
||||
assert.eq("foofoo".rfind(""), 6)
|
||||
|
||||
# str.{,r}partition
|
||||
assert.eq("foo/bar/wiz".partition("/"), ("foo", "/", "bar/wiz"))
|
||||
assert.eq("foo/bar/wiz".rpartition("/"), ("foo/bar", "/", "wiz"))
|
||||
assert.eq("foo/bar/wiz".partition("."), ("foo/bar/wiz", "", ""))
|
||||
assert.eq("foo/bar/wiz".rpartition("."), ("", "", "foo/bar/wiz"))
|
||||
assert.fails(lambda: "foo/bar/wiz".partition(""), "empty separator")
|
||||
assert.fails(lambda: "foo/bar/wiz".rpartition(""), "empty separator")
|
||||
|
||||
assert.eq("?".join(["foo", "a/b/c.go".rpartition("/")[0]]), "foo?a/b")
|
||||
|
||||
# str.is{alpha,...}
|
||||
def test_predicates():
|
||||
predicates = ["alnum", "alpha", "digit", "lower", "space", "title", "upper"]
|
||||
table = {
|
||||
"Hello, World!": "title",
|
||||
"hello, world!": "lower",
|
||||
"base64": "alnum lower",
|
||||
"HAL-9000": "upper",
|
||||
"Catch-22": "title",
|
||||
"": "",
|
||||
"\n\t\r": "space",
|
||||
"abc": "alnum alpha lower",
|
||||
"ABC": "alnum alpha upper",
|
||||
"123": "alnum digit",
|
||||
"DŽLJ": "alnum alpha upper",
|
||||
"DžLj": "alnum alpha",
|
||||
"Dž Lj": "title",
|
||||
"džlj": "alnum alpha lower",
|
||||
}
|
||||
for str, want in table.items():
|
||||
got = " ".join([name for name in predicates if getattr(str, "is" + name)()])
|
||||
if got != want:
|
||||
assert.fail("%r matched [%s], want [%s]" % (str, got, want))
|
||||
|
||||
test_predicates()
|
||||
|
||||
# Strings are not iterable.
|
||||
# ok
|
||||
assert.eq(len("abc"), 3) # len
|
||||
assert.true("a" in "abc") # str in str
|
||||
assert.eq("abc"[1], "b") # indexing
|
||||
|
||||
# not ok
|
||||
def for_string():
|
||||
for x in "abc":
|
||||
pass
|
||||
|
||||
def args(*args):
|
||||
return args
|
||||
|
||||
assert.fails(lambda: args(*"abc"), "must be iterable, not string") # varargs
|
||||
assert.fails(lambda: list("abc"), "got string, want iterable") # list(str)
|
||||
assert.fails(lambda: tuple("abc"), "got string, want iterable") # tuple(str)
|
||||
assert.fails(lambda: set("abc"), "got string, want iterable") # set(str)
|
||||
assert.fails(lambda: set() | "abc", "unknown binary op: set | string") # set union
|
||||
assert.fails(lambda: enumerate("ab"), "got string, want iterable") # enumerate
|
||||
assert.fails(lambda: sorted("abc"), "got string, want iterable") # sorted
|
||||
assert.fails(lambda: [].extend("bc"), "got string, want iterable") # list.extend
|
||||
assert.fails(lambda: ",".join("abc"), "got string, want iterable") # string.join
|
||||
assert.fails(lambda: dict(["ab"]), "not iterable .*string") # dict
|
||||
assert.fails(for_string, "string value is not iterable") # for loop
|
||||
assert.fails(lambda: [x for x in "abc"], "string value is not iterable") # comprehension
|
||||
assert.fails(lambda: all("abc"), "got string, want iterable") # all
|
||||
assert.fails(lambda: any("abc"), "got string, want iterable") # any
|
||||
assert.fails(lambda: reversed("abc"), "got string, want iterable") # reversed
|
||||
assert.fails(lambda: zip("ab", "cd"), "not iterable: string") # zip
|
||||
|
||||
# str.join
|
||||
assert.eq(",".join([]), "")
|
||||
assert.eq(",".join(["a"]), "a")
|
||||
assert.eq(",".join(["a", "b"]), "a,b")
|
||||
assert.eq(",".join(["a", "b", "c"]), "a,b,c")
|
||||
assert.eq(",".join(("a", "b", "c")), "a,b,c")
|
||||
assert.eq("".join(("a", "b", "c")), "abc")
|
||||
assert.fails(lambda: "".join(None), "got NoneType, want iterable")
|
||||
assert.fails(lambda: "".join(["one", 2]), "join: in list, want string, got int")
|
||||
|
||||
# TODO(adonovan): tests for: {,r}index
|
||||
|
||||
# str.capitalize
|
||||
assert.eq("hElLo, WoRlD!".capitalize(), "Hello, world!")
|
||||
assert.eq("por qué".capitalize(), "Por qué")
|
||||
assert.eq("¿Por qué?".capitalize(), "¿por qué?")
|
||||
|
||||
# str.lower
|
||||
assert.eq("hElLo, WoRlD!".lower(), "hello, world!")
|
||||
assert.eq("por qué".lower(), "por qué")
|
||||
assert.eq("¿Por qué?".lower(), "¿por qué?")
|
||||
assert.eq("LJUBOVIĆ".lower(), "ljubović")
|
||||
assert.true("dženan ljubović".islower())
|
||||
|
||||
# str.upper
|
||||
assert.eq("hElLo, WoRlD!".upper(), "HELLO, WORLD!")
|
||||
assert.eq("por qué".upper(), "POR QUÉ")
|
||||
assert.eq("¿Por qué?".upper(), "¿POR QUÉ?")
|
||||
assert.eq("ljubović".upper(), "LJUBOVIĆ")
|
||||
assert.true("DŽENAN LJUBOVIĆ".isupper())
|
||||
|
||||
# str.title
|
||||
assert.eq("hElLo, WoRlD!".title(), "Hello, World!")
|
||||
assert.eq("por qué".title(), "Por Qué")
|
||||
assert.eq("¿Por qué?".title(), "¿Por Qué?")
|
||||
assert.eq("ljubović".title(), "Ljubović")
|
||||
assert.true("Dženan Ljubović".istitle())
|
||||
assert.true(not "DŽenan LJubović".istitle())
|
||||
|
||||
# method spell check
|
||||
assert.fails(lambda: "".starts_with, "no .starts_with field.*did you mean .startswith")
|
||||
assert.fails(lambda: "".StartsWith, "no .StartsWith field.*did you mean .startswith")
|
||||
assert.fails(lambda: "".fin, "no .fin field.*.did you mean .find")
|
||||
|
||||
|
||||
# removesuffix
|
||||
assert.eq("Apricot".removesuffix("cot"), "Apri")
|
||||
assert.eq("Apricot".removesuffix("Cot"), "Apricot")
|
||||
assert.eq("Apricot".removesuffix("t"), "Aprico")
|
||||
assert.eq("a".removesuffix(""), "a")
|
||||
assert.eq("".removesuffix(""), "")
|
||||
assert.eq("".removesuffix("a"), "")
|
||||
assert.eq("Apricot".removesuffix("co"), "Apricot")
|
||||
assert.eq("Apricotcot".removesuffix("cot"), "Apricot")
|
||||
|
||||
# removeprefix
|
||||
assert.eq("Apricot".removeprefix("Apr"), "icot")
|
||||
assert.eq("Apricot".removeprefix("apr"), "Apricot")
|
||||
assert.eq("Apricot".removeprefix("A"), "pricot")
|
||||
assert.eq("a".removeprefix(""), "a")
|
||||
assert.eq("".removeprefix(""), "")
|
||||
assert.eq("".removeprefix("a"), "")
|
||||
assert.eq("Apricot".removeprefix("pr"), "Apricot")
|
||||
assert.eq("AprApricot".removeprefix("Apr"), "Apricot")
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
# Tests of time module.
|
||||
|
||||
load('assert.star', 'assert')
|
||||
load('time.star', 'time')
|
||||
|
||||
assert.true(time.now() > time.parse_time("2021-03-20T00:00:00Z"))
|
||||
|
||||
assert.eq(time.parse_time("2020-06-26T17:38:36Z"), time.from_timestamp(1593193116))
|
||||
assert.eq(time.parse_time("2020-06-26T17:38:36.123456789", format="2006-01-02T15:04:05.999999999"), time.from_timestamp(1593193116, 123456789))
|
||||
|
||||
assert.eq(time.parse_time("1970-01-01T00:00:00Z").unix, 0)
|
||||
assert.eq(time.parse_time("1970-01-01T00:00:00Z").unix_nano, 0)
|
||||
|
||||
t = time.parse_time("2000-01-02T03:04:05Z")
|
||||
assert.eq(t.year, 2000)
|
||||
assert.eq(t.in_location("US/Eastern"), time.parse_time("2000-01-01T22:04:05-05:00"))
|
||||
assert.eq(t.in_location("US/Eastern").format("3 04 PM"), "10 04 PM")
|
||||
|
||||
assert.eq(t - t, time.parse_duration("0s"))
|
||||
|
||||
d1s = time.parse_duration("1s")
|
||||
assert.eq(d1s - d1s, time.parse_duration("0"))
|
||||
assert.eq(d1s + d1s, time.parse_duration("2s"))
|
||||
assert.eq(d1s * 5, time.parse_duration("5s"))
|
||||
assert.eq(time.parse_duration("0s") + time.parse_duration("3m35s"), time.parse_duration("3m35s"))
|
||||
|
||||
d10h = time.parse_duration("10h")
|
||||
# duration attributes
|
||||
assert.eq(10.0, d10h.hours)
|
||||
assert.eq(10*60.0, d10h.minutes)
|
||||
assert.eq(10*60*60.0, d10h.seconds)
|
||||
assert.eq(10*60*60*1000, d10h.milliseconds)
|
||||
assert.eq(10*60*60*1000000, d10h.microseconds)
|
||||
assert.eq(10*60*60*1000000000, d10h.nanoseconds)
|
||||
|
||||
# duration type
|
||||
assert.eq("time.duration", type(d10h))
|
||||
# duration str
|
||||
assert.eq("10h0m0s", str(d10h))
|
||||
# duration hash
|
||||
durations = {
|
||||
d10h: "10h",
|
||||
d1s: "10s",
|
||||
}
|
||||
assert.eq("10h", durations[d10h])
|
||||
assert.eq("10s", durations[d1s])
|
||||
|
||||
# duration == duration
|
||||
# duration != duration
|
||||
assert.eq(time.parse_duration("1h"), time.parse_duration("1h"))
|
||||
assert.ne(time.parse_duration("1h"), time.parse_duration("1m"))
|
||||
# duration < duration
|
||||
assert.lt(time.parse_duration("1m"), time.parse_duration("1h"))
|
||||
assert.true(not time.parse_duration("1h") < time.parse_duration("1h"))
|
||||
assert.true(not time.parse_duration("1h") < time.parse_duration("1m"))
|
||||
# duration <= duration
|
||||
assert.true(time.parse_duration("1m") <= time.parse_duration("1h"))
|
||||
assert.true(time.parse_duration("1h") <= time.parse_duration("1h"))
|
||||
assert.true(not time.parse_duration("1h") <= time.parse_duration("1m"))
|
||||
# duration > duration
|
||||
assert.true(not time.parse_duration("1m") > time.parse_duration("1h"))
|
||||
assert.true(not time.parse_duration("1h") > time.parse_duration("1h"))
|
||||
assert.true(time.parse_duration("1h") > time.parse_duration("1m"))
|
||||
# duration >= duration
|
||||
assert.true(not time.parse_duration("1m") >= time.parse_duration("1h"))
|
||||
assert.true(time.parse_duration("1h") >= time.parse_duration("1h"))
|
||||
assert.true(time.parse_duration("1h") >= time.parse_duration("1m"))
|
||||
|
||||
refTime = time.parse_time("2011-04-22T13:33:48Z")
|
||||
tenHoursAfterRefTime = time.parse_time("2011-04-22T23:33:48Z")
|
||||
|
||||
# duration + duration = duration
|
||||
assert.eq(d10h + d1s, time.parse_duration("10h01s"))
|
||||
# duration + time = time
|
||||
assert.eq(d10h + refTime, tenHoursAfterRefTime)
|
||||
# duration - duration = duration
|
||||
assert.eq(d10h - d1s, time.parse_duration("9h59m59s"))
|
||||
# duration / duration = float
|
||||
assert.eq(d10h / time.parse_duration("16m"), 37.5)
|
||||
assert.fails(lambda: d10h / time.parse_duration("0"), "division by zero")
|
||||
# duration / int = duration
|
||||
assert.eq(d10h / 20, time.parse_duration("30m"))
|
||||
assert.fails(lambda: d10h / 0, "division by zero")
|
||||
# int / duration = error
|
||||
assert.fails(lambda: 20 / d10h, "unsupported operation")
|
||||
# duration / float = duration
|
||||
assert.eq(d10h / 37.5, time.parse_duration("16m"))
|
||||
assert.fails(lambda: d10h / 0.0, "division by zero")
|
||||
# duration // duration = int
|
||||
assert.eq(d10h // time.parse_duration("16m"), 37)
|
||||
assert.fails(lambda: d10h // time.parse_duration("0"), "division by zero")
|
||||
# duration * int = duration
|
||||
assert.eq(d1s * 1000, time.parse_duration("16m40s"))
|
||||
# int * duration = duration
|
||||
assert.eq(1000 * d1s, time.parse_duration("16m40s"))
|
||||
|
||||
# is_valid_timezone(location)
|
||||
assert.true(time.is_valid_timezone("UTC"))
|
||||
assert.true(time.is_valid_timezone("US/Eastern"))
|
||||
assert.true(not time.is_valid_timezone("UKN"))
|
||||
|
||||
# time(year=..., month=..., day=..., hour=..., minute=..., second=..., nanosecond=..., location=...)
|
||||
assert.fails(lambda: time.time(2009, 6, 12, 12, 6, 10, 99, "US/Eastern"), "unexpected positional argument")
|
||||
t1 = time.time(year=2009, month=6, day=12, hour=12, minute=6, second=10, nanosecond=99, location="US/Eastern")
|
||||
assert.eq(t1, time.parse_time("2009-06-12T12:06:10.000000099", format="2006-01-02T15:04:05.999999999", location="US/Eastern"))
|
||||
assert.eq(time.time(year=2012, month=12, day=31), time.parse_time("2012-12-31T00:00:00Z"))
|
||||
assert.eq(time.time(year=2009, month=6, day=12, hour=12, minute=6, second=10, nanosecond=99, location="UTC"), time.time(year=2009, month=6, day=12, hour=12, minute=6, second=10, nanosecond=99))
|
||||
|
||||
# time attributes
|
||||
assert.eq(2009, t1.year)
|
||||
assert.eq(6, t1.month)
|
||||
assert.eq(12, t1.day)
|
||||
assert.eq(12, t1.hour)
|
||||
assert.eq(6, t1.minute)
|
||||
assert.eq(10, t1.second)
|
||||
assert.eq(99, t1.nanosecond)
|
||||
assert.eq(1244822770, t1.unix)
|
||||
assert.eq(1244822770000000099, t1.unix_nano)
|
||||
assert.true(not time.parse_time("0001-01-01T00:00:00Z"))
|
||||
assert.true(time.parse_time("2022-01-01T00:00:00Z"))
|
||||
|
||||
# time type
|
||||
assert.eq("time.time", type(refTime))
|
||||
# duration str
|
||||
assert.eq("2011-04-22 13:33:48 +0000 UTC", str(refTime))
|
||||
# duration hash
|
||||
times = {
|
||||
refTime: "refTime",
|
||||
t1: "t1",
|
||||
}
|
||||
assert.eq("refTime", times[refTime])
|
||||
assert.eq("t1", times[t1])
|
||||
|
||||
oneSecondAfterRefTime = time.parse_time("2011-04-22T13:33:49Z")
|
||||
oneYearAfterRefTime = time.parse_time("2012-04-22T13:33:48Z")
|
||||
oneYearBeforeRefTime = time.parse_time("2010-04-22T13:33:48Z")
|
||||
twoYearsBeforeRefTime = time.parse_time("2009-04-22T13:33:48Z")
|
||||
tenHoursBeforeRefTime = time.parse_time("2011-04-22T03:33:48Z")
|
||||
|
||||
# time == time
|
||||
# time != time
|
||||
assert.eq(refTime, refTime)
|
||||
assert.ne(refTime, oneSecondAfterRefTime)
|
||||
# time < time
|
||||
assert.lt(oneYearBeforeRefTime, refTime)
|
||||
assert.true(not oneYearBeforeRefTime < oneYearBeforeRefTime)
|
||||
assert.true(not oneYearBeforeRefTime < twoYearsBeforeRefTime)
|
||||
# time <= time
|
||||
assert.true(oneYearBeforeRefTime <= refTime)
|
||||
assert.true(oneYearBeforeRefTime <= oneYearBeforeRefTime)
|
||||
assert.true(not oneYearBeforeRefTime <= twoYearsBeforeRefTime)
|
||||
# time > time
|
||||
assert.true(oneYearAfterRefTime > refTime)
|
||||
assert.true(not refTime > refTime)
|
||||
assert.true(not oneYearBeforeRefTime > refTime)
|
||||
# time >= time
|
||||
assert.true(oneYearAfterRefTime >= refTime)
|
||||
assert.true(refTime >= refTime)
|
||||
assert.true(not oneYearBeforeRefTime >= refTime)
|
||||
# time + duration = time
|
||||
assert.eq(refTime + d10h, tenHoursAfterRefTime)
|
||||
# time - duration = time
|
||||
assert.eq(refTime - d10h, tenHoursBeforeRefTime)
|
||||
# time - time = duration
|
||||
assert.eq(refTime - tenHoursBeforeRefTime, d10h)
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# Tests of Starlark 'tuple'
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
# literal
|
||||
assert.eq((), ())
|
||||
assert.eq((1), 1)
|
||||
assert.eq((1,), (1,))
|
||||
assert.ne((1), (1,))
|
||||
assert.eq((1, 2), (1, 2))
|
||||
assert.eq((1, 2, 3, 4, 5), (1, 2, 3, 4, 5))
|
||||
assert.ne((1, 2, 3), (1, 2, 4))
|
||||
|
||||
# truth
|
||||
assert.true((False,))
|
||||
assert.true((False, False))
|
||||
assert.true(not ())
|
||||
|
||||
# indexing, x[i]
|
||||
assert.eq(("a", "b")[0], "a")
|
||||
assert.eq(("a", "b")[1], "b")
|
||||
|
||||
# slicing, x[i:j]
|
||||
assert.eq("abcd"[0:4:1], "abcd")
|
||||
assert.eq("abcd"[::2], "ac")
|
||||
assert.eq("abcd"[1::2], "bd")
|
||||
assert.eq("abcd"[4:0:-1], "dcb")
|
||||
banana = tuple("banana".elems())
|
||||
assert.eq(banana[7::-2], tuple("aaa".elems()))
|
||||
assert.eq(banana[6::-2], tuple("aaa".elems()))
|
||||
assert.eq(banana[5::-2], tuple("aaa".elems()))
|
||||
assert.eq(banana[4::-2], tuple("nnb".elems()))
|
||||
|
||||
# tuple
|
||||
assert.eq(tuple(), ())
|
||||
assert.eq(tuple("abc".elems()), ("a", "b", "c"))
|
||||
assert.eq(tuple(["a", "b", "c"]), ("a", "b", "c"))
|
||||
assert.eq(tuple([1]), (1,))
|
||||
assert.fails(lambda: tuple(1), "got int, want iterable")
|
||||
|
||||
# tuple * int, int * tuple
|
||||
abc = tuple("abc".elems())
|
||||
assert.eq(abc * 0, ())
|
||||
assert.eq(abc * -1, ())
|
||||
assert.eq(abc * 1, abc)
|
||||
assert.eq(abc * 3, ("a", "b", "c", "a", "b", "c", "a", "b", "c"))
|
||||
assert.eq(0 * abc, ())
|
||||
assert.eq(-1 * abc, ())
|
||||
assert.eq(1 * abc, abc)
|
||||
assert.eq(3 * abc, ("a", "b", "c", "a", "b", "c", "a", "b", "c"))
|
||||
assert.fails(lambda: abc * (1000000 * 1000000), "repeat count 1000000000000 too large")
|
||||
assert.fails(lambda: abc * 1000000 * 1000000, "excessive repeat \\(3000000 \\* 1000000 elements")
|
||||
|
||||
# TODO(adonovan): test use of tuple as sequence
|
||||
# (for loop, comprehension, library functions).
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Tests of Starlark while statement.
|
||||
|
||||
# This is a "chunked" file: each "---" effectively starts a new file.
|
||||
|
||||
# option:while
|
||||
|
||||
load("assert.star", "assert")
|
||||
|
||||
def sum(n):
|
||||
r = 0
|
||||
while n > 0:
|
||||
r += n
|
||||
n -= 1
|
||||
return r
|
||||
|
||||
def while_break(n):
|
||||
r = 0
|
||||
while n > 0:
|
||||
if n == 5:
|
||||
break
|
||||
r += n
|
||||
n -= 1
|
||||
return r
|
||||
|
||||
def while_continue(n):
|
||||
r = 0
|
||||
while n > 0:
|
||||
if n % 2 == 0:
|
||||
n -= 1
|
||||
continue
|
||||
r += n
|
||||
n -= 1
|
||||
return r
|
||||
|
||||
assert.eq(sum(5), 5+4+3+2+1)
|
||||
assert.eq(while_break(10), 40)
|
||||
assert.eq(while_continue(10), 25)
|
||||
Reference in New Issue
Block a user