-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtest_llist.py
More file actions
183 lines (143 loc) · 7.54 KB
/
test_llist.py
File metadata and controls
183 lines (143 loc) · 7.54 KB
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# -*- coding: utf-8 -*-
from ..syntax import macros, test, test_raises, the # noqa: F401
from ..test.fixtures import session, testset
from pickle import dumps, loads
from ..llist import (cons, car, cdr, nil, ll, llist,
caar, cdar, cadr, cddr, caddr, cdddr,
member, lreverse, lappend, lzip,
BinaryTreeIterator, JackOfAllTradesIterator,
TailIterator)
from ..fold import foldl, foldr
def runtests():
with testset("cons, car, cdr"):
c = cons(1, 2)
test[car(c) == 1]
test[cdr(c) == 2]
test_raises[TypeError, car("sedan")]
test_raises[TypeError, cdr("disc")]
with test_raises[TypeError, "cons cells should be immutable"]:
c.car = 3
test[the[c == c]]
test[the[cons(1, 2) == cons(1, 2)]]
test[the[cons(1, 2) != cons(2, 3)]]
with testset("ll"):
test[ll(1, 2, 3) == cons(1, cons(2, cons(3, nil)))]
# print(ll(1, 2, cons(3, 4), 5, 6)) # a list may also contain pairs as items
# print(cons(cons(cons(nil, 3), 2), 1)) # improper list
test[ll(1, 2, 3) == ll(1, 2, 3)]
test[the[ll(1, 2)] != the[ll(1, 2, 3)]]
# cons structures are immutable (because cons cells are),
# but new instances based on existing ones are ok.
l1 = ll(3, 2, 1)
l2 = cons(4, l1)
test[l1 == ll(3, 2, 1)]
test[l2 == ll(4, 3, 2, 1)]
l3 = cons(6, cdr(l1))
test[l3 == ll(6, 2, 1)]
thebinarytree = cons(cons(1, 2), cons(3, 4))
with testset("repr"):
test[repr(nil) == "nil"]
test[repr(cons(1, 2)) == "cons(1, 2)"]
test[repr(ll(1, 2, 3)) == "ll(1, 2, 3)"]
test[repr(thebinarytree) == "cons(cons(1, 2), cons(3, 4))"]
test[repr(cons(cons(1, 2), 3)) == "cons(cons(1, 2), 3)"]
test[cons(1, 2).lispyrepr() == "(1 . 2)"]
test[ll(1, 2, 3).lispyrepr() == "(1 2 3)"]
test[thebinarytree.lispyrepr() == "((1 . 2) . (3 . 4))"]
with testset("deeper accessors"):
q = ll(cons(1, 2), cons(3, 4)) # list of pairs, not a tree!
test[[f(q) for f in [caar, cdar, cadr, cddr]] == [1, 2, cons(3, 4), nil]]
mylinkedlist = ll(1, 2, 3)
test[[f(mylinkedlist) for f in [car, cadr, caddr, cdddr, cdr, cddr]] == [1, 2, 3, nil, ll(2, 3), ll(3)]]
with testset("member (lispy membership test function)"):
test[member(2, mylinkedlist) == ll(2, 3)]
test[not member(5, mylinkedlist)]
with testset("type conversions"):
thetuple = (1, 2, 3)
thelist = [1, 2, 3]
thelinkedlist = ll(1, 2, 3)
test[list(thelinkedlist) == thelist]
test[tuple(thelinkedlist) == thetuple]
test[llist(thetuple) == thelinkedlist]
test[llist(thelist) == thelinkedlist]
test[list(nil) == []]
test[tuple(nil) == ()]
with testset("hashability"):
# independently constructed instances with the same data should hash the same.
test[the[hash(cons(1, 2)) == hash(cons(1, 2))]]
test[the[hash(ll(1, 2, 3)) == hash(ll(1, 2, 3))]]
s = set()
s.add(cons(1, 2))
s.add(ll(1, 2, 3))
test[cons(1, 2) in the[s]]
test[ll(1, 2, 3) in the[s]]
test[cons(3, 4) not in the[s]]
test[ll(1, 2) not in the[s]]
with testset("iteration schemes"):
test[[f(thebinarytree) for f in [caar, cdar, cadr, cddr]] == [1, 2, 3, 4]]
test[tuple(BinaryTreeIterator(thebinarytree)) == (1, 2, 3, 4)] # non-default iteration scheme
test_raises[TypeError, tuple(thebinarytree)] # binary tree should not be iterable as a linked list
test_raises[TypeError, JackOfAllTradesIterator("not a cons")]
test_raises[TypeError, BinaryTreeIterator("not a cons")]
test_raises[TypeError, TailIterator("not a cons")]
test_raises[TypeError, tuple(TailIterator(cons(cons(1, 2), 3)))]
# generic iterator that understands both linked lists and binary trees
test[tuple(JackOfAllTradesIterator(ll(1, 2, 3, 4))) == (1, 2, 3, 4)]
test[tuple(JackOfAllTradesIterator(thebinarytree)) == (1, 2, 3, 4)]
test[tuple(JackOfAllTradesIterator(cons(1, 2))) == (1, 2)] # a single cons is a degenerate binary tree
nested_linked_lists = ll(ll(1, 2), ll(3, 4))
test[tuple(JackOfAllTradesIterator(nested_linked_lists)) == (1, 2, 3, 4)] # flattens nested lists
test[tuple(nested_linked_lists) == (ll(1, 2), ll(3, 4))]
c = ll(cons(1, 2), cons(3, 4))
test[tuple(JackOfAllTradesIterator(c)) == (1, 2, 3, 4)]
test[tuple(c) == (cons(1, 2), cons(3, 4))]
t2 = cons(cons(1, nil), cons(2, nil))
test[tuple(BinaryTreeIterator(t2)) == (1, nil, 2, nil)]
test[tuple(JackOfAllTradesIterator(t2)) == (1, 2)] # skips nil in any cdr slot for list compatibility
t2 = cons(cons(nil, 1), cons(nil, 2))
test[tuple(BinaryTreeIterator(t2)) == (nil, 1, nil, 2)]
test[tuple(JackOfAllTradesIterator(t2)) == (nil, 1, nil, 2)] # but doesn't skip nil in the car slot
test[tuple(TailIterator(thelinkedlist)) == (ll(1, 2, 3), ll(2, 3), ll(3))]
with testset("long linked list"):
test[tuple(JackOfAllTradesIterator(llist(range(10000)))) == tuple(range(10000))] # no crash
test[tuple(BinaryTreeIterator(llist(range(10000)))) == tuple(range(10000)) + (nil,)] # no crash
with testset("sequence unpacking syntax"):
left, right = cons(1, 2)
test[the[left] == 1 and the[right] == 2]
a, b, c = ll(1, 2, 3)
test[the[a] == 1 and the[b] == 2 and the[c] == 3]
# unpacking a binary tree
a, b, c, d = BinaryTreeIterator(thebinarytree)
test[the[a] == 1 and the[b] == 2 and the[c] == 3 and the[d] == 4]
with testset("lzip"):
test[tuple(zip(ll(1, 2, 3), ll(4, 5, 6))) == ((1, 4), (2, 5), (3, 6))]
test[lzip(ll(1, 2, 3), ll(4, 5, 6)) == ll(ll(1, 4), ll(2, 5), ll(3, 6))]
with testset("lappend"):
test[lappend(ll(1, 2, 3), ll(4, 5, 6)) == ll(1, 2, 3, 4, 5, 6)]
test[lappend(ll(1, 2), ll(3, 4), ll(5, 6)) == ll(1, 2, 3, 4, 5, 6)]
with testset("lreverse"):
test[lreverse(ll(1, 2, 3)) == ll(3, 2, 1)]
with testset("other ways to reverse a linked list"):
test[llist(reversed(ll(1, 2, 3))) == ll(3, 2, 1)]
test[foldl(cons, nil, ll(1, 2, 3)) == ll(3, 2, 1)]
# implementation detail to avoid extra reverses (performance implications, so test it)
r = reversed(ll(1, 2, 3)) # an iterator that internally builds the reversed list...
test[llist(r) is r._data] # ...which llist should just grab
# foldr implicitly reverses the input
test[foldr(cons, nil, ll(1, 2, 3)) == ll(1, 2, 3)]
with testset("pickling"):
mylinkedlist = ll(1, 2, 3)
k = loads(dumps(mylinkedlist))
# Should iterate without crashing, since upon unpickling, the pickled nil singleton
# translates to our nil singleton.
#
# (Remember `pickle` doesn't save class definitions; what it saves is that there
# was a reference to `unpythonic.llist.nil`, which was an instance of a class named
# `unpythonic.llist.Nil`. Unpickling will re-create the saved `nil` instance by
# calling `Nil.__new__` and then loading the instance data into it. In this case,
# `Nil.__new__` just returns the singleton instance already created for the current
# session, and there's no instance data to load.)
test[tuple(k) == (1, 2, 3)]
if __name__ == '__main__': # pragma: no cover
with session(__file__):
runtests()