adsp 1.0.1 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/{test → lib/adsp/test}/common.rb +2 -1
  4. data/{test → lib/adsp/test}/coverage_helper.rb +0 -0
  5. data/lib/adsp/test/file.rb +120 -0
  6. data/{test → lib/adsp/test}/minitest.rb +1 -0
  7. data/{test → lib/adsp/test}/mock/common.rb +1 -0
  8. data/{test → lib/adsp/test}/mock/file.rb +1 -0
  9. data/{test → lib/adsp/test}/mock/stream/raw/compressor.rb +1 -0
  10. data/{test → lib/adsp/test}/mock/stream/raw/decompressor.rb +1 -0
  11. data/{test → lib/adsp/test}/mock/stream/raw/native_compressor.rb +1 -0
  12. data/{test → lib/adsp/test}/mock/stream/raw/native_decompressor.rb +1 -0
  13. data/{test → lib/adsp/test}/mock/stream/reader.rb +1 -0
  14. data/{test → lib/adsp/test}/mock/stream/writer.rb +1 -0
  15. data/{test → lib/adsp/test}/mock/string.rb +1 -0
  16. data/{test → lib/adsp/test}/option.rb +1 -0
  17. data/{test → lib/adsp/test}/stream/abstract.rb +1 -0
  18. data/lib/adsp/test/stream/minitar.rb +50 -0
  19. data/{test → lib/adsp/test}/stream/raw/abstract.rb +1 -0
  20. data/lib/adsp/test/stream/raw/compressor.rb +165 -0
  21. data/lib/adsp/test/stream/raw/decompressor.rb +165 -0
  22. data/lib/adsp/test/stream/reader.rb +642 -0
  23. data/lib/adsp/test/stream/reader_helpers.rb +421 -0
  24. data/lib/adsp/test/stream/writer.rb +609 -0
  25. data/lib/adsp/test/stream/writer_helpers.rb +267 -0
  26. data/lib/adsp/test/string.rb +95 -0
  27. data/{test → lib/adsp/test}/validation.rb +7 -0
  28. data/lib/adsp/test/version.rb +18 -0
  29. data/lib/adsp/version.rb +1 -1
  30. data/test/file.test.rb +3 -116
  31. data/test/stream/minitar.test.rb +3 -46
  32. data/test/stream/raw/compressor.test.rb +3 -162
  33. data/test/stream/raw/decompressor.test.rb +3 -162
  34. data/test/stream/reader.test.rb +3 -639
  35. data/test/stream/reader_helpers.test.rb +3 -417
  36. data/test/stream/writer.test.rb +3 -606
  37. data/test/stream/writer_helpers.test.rb +3 -263
  38. data/test/string.test.rb +3 -91
  39. data/test/version.test.rb +3 -14
  40. 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