adsp 1.0.2 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/{test → lib/adsp/test}/common.rb +4 -1
  3. data/{test → lib/adsp/test}/coverage_helper.rb +0 -0
  4. data/lib/adsp/test/file.rb +120 -0
  5. data/{test → lib/adsp/test}/minitest.rb +1 -0
  6. data/{test → lib/adsp/test}/mock/common.rb +1 -0
  7. data/{test → lib/adsp/test}/mock/file.rb +1 -0
  8. data/{test → lib/adsp/test}/mock/stream/raw/compressor.rb +1 -0
  9. data/{test → lib/adsp/test}/mock/stream/raw/decompressor.rb +1 -0
  10. data/{test → lib/adsp/test}/mock/stream/raw/native_compressor.rb +1 -0
  11. data/{test → lib/adsp/test}/mock/stream/raw/native_decompressor.rb +1 -0
  12. data/{test → lib/adsp/test}/mock/stream/reader.rb +1 -0
  13. data/{test → lib/adsp/test}/mock/stream/writer.rb +1 -0
  14. data/{test → lib/adsp/test}/mock/string.rb +1 -0
  15. data/{test → lib/adsp/test}/option.rb +1 -0
  16. data/{test → lib/adsp/test}/stream/abstract.rb +1 -0
  17. data/lib/adsp/test/stream/minitar.rb +50 -0
  18. data/{test → lib/adsp/test}/stream/raw/abstract.rb +1 -0
  19. data/lib/adsp/test/stream/raw/compressor.rb +165 -0
  20. data/lib/adsp/test/stream/raw/decompressor.rb +165 -0
  21. data/lib/adsp/test/stream/reader.rb +642 -0
  22. data/lib/adsp/test/stream/reader_helpers.rb +421 -0
  23. data/lib/adsp/test/stream/writer.rb +609 -0
  24. data/lib/adsp/test/stream/writer_helpers.rb +267 -0
  25. data/lib/adsp/test/string.rb +95 -0
  26. data/{test → lib/adsp/test}/validation.rb +7 -0
  27. data/lib/adsp/test/version.rb +18 -0
  28. data/lib/adsp/version.rb +1 -1
  29. data/test/file.test.rb +3 -116
  30. data/test/stream/minitar.test.rb +3 -46
  31. data/test/stream/raw/compressor.test.rb +3 -162
  32. data/test/stream/raw/decompressor.test.rb +3 -162
  33. data/test/stream/reader.test.rb +3 -639
  34. data/test/stream/reader_helpers.test.rb +3 -417
  35. data/test/stream/writer.test.rb +3 -606
  36. data/test/stream/writer_helpers.test.rb +3 -263
  37. data/test/string.test.rb +3 -91
  38. data/test/version.test.rb +3 -14
  39. metadata +28 -19
@@ -0,0 +1,642 @@
1
+ # Abstract data stream processor.
2
+ # Copyright (c) 2021 AUTHORS, MIT License.
3
+
4
+ require "set"
5
+ require "socket"
6
+ require "stringio"
7
+
8
+ require_relative "abstract"
9
+ require_relative "../common"
10
+ require_relative "../option"
11
+ require_relative "../validation"
12
+ require_relative "../mock/stream/reader"
13
+ require_relative "../mock/string"
14
+
15
+ module ADSP
16
+ module Test
17
+ # ADSP::Test::Stream module.
18
+ module Stream
19
+ # ADSP::Test::Stream::Reader class.
20
+ class Reader < Abstract
21
+ Target = Mock::Stream::Reader
22
+ String = Mock::String
23
+
24
+ ARCHIVE_PATH = Common::ARCHIVE_PATH
25
+ ENCODINGS = Common::ENCODINGS
26
+ TRANSCODE_OPTIONS = Common::TRANSCODE_OPTIONS
27
+ TEXTS = Common::TEXTS
28
+ LARGE_TEXTS = Common::LARGE_TEXTS
29
+ PORTION_LENGTHS = Common::PORTION_LENGTHS
30
+ LARGE_PORTION_LENGTHS = Common::LARGE_PORTION_LENGTHS
31
+
32
+ BUFFER_LENGTH_NAMES = %i[source_buffer_length destination_buffer_length].freeze
33
+ BUFFER_LENGTH_MAPPING = {
34
+ :source_buffer_length => :destination_buffer_length,
35
+ :destination_buffer_length => :source_buffer_length
36
+ }
37
+ .freeze
38
+
39
+ def test_invalid_initialize
40
+ get_invalid_decompressor_options do |invalid_options|
41
+ assert_raises ValidateError do
42
+ target.new ::StringIO.new, invalid_options
43
+ end
44
+ end
45
+
46
+ super
47
+ end
48
+
49
+ # -- synchronous --
50
+
51
+ def test_invalid_read
52
+ instance = target.new ::StringIO.new
53
+
54
+ (Validation::INVALID_NOT_NEGATIVE_INTEGERS - [nil]).each do |invalid_integer|
55
+ assert_raises ValidateError do
56
+ instance.read invalid_integer
57
+ end
58
+ end
59
+
60
+ (Validation::INVALID_STRINGS - [nil]).each do |invalid_string|
61
+ assert_raises ValidateError do
62
+ instance.read nil, invalid_string
63
+ end
64
+ end
65
+
66
+ assert_raises ValidateError do
67
+ instance = target.new Validation::StringIOWithoutRead.new
68
+ instance.read
69
+ end
70
+
71
+ assert_raises ValidateError do
72
+ instance = target.new Validation::StringIOWithoutEOF.new
73
+ instance.read
74
+ end
75
+ end
76
+
77
+ def test_read
78
+ parallel_compressor_options do |compressor_options|
79
+ TEXTS.each do |text|
80
+ archive = get_archive text, compressor_options
81
+ prev_result = "".b
82
+
83
+ Option::BOOLS.each do |with_buffer|
84
+ PORTION_LENGTHS.each do |portion_length|
85
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
86
+ instance = target.new ::StringIO.new(archive), decompressor_options
87
+ decompressed_text = "".b
88
+
89
+ begin
90
+ result = instance.read 0
91
+ assert_equal "", result
92
+
93
+ loop do
94
+ prev_eof = instance.eof?
95
+
96
+ result =
97
+ if with_buffer
98
+ instance.read portion_length, prev_result
99
+ else
100
+ instance.read portion_length
101
+ end
102
+
103
+ if result.nil?
104
+ assert_predicate instance, :eof?
105
+ break
106
+ end
107
+
108
+ refute prev_eof unless archive.bytesize.zero?
109
+
110
+ assert_equal prev_result, result if with_buffer
111
+ decompressed_text << result
112
+ end
113
+
114
+ assert_equal instance.pos, decompressed_text.bytesize
115
+ assert_equal instance.pos, instance.tell
116
+ ensure
117
+ refute_predicate instance, :closed?
118
+ instance.close
119
+ assert_predicate instance, :closed?
120
+ end
121
+
122
+ decompressed_text.force_encoding text.encoding
123
+ assert_equal text, decompressed_text
124
+ end
125
+ end
126
+
127
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
128
+ instance = target.new ::StringIO.new(archive), decompressor_options
129
+ decompressed_text = nil
130
+
131
+ begin
132
+ prev_eof = instance.eof?
133
+
134
+ if with_buffer
135
+ decompressed_text = instance.read nil, prev_result
136
+ assert_equal prev_result, decompressed_text
137
+ else
138
+ decompressed_text = instance.read
139
+ end
140
+
141
+ assert_predicate instance, :eof?
142
+ refute prev_eof unless archive.bytesize.zero?
143
+
144
+ assert_equal instance.pos, decompressed_text.bytesize
145
+ assert_equal instance.pos, instance.tell
146
+ ensure
147
+ refute_predicate instance, :closed?
148
+ instance.close
149
+ assert_predicate instance, :closed?
150
+ end
151
+
152
+ decompressed_text.force_encoding text.encoding
153
+ assert_equal text, decompressed_text
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def test_read_with_large_texts
161
+ options_generator = OCG.new(
162
+ :text => LARGE_TEXTS,
163
+ :with_buffer => Option::BOOLS
164
+ )
165
+
166
+ Common.parallel_options options_generator do |options|
167
+ text = options[:text]
168
+ with_buffer = options[:with_buffer]
169
+
170
+ archive = get_archive text
171
+ prev_result = "".b
172
+
173
+ LARGE_PORTION_LENGTHS.each do |portion_length|
174
+ instance = target.new ::StringIO.new(archive)
175
+ decompressed_text = "".b
176
+
177
+ begin
178
+ loop do
179
+ result =
180
+ if with_buffer
181
+ instance.read portion_length, prev_result
182
+ else
183
+ instance.read portion_length
184
+ end
185
+
186
+ break if result.nil?
187
+
188
+ assert_equal prev_result, result if with_buffer
189
+ decompressed_text << result
190
+ end
191
+ ensure
192
+ instance.close
193
+ end
194
+
195
+ decompressed_text.force_encoding text.encoding
196
+ assert_equal text, decompressed_text
197
+ end
198
+
199
+ instance = target.new ::StringIO.new(archive)
200
+ decompressed_text = nil
201
+
202
+ begin
203
+ if with_buffer
204
+ decompressed_text = instance.read nil, prev_result
205
+ assert_equal prev_result, decompressed_text
206
+ else
207
+ decompressed_text = instance.read
208
+ end
209
+ ensure
210
+ instance.close
211
+ end
212
+
213
+ decompressed_text.force_encoding text.encoding
214
+ assert_equal text, decompressed_text
215
+ end
216
+ end
217
+
218
+ def test_encoding
219
+ parallel_compressor_options do |compressor_options|
220
+ TEXTS.each do |text|
221
+ external_encoding = text.encoding
222
+ archive = get_archive text, compressor_options
223
+
224
+ PORTION_LENGTHS.each do |portion_length|
225
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
226
+ instance = target.new ::StringIO.new(archive), decompressor_options
227
+ decompressed_text = "".b
228
+
229
+ begin
230
+ result = instance.read 0
231
+ assert_equal Encoding::BINARY, result.encoding
232
+
233
+ loop do
234
+ result = instance.read portion_length
235
+ break if result.nil?
236
+
237
+ assert_equal Encoding::BINARY, result.encoding
238
+ decompressed_text << result
239
+ end
240
+ ensure
241
+ instance.close
242
+ end
243
+
244
+ decompressed_text.force_encoding external_encoding
245
+ assert_equal text, decompressed_text
246
+ end
247
+ end
248
+
249
+ # We don't need to transcode between same encodings.
250
+ (ENCODINGS - [external_encoding]).each do |internal_encoding|
251
+ target_text = text.encode internal_encoding, **TRANSCODE_OPTIONS
252
+
253
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
254
+ instance = target.new(
255
+ ::StringIO.new(archive),
256
+ decompressor_options,
257
+ :external_encoding => external_encoding,
258
+ :internal_encoding => internal_encoding,
259
+ :transcode_options => TRANSCODE_OPTIONS
260
+ )
261
+
262
+ assert_equal external_encoding, instance.external_encoding
263
+ assert_equal internal_encoding, instance.internal_encoding
264
+ assert_equal TRANSCODE_OPTIONS, instance.transcode_options
265
+
266
+ decompressed_text = nil
267
+
268
+ begin
269
+ instance.set_encoding external_encoding, internal_encoding, TRANSCODE_OPTIONS
270
+ assert_equal external_encoding, instance.external_encoding
271
+ assert_equal internal_encoding, instance.internal_encoding
272
+ assert_equal TRANSCODE_OPTIONS, instance.transcode_options
273
+
274
+ decompressed_text = instance.read
275
+ assert_equal internal_encoding, decompressed_text.encoding
276
+ ensure
277
+ instance.close
278
+ end
279
+
280
+ assert_equal target_text, decompressed_text
281
+ assert_predicate target_text, :valid_encoding?
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ def test_rewind
289
+ parallel_compressor_options do |compressor_options, worker_index|
290
+ archive_path = Common.get_path ARCHIVE_PATH, worker_index
291
+
292
+ TEXTS.each do |text|
293
+ write_archive archive_path, text, compressor_options
294
+
295
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
296
+ decompressed_text = nil
297
+
298
+ ::File.open archive_path, "rb" do |file|
299
+ instance = target.new file, decompressor_options
300
+
301
+ begin
302
+ result_1 = instance.read
303
+
304
+ assert_equal 0, instance.rewind
305
+ assert_equal 0, instance.pos
306
+ assert_equal instance.pos, instance.tell
307
+
308
+ result_2 = instance.read
309
+ assert_equal result_1, result_2
310
+
311
+ decompressed_text = result_1
312
+ ensure
313
+ instance.close
314
+ end
315
+ end
316
+
317
+ decompressed_text.force_encoding text.encoding
318
+ assert_equal text, decompressed_text
319
+ end
320
+ end
321
+ end
322
+ end
323
+
324
+ def test_invalid_eof
325
+ assert_raises ValidateError do
326
+ instance = target.new Validation::StringIOWithoutEOF.new
327
+ instance.eof?
328
+ end
329
+ end
330
+
331
+ def test_eof
332
+ compressed_text = String.compress "ab"
333
+ instance = target.new ::StringIO.new(compressed_text)
334
+
335
+ refute_predicate instance, :eof?
336
+
337
+ byte = instance.read 1
338
+ refute_predicate instance, :eof?
339
+ assert_equal "a", byte
340
+
341
+ byte = instance.read 1
342
+ assert_predicate instance, :eof?
343
+ assert_equal "b", byte
344
+ end
345
+
346
+ # -- asynchronous --
347
+
348
+ def test_invalid_readpartial_and_read_nonblock
349
+ instance = target.new ::StringIO.new
350
+
351
+ Validation::INVALID_NOT_NEGATIVE_INTEGERS.each do |invalid_integer|
352
+ assert_raises ValidateError do
353
+ instance.readpartial invalid_integer
354
+ end
355
+ assert_raises ValidateError do
356
+ instance.read_nonblock invalid_integer
357
+ end
358
+ end
359
+
360
+ (Validation::INVALID_STRINGS - [nil]).each do |invalid_string|
361
+ assert_raises ValidateError do
362
+ instance.readpartial 0, invalid_string
363
+ end
364
+ assert_raises ValidateError do
365
+ instance.read_nonblock 0, invalid_string
366
+ end
367
+ end
368
+
369
+ assert_raises ValidateError do
370
+ instance = target.new Validation::StringIOWithoutReadpartial.new
371
+ instance.readpartial 1
372
+ end
373
+
374
+ assert_raises ValidateError do
375
+ instance = target.new Validation::StringIOWithoutReadNonblock.new
376
+ instance.read_nonblock 1
377
+ end
378
+ end
379
+
380
+ def test_readpartial
381
+ IO.pipe do |read_io, write_io|
382
+ instance = target.new read_io
383
+ write_io.close
384
+
385
+ assert_raises ::EOFError do
386
+ instance.readpartial 1
387
+ end
388
+ end
389
+
390
+ nonblock_server do |server|
391
+ parallel_compressor_options do |compressor_options|
392
+ TEXTS.each do |text|
393
+ PORTION_LENGTHS.each do |portion_length|
394
+ Option::BOOLS.each do |with_buffer|
395
+ nonblock_test server, text, portion_length, compressor_options do |instance|
396
+ prev_result = "".b
397
+ decompressed_text = "".b
398
+
399
+ loop do
400
+ if with_buffer
401
+ result = instance.readpartial portion_length, prev_result
402
+ assert_equal prev_result, result
403
+ else
404
+ result = instance.readpartial portion_length
405
+ end
406
+
407
+ decompressed_text << result
408
+ rescue ::EOFError
409
+ break
410
+ end
411
+
412
+ decompressed_text
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end
419
+ end
420
+
421
+ def test_read_nonblock
422
+ IO.pipe do |read_io, write_io|
423
+ instance = target.new read_io
424
+
425
+ assert_raises ::IO::WaitReadable do
426
+ instance.read_nonblock 1
427
+ end
428
+
429
+ write_io.close
430
+
431
+ assert_raises ::EOFError do
432
+ instance.read_nonblock 1
433
+ end
434
+ end
435
+
436
+ nonblock_server do |server|
437
+ parallel_compressor_options do |compressor_options|
438
+ TEXTS.each do |text|
439
+ PORTION_LENGTHS.each do |portion_length|
440
+ nonblock_test server, text, portion_length, compressor_options do |instance, socket|
441
+ decompressed_text = "".b
442
+
443
+ loop do
444
+ begin
445
+ decompressed_text << instance.read_nonblock(portion_length)
446
+ rescue ::IO::WaitReadable
447
+ socket.wait_readable
448
+ retry
449
+ rescue ::EOFError
450
+ break
451
+ end
452
+
453
+ begin
454
+ decompressed_text << instance.readpartial(portion_length)
455
+ rescue ::EOFError
456
+ break
457
+ end
458
+
459
+ result = instance.read portion_length
460
+ break if result.nil?
461
+
462
+ decompressed_text << result
463
+ end
464
+
465
+ decompressed_text
466
+ end
467
+ end
468
+ end
469
+ end
470
+ end
471
+ end
472
+
473
+ def test_read_nonblock_with_large_texts
474
+ nonblock_server do |server|
475
+ Common.parallel LARGE_TEXTS do |text|
476
+ LARGE_PORTION_LENGTHS.each do |portion_length|
477
+ nonblock_test server, text, portion_length do |instance, socket|
478
+ decompressed_text = "".b
479
+
480
+ loop do
481
+ begin
482
+ decompressed_text << instance.read_nonblock(portion_length)
483
+ rescue ::IO::WaitReadable
484
+ socket.wait_readable
485
+ retry
486
+ rescue ::EOFError
487
+ break
488
+ end
489
+
490
+ begin
491
+ decompressed_text << instance.readpartial(portion_length)
492
+ rescue ::EOFError
493
+ break
494
+ end
495
+
496
+ result = instance.read portion_length
497
+ break if result.nil?
498
+
499
+ decompressed_text << result
500
+ end
501
+
502
+ decompressed_text
503
+ end
504
+ end
505
+ end
506
+ end
507
+ end
508
+
509
+ # -- nonblock test --
510
+
511
+ protected def nonblock_server
512
+ # Server need just to redirect content for client.
513
+
514
+ ::TCPServer.open 0 do |server|
515
+ # Server loop will be processed in separate (parent) thread.
516
+ # Child threads will be collected for later usage.
517
+ child_lock = ::Mutex.new
518
+ child_threads = ::Set.new
519
+
520
+ parent_thread = ::Thread.new do
521
+ loop do
522
+ child_thread = ::Thread.start server.accept do |socket|
523
+ result = "".b
524
+
525
+ # Reading head.
526
+ result_size, portion_length = socket.read(16).unpack "QQ"
527
+ next if result_size.zero?
528
+
529
+ # Reading result.
530
+ loop do
531
+ begin
532
+ result << socket.read_nonblock(portion_length)
533
+ rescue ::IO::WaitReadable
534
+ socket.wait_readable
535
+ retry
536
+ end
537
+
538
+ break if result.bytesize == result_size
539
+ end
540
+
541
+ # Writing result.
542
+ loop do
543
+ begin
544
+ bytes_written = socket.write_nonblock result
545
+ rescue ::IO::WaitWritable
546
+ socket.wait_writable
547
+ retry
548
+ end
549
+
550
+ result = result.byteslice bytes_written, result.bytesize - bytes_written
551
+ result_size -= bytes_written
552
+
553
+ break if result_size.zero?
554
+ end
555
+
556
+ ensure
557
+ socket.close
558
+
559
+ # Removing current child thread.
560
+ child_lock.synchronize { child_threads.delete ::Thread.current }
561
+ end
562
+
563
+ # Adding new child thread.
564
+ child_lock.synchronize { child_threads.add child_thread }
565
+ end
566
+ end
567
+
568
+ # Processing client.
569
+ begin
570
+ yield server
571
+ ensure
572
+ # We need to kill parent thread when client has finished.
573
+ # So server won't be able to create new child threads.
574
+ # Than we can join all remaining child threads.
575
+ parent_thread.kill.join
576
+ child_threads.each(&:join)
577
+ end
578
+ end
579
+ end
580
+
581
+ protected def nonblock_test(server, text, portion_length, compressor_options = {}, &_block)
582
+ port = server.addr[1]
583
+ compressed_text = String.compress text, compressor_options
584
+
585
+ processor = proc do |decompressor_options|
586
+ decompressed_text = ::TCPSocket.open "localhost", port do |socket|
587
+ # Writing head.
588
+ head = [compressed_text.bytesize, portion_length].pack "QQ"
589
+ socket.write head
590
+
591
+ # Writing compressed text.
592
+ socket.write compressed_text
593
+
594
+ instance = target.new socket, decompressor_options
595
+
596
+ begin
597
+ yield instance, socket
598
+ ensure
599
+ instance.close
600
+ end
601
+ end
602
+
603
+ # Testing decompressed text.
604
+ decompressed_text.force_encoding text.encoding
605
+ assert_equal text, decompressed_text
606
+ end
607
+
608
+ if compressor_options.empty?
609
+ processor.call({})
610
+ else
611
+ get_compatible_decompressor_options compressor_options do |decompressor_options|
612
+ processor.call decompressor_options
613
+ end
614
+ end
615
+ end
616
+
617
+ # -----
618
+
619
+ protected def get_archive(text, compressor_options = {})
620
+ String.compress text, compressor_options
621
+ end
622
+
623
+ protected def write_archive(archive_path, text, compressor_options = {})
624
+ compressed_text = String.compress text, compressor_options
625
+ ::File.write archive_path, compressed_text, :mode => "wb"
626
+ end
627
+
628
+ def get_invalid_decompressor_options(&block)
629
+ Option.get_invalid_decompressor_options BUFFER_LENGTH_NAMES, &block
630
+ end
631
+
632
+ def parallel_compressor_options(&block)
633
+ Common.parallel_options Option.get_compressor_options_generator(BUFFER_LENGTH_NAMES), &block
634
+ end
635
+
636
+ def get_compatible_decompressor_options(compressor_options, &block)
637
+ Option.get_compatible_decompressor_options compressor_options, BUFFER_LENGTH_MAPPING, &block
638
+ end
639
+ end
640
+ end
641
+ end
642
+ end