1 |
|
2 |
|
3 |
|
4 | CND = require 'cnd'
|
5 | rpr = CND.rpr
|
6 | badge = 'HOLLERITH-CODEC/tests'
|
7 | log = CND.get_logger 'plain', badge
|
8 | info = CND.get_logger 'info', badge
|
9 | whisper = CND.get_logger 'whisper', badge
|
10 | alert = CND.get_logger 'alert', badge
|
11 | debug = CND.get_logger 'debug', badge
|
12 | warn = CND.get_logger 'warn', badge
|
13 | help = CND.get_logger 'help', badge
|
14 | urge = CND.get_logger 'urge', badge
|
15 | echo = CND.echo.bind CND
|
16 |
|
17 | test = require 'guy-test'
|
18 | CODEC = require './main'
|
19 | ƒ = CND.format_number
|
20 | { jr, } = CND
|
21 |
|
22 |
|
23 | @[ "codec encodes and decodes numbers" ] = ( T ) ->
|
24 | key = [ 'foo', 1234, 5678, ]
|
25 | key_bfr = CODEC.encode key
|
26 | T.eq key, CODEC.decode key_bfr
|
27 | whisper "key length: #{key_bfr.length}"
|
28 |
|
29 |
|
30 | @[ "codec encodes and decodes dates" ] = ( T ) ->
|
31 | key = [ 'foo', ( new Date() ), 5678, ]
|
32 | key_bfr = CODEC.encode key
|
33 | T.eq key, CODEC.decode key_bfr
|
34 | whisper "key length: #{key_bfr.length}"
|
35 |
|
36 |
|
37 | @[ "codec accepts long numbers" ] = ( T ) ->
|
38 | key = [ 'foo', ( i for i in [ 0 .. 1000 ] ), 'bar', ]
|
39 | key_bfr = CODEC.encode key
|
40 | T.eq key, CODEC.decode key_bfr
|
41 | whisper "key length: #{key_bfr.length}"
|
42 |
|
43 |
|
44 | @[ "codec accepts long texts" ] = ( T ) ->
|
45 | long_text = ( new Array 1e4 ).join '#'
|
46 | key = [ 'foo', [ long_text, long_text, long_text, long_text, ], 42, ]
|
47 | key_bfr = CODEC.encode key
|
48 | T.eq key, CODEC.decode key_bfr
|
49 | whisper "key length: #{key_bfr.length}"
|
50 |
|
51 |
|
52 | @[ "codec preserves critical escaped characters (roundtrip) (1)" ] = ( T ) ->
|
53 | text = 'abc\x00\x00\x00\x00def'
|
54 | key = [ 'xxx', [ text, ], 0, ]
|
55 | key_bfr = CODEC.encode key
|
56 | T.eq key, CODEC.decode key_bfr
|
57 |
|
58 |
|
59 | @[ "codec preserves critical escaped characters (roundtrip) (2)" ] = ( T ) ->
|
60 | text = 'abc\x01\x01\x01\x01def'
|
61 | key = [ 'xxx', [ text, ], 0, ]
|
62 | key_bfr = CODEC.encode key
|
63 | T.eq key, CODEC.decode key_bfr
|
64 |
|
65 |
|
66 | @[ "codec preserves critical escaped characters (roundtrip) (3)" ] = ( T ) ->
|
67 | text = 'abc\x00\x01\x00\x01def'
|
68 | key = [ 'xxx', [ text, ], 0, ]
|
69 | key_bfr = CODEC.encode key
|
70 | T.eq key, CODEC.decode key_bfr
|
71 |
|
72 |
|
73 | @[ "codec preserves critical escaped characters (roundtrip) (4)" ] = ( T ) ->
|
74 | text = 'abc\x01\x00\x01\x00def'
|
75 | key = [ 'xxx', [ text, ], 0, ]
|
76 | key_bfr = CODEC.encode key
|
77 | T.eq key, CODEC.decode key_bfr
|
78 |
|
79 |
|
80 | @[ "codec accepts private type (1)" ] = ( T ) ->
|
81 | key = [ { type: 'price', value: 'abc', }, ]
|
82 | key_bfr = CODEC.encode key
|
83 | T.eq key, CODEC.decode key_bfr
|
84 |
|
85 |
|
86 | @[ "codec accepts private type (2)" ] = ( T ) ->
|
87 | key = [ 123, 456, { type: 'price', value: 'abc', }, 'xxx', ]
|
88 | key_bfr = CODEC.encode key
|
89 | T.eq key, CODEC.decode key_bfr
|
90 |
|
91 |
|
92 | @[ "codec decodes private type with custom decoder (1)" ] = ( T ) ->
|
93 | value = '/etc/cron.d/anacron'
|
94 | matcher = [ value, ]
|
95 | encoded_value = value.split '/'
|
96 | key = [ { type: 'route', value: encoded_value, }, ]
|
97 | key_bfr = CODEC.encode key
|
98 |
|
99 | decoded_key = CODEC.decode key_bfr, ( type, value ) ->
|
100 | return value.join '/' if type is 'route'
|
101 | throw new Error "unknown private type #{rpr type}"
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | T.eq matcher, decoded_key
|
107 |
|
108 |
|
109 | @_sets_are_equal = ( a, b ) ->
|
110 |
|
111 | return false unless ( CODEC.types.isa.set a ) and ( CODEC.types.isa.set b )
|
112 | return false unless a.size is b.size
|
113 | a_keys = a.keys()
|
114 | b_keys = b.keys()
|
115 | loop
|
116 | { value: a_value, done: a_done, } = a_keys.next()
|
117 | { value: b_value, done: b_done, } = b_keys.next()
|
118 | break if a_done or b_done
|
119 | return false unless CODEC.types.equals a_value, b_value
|
120 | return true
|
121 |
|
122 |
|
123 | @[ "codec decodes private type with custom decoder (2)" ] = ( T ) ->
|
124 | value = new Set 'qwert'
|
125 | matcher = [ value, ]
|
126 | encoded_value = Array.from value
|
127 | key = [ { type: 'set', value: encoded_value, }, ]
|
128 | key_bfr = CODEC.encode key
|
129 |
|
130 | decoded_key = CODEC.decode key_bfr, ( type, value ) ->
|
131 | return new Set value if type is 'set'
|
132 | throw new Error "unknown private type #{rpr type}"
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | T.ok @_sets_are_equal matcher[ 0 ], decoded_key[ 0 ]
|
139 |
|
140 |
|
141 | @[ "Support for Sets" ] = ( T ) ->
|
142 | key = [ ( new Set 'qwert' ), ]
|
143 | matcher = [ ( new Set 'qwert' ), ]
|
144 | key_bfr = CODEC.encode key
|
145 | decoded_key = CODEC.decode key_bfr
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | T.ok @_sets_are_equal matcher[ 0 ], decoded_key[ 0 ]
|
151 |
|
152 |
|
153 | @[ "codec decodes private type with custom encoder and decoder (3)" ] = ( T ) ->
|
154 | route = '/usr/local/lib/node_modules/coffee-script/README.md'
|
155 | parts = route.split '/'
|
156 | key = [ { type: 'route', value: route, }, ]
|
157 | matcher_1 = [ { type: 'route', value: parts, }]
|
158 | matcher_2 = [ route, ]
|
159 |
|
160 | encoder = ( type, value ) ->
|
161 | return value.split '/' if type is 'route'
|
162 | throw new Error "unknown private type #{rpr type}"
|
163 |
|
164 | decoder = ( type, value ) ->
|
165 | return value.join '/' if type is 'route'
|
166 | throw new Error "unknown private type #{rpr type}"
|
167 |
|
168 | key_bfr = CODEC.encode key, encoder
|
169 |
|
170 | decoded_key_1 = CODEC.decode key_bfr
|
171 | T.eq matcher_1, decoded_key_1
|
172 | decoded_key_2 = CODEC.decode key_bfr, decoder
|
173 | T.eq matcher_2, decoded_key_2
|
174 |
|
175 |
|
176 | @[ "private type takes default shape when handler returns use_fallback" ] = ( T ) ->
|
177 | matcher = [ 84, { type: 'bar', value: 108, }, ]
|
178 | key = [ { type: 'foo', value: 42, }, { type: 'bar', value: 108, }, ]
|
179 | key_bfr = CODEC.encode key
|
180 |
|
181 | decoded_key = CODEC.decode key_bfr, ( type, value, use_fallback ) ->
|
182 | return value * 2 if type is 'foo'
|
183 | return use_fallback
|
184 |
|
185 | T.eq matcher, decoded_key
|
186 |
|
187 |
|
188 | @[ "test: flat file DB storage (1)" ] = ( T ) ->
|
189 | probes = [
|
190 | [ 'foo', -Infinity, ]
|
191 | [ 'foo', -1e12, ]
|
192 | [ 'foo', -3, ]
|
193 | [ 'foo', -2, ]
|
194 | [ 'foo', -1, ]
|
195 | [ 'foo', 1, ]
|
196 | [ 'foo', 2, ]
|
197 | [ 'foo', 3, ]
|
198 | [ 'foo', 1e12, ]
|
199 | [ 'foo', Infinity, ]
|
200 | [ 'bar', 'blah', ]
|
201 | [ 'bar', 'gnu', ]
|
202 | [ 'a', ]
|
203 | [ 'b', ]
|
204 | [ 'c', ]
|
205 | [ 'A', ]
|
206 | [ '箲' ]
|
207 | [ '筅' ]
|
208 | [ '𥬗' ]
|
209 | [ 'B', ]
|
210 | [ 'C', ]
|
211 | [ '0', ]
|
212 | [ '1', ]
|
213 | [ '2', ]
|
214 | [ 'Number', Number.EPSILON, 'EPSILON', ]
|
215 | [ 'Number', Number.MAX_SAFE_INTEGER, 'MAX_SAFE_INTEGER', ]
|
216 | [ 'Number', Number.MAX_VALUE, 'MAX_VALUE', ]
|
217 | [ 'Number', 0, 'ZERO', ]
|
218 | [ 'Number', Number.MIN_SAFE_INTEGER, 'MIN_SAFE_INTEGER', ]
|
219 | [ 'Number', Number.MIN_VALUE, 'MIN_VALUE', ]
|
220 | ]
|
221 | buffer_as_text = ( buffer ) ->
|
222 | R = []
|
223 | for idx in [ 0 ... buffer.length ]
|
224 | R.push String.fromCodePoint 0x2800 + buffer[ idx ]
|
225 |
|
226 |
|
227 | return R.join ''
|
228 | probes = ( [ ( buffer_as_text CODEC.encode probe ), JSON.stringify probe, ] for probe in probes )
|
229 | probes.sort ( a, b ) ->
|
230 | return -1 if a[ 0 ] < b[ 0 ]
|
231 | return +1 if a[ 0 ] > b[ 0 ]
|
232 | return 0
|
233 | for probe in probes
|
234 | urge probe.join ' - '
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | urge "use `( export LC_ALL=C && sort hollerith-codec-flatfile-db.txt ) | less -SRN`"
|
240 | urge "to sort a file with these lines"
|
241 | return null
|
242 |
|
243 |
|
244 | @[ "test: flat file DB storage (2)" ] = ( T ) ->
|
245 | probes_and_matchers = [
|
246 | [["foo",-1000000000000],"⡔⡦⡯⡯⠀⡋⢽⢒⣥⡫⡝⣿⣿⣿![\"foo\",-1000000000000]"]
|
247 | [["foo",-3],"⡔⡦⡯⡯⠀⡋⢿⣷⣿⣿⣿⣿⣿⣿![\"foo\",-3]"]
|
248 | [["foo",-2],"⡔⡦⡯⡯⠀⡋⢿⣿⣿⣿⣿⣿⣿⣿![\"foo\",-2]"]
|
249 | [["foo",-1],"⡔⡦⡯⡯⠀⡋⣀⠏⣿⣿⣿⣿⣿⣿![\"foo\",-1]"]
|
250 | [["foo",1],"⡔⡦⡯⡯⠀⡍⠿⣰⠀⠀⠀⠀⠀⠀![\"foo\",1]"]
|
251 | [["foo",2],"⡔⡦⡯⡯⠀⡍⡀⠀⠀⠀⠀⠀⠀⠀![\"foo\",2]"]
|
252 | [["foo",3],"⡔⡦⡯⡯⠀⡍⡀⠈⠀⠀⠀⠀⠀⠀![\"foo\",3]"]
|
253 | [["foo",1000000000000],"⡔⡦⡯⡯⠀⡍⡂⡭⠚⢔⢢⠀⠀⠀![\"foo\",1000000000000]"]
|
254 | [["bar","blah"],"⡔⡢⡡⡲⠀⡔⡢⡬⡡⡨⠀![\"bar\",\"blah\"]"]
|
255 | [["bar","gnu"],"⡔⡢⡡⡲⠀⡔⡧⡮⡵⠀![\"bar\",\"gnu\"]"]
|
256 | [["a"],"⡔⡡⠀![\"a\"]"]
|
257 | [["b"],"⡔⡢⠀![\"b\"]"]
|
258 | [["c"],"⡔⡣⠀![\"c\"]"]
|
259 | [["A"],"⡔⡁⠀![\"A\"]"]
|
260 | [["箲"],"⡔⣧⢮⢲⠀![\"箲\"]"]
|
261 | [["筅"],"⡔⣧⢭⢅⠀![\"筅\"]"]
|
262 | [["𥬗"],"⡔⣰⢥⢬⢗⠀![\"𥬗\"]"]
|
263 | [["B"],"⡔⡂⠀![\"B\"]"]
|
264 | [["C"],"⡔⡃⠀![\"C\"]"]
|
265 | [["0"],"⡔⠰⠀![\"0\"]"]
|
266 | [["1"],"⡔⠱⠀![\"1\"]"]
|
267 | [["2"],"⡔⠲⠀![\"2\"]"]
|
268 | [["Number",2.220446049250313e-16,"EPSILON"],"⡔⡎⡵⡭⡢⡥⡲⠀⡍⠼⢰⠀⠀⠀⠀⠀⠀⡔⡅⡐⡓⡉⡌⡏⡎⠀![\"Number\",2.220446049250313e-16,\"EPSILON\"]"]
|
269 | [["Number",9007199254740991,"MAX_SAFE_INTEGER"],"⡔⡎⡵⡭⡢⡥⡲⠀⡍⡃⠿⣿⣿⣿⣿⣿⣿⡔⡍⡁⡘⡟⡓⡁⡆⡅⡟⡉⡎⡔⡅⡇⡅⡒⠀![\"Number\",9007199254740991,\"MAX_SAFE_INTEGER\"]"]
|
270 | [["Number",1.7976931348623157e+308,"MAX_VALUE"],"⡔⡎⡵⡭⡢⡥⡲⠀⡍⡿⣯⣿⣿⣿⣿⣿⣿⡔⡍⡁⡘⡟⡖⡁⡌⡕⡅⠀![\"Number\",1.7976931348623157e+308,\"MAX_VALUE\"]"]
|
271 | [["Number",0,"ZERO"],"⡔⡎⡵⡭⡢⡥⡲⠀⡍⠀⠀⠀⠀⠀⠀⠀⠀⡔⡚⡅⡒⡏⠀![\"Number\",0,\"ZERO\"]"]
|
272 | [["Number",-9007199254740991,"MIN_SAFE_INTEGER"],"⡔⡎⡵⡭⡢⡥⡲⠀⡋⢼⣀⠀⠀⠀⠀⠀⠀⡔⡍⡉⡎⡟⡓⡁⡆⡅⡟⡉⡎⡔⡅⡇⡅⡒⠀![\"Number\",-9007199254740991,\"MIN_SAFE_INTEGER\"]"]
|
273 | [["Number",5e-324,"MIN_VALUE"],"⡔⡎⡵⡭⡢⡥⡲⠀⡍⠀⠀⠀⠀⠀⠀⠀⠁⡔⡍⡉⡎⡟⡖⡁⡌⡕⡅⠀![\"Number\",5e-324,\"MIN_VALUE\"]"]
|
274 | ]
|
275 |
|
276 | stringify = jr
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | settings = { stringify, joiner: '!', }
|
284 | for [ probe, matcher, ] in probes_and_matchers
|
285 | result = CODEC.as_sortline probe, settings
|
286 | debug '33392', stringify [ probe, result, ]
|
287 | T.eq result, matcher
|
288 | return null
|
289 |
|
290 |
|
291 | @[ "test: flat file DB storage (3)" ] = ( T ) ->
|
292 | stringify = ( x ) -> ( require 'util' ).inspect x, { maxArrayLength: null, breakLength: Infinity, }
|
293 | probes_and_matchers = [
|
294 | [ [ 'foo', 1234 ], { bare: false, joiner: ',', base: 19968 }, '乔书乯乯一乍乀亓么一一一一一,["foo",1234]' ]
|
295 | [ [ 'foo', 1234 ], { bare: true, joiner: ',', base: null }, '⡔⡦⡯⡯⠀⡍⡀⢓⡈⠀⠀⠀⠀⠀' ]
|
296 | ]
|
297 | for [ probe, settings, matcher, ] in probes_and_matchers
|
298 | result = CODEC.as_sortline probe, settings
|
299 | debug '33392', stringify [ probe, settings, result, ]
|
300 | T.eq result, matcher
|
301 |
|
302 | return null
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 | @[ "test: ordering where negatives precede void" ] = ( T ) ->
|
319 | probes = [
|
320 | [-10,]
|
321 | [10,]
|
322 | [10,0]
|
323 | [10,-1]
|
324 | [10,-2]
|
325 | [10,1]
|
326 | [10,2]
|
327 | [10,4]
|
328 | [10,4,0]
|
329 | [10,4,-1]
|
330 | [10,4,-2]
|
331 | [10,4,1]
|
332 | [10,4,2]
|
333 | [10,0,3]
|
334 | [10,-1,3]
|
335 | [10,-2,3]
|
336 | [10,1,3]
|
337 | [10,2,3]
|
338 | []
|
339 | [0]
|
340 | [-1]
|
341 | [1]
|
342 | [1,0]
|
343 | [1,-1]
|
344 | [1,1]
|
345 | ]
|
346 | buffer_as_text = ( buffer ) -> buffer.toString 'hex'
|
347 | jrx = ( x ) -> ( JSON.stringify x ).padEnd 15
|
348 | encode = ( x ) -> buffer_as_text CODEC.encode x
|
349 | results = ( [ ( jrx probe ), ( probe ), ( encode probe ), ] for probe in probes )
|
350 | results.sort ( a, b ) ->
|
351 | return -1 if a[ 2 ] < b[ 2 ]
|
352 | return +1 if a[ 2 ] > b[ 2 ]
|
353 | return 0
|
354 | for result in results
|
355 | urge result[ 0 ] + ' ... ' + result[ 2 ]
|
356 | results = ( result[ 1 ] for result in results )
|
357 | T.eq results, [[-10],[-1],[],[0],[1,-1],[1],[1,0],[1,1],[10,-2],[10,-2,3],[10,-1],[10,-1,3],[10],[10,0],[10,0,3],[10,1],[10,1,3],[10,2],[10,2,3],[10,4,-2],[10,4,-1],[10,4],[10,4,0],[10,4,1],[10,4,2]]
|
358 | return null
|
359 |
|
360 |
|
361 |
|
362 | unless module.parent?
|
363 | test @
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|