axon 0.0.2 → 0.1.0

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 (56) hide show
  1. data/CHANGELOG.rdoc +9 -3
  2. data/README.rdoc +29 -36
  3. data/Rakefile +26 -21
  4. data/TODO.rdoc +1 -6
  5. data/ext/axon/axon.c +6 -15
  6. data/ext/axon/extconf.rb +19 -9
  7. data/ext/axon/interpolation.c +147 -0
  8. data/ext/axon/jpeg.c +1207 -0
  9. data/ext/axon/png.c +542 -0
  10. data/lib/axon.rb +235 -32
  11. data/lib/axon/cropper.rb +80 -18
  12. data/lib/axon/fit.rb +69 -19
  13. data/lib/axon/generators.rb +109 -0
  14. data/lib/axon/scalers.rb +160 -0
  15. data/test/helper.rb +151 -6
  16. data/test/reader_tests.rb +37 -82
  17. data/test/scaler_tests.rb +102 -0
  18. data/test/stress_helper.rb +58 -0
  19. data/test/stress_tests.rb +8 -5
  20. data/test/test_bilinear_scaler.rb +60 -2
  21. data/test/test_cropper.rb +68 -1
  22. data/test/test_fit.rb +35 -0
  23. data/test/test_generators.rb +21 -0
  24. data/test/test_image.rb +61 -0
  25. data/test/test_jpeg_reader.rb +96 -94
  26. data/test/test_jpeg_writer.rb +95 -8
  27. data/test/test_nearest_neighbor_scaler.rb +28 -4
  28. data/test/test_png_reader.rb +12 -8
  29. data/test/test_png_writer.rb +8 -6
  30. data/test/writer_tests.rb +129 -111
  31. metadata +71 -128
  32. data/.gemtest +0 -0
  33. data/ext/axon/bilinear_interpolation.c +0 -115
  34. data/ext/axon/interpolation.h +0 -7
  35. data/ext/axon/jpeg_common.c +0 -118
  36. data/ext/axon/jpeg_common.h +0 -37
  37. data/ext/axon/jpeg_native_writer.c +0 -248
  38. data/ext/axon/jpeg_reader.c +0 -774
  39. data/ext/axon/nearest_neighbor_interpolation.c +0 -50
  40. data/ext/axon/png_common.c +0 -21
  41. data/ext/axon/png_common.h +0 -18
  42. data/ext/axon/png_native_writer.c +0 -166
  43. data/ext/axon/png_reader.c +0 -381
  44. data/lib/axon/axon.so +0 -0
  45. data/lib/axon/bilinear_scaler.rb +0 -60
  46. data/lib/axon/jpeg_writer.rb +0 -41
  47. data/lib/axon/nearest_neighbor_scaler.rb +0 -39
  48. data/lib/axon/png_writer.rb +0 -35
  49. data/lib/axon/scaler.rb +0 -41
  50. data/lib/axon/solid.rb +0 -23
  51. data/test/_test_readme.rb +0 -34
  52. data/test/test_exif.rb +0 -39
  53. data/test/test_generator.rb +0 -10
  54. data/test/test_icc.rb +0 -18
  55. data/test/test_jpeg.rb +0 -9
  56. data/test/test_png.rb +0 -9
@@ -1,108 +1,110 @@
1
1
  require 'helper'
2
+ require 'reader_tests'
2
3
 
3
4
  module Axon
4
- class TestJPEGReader < AxonTestCase
5
- include ReaderTests
6
-
7
- def setup
8
- super
9
- @readerclass = JPEGReader
10
- @data = @image.to_jpeg.data
11
- @io_in = StringIO.new @data
12
- @reader = @readerclass.new(@io_in)
13
- end
5
+ module JPEG
6
+ class TestJPEGReader < AxonTestCase
7
+ include ReaderTests
14
8
 
15
- def test_in_color_model
16
- assert_equal :YCbCr, @reader.in_color_model
17
- end
18
-
19
- def test_set_in_color_model
20
- @reader.in_color_model = :RGB
21
- assert_equal :RGB, @reader.in_color_model
22
-
23
- assert_raises(RuntimeError){ @reader.in_color_model = "foobar" }
24
- end
25
-
26
- def test_set_out_color_model
27
- @reader.color_model = :YCbCr
28
- assert_equal :YCbCr, @reader.color_model
29
-
30
- assert_raises(RuntimeError){ @reader.color_model = "foobar" }
31
- end
32
-
33
- def test_scale_num
34
- assert @reader.scale_num > 0
35
- end
36
-
37
- def test_set_scale_num
38
- @reader.scale_num = 5
39
- assert_equal 5, @reader.scale_num
40
- end
41
-
42
- def test_scale_denom
43
- assert @reader.scale_denom > 0
44
- end
45
-
46
- def test_set_scale_denom
47
- @reader.scale_denom = 8
48
-
49
- assert_equal 8, @reader.scale_denom
50
- end
51
-
52
- def test_scale_denom_affects_image_size
53
- pre_width = @reader.width
54
- pre_height = @reader.height
55
-
56
- @reader.scale_denom = 2
57
-
58
- assert @reader.width < pre_width
59
- assert @reader.height < pre_height
60
- end
9
+ def setup
10
+ super
11
+ io = StringIO.new
12
+ JPEG.write(@image, io)
13
+ @data = io.string
14
+ @readerclass = Reader
15
+ @reader = Reader.new(StringIO.new(@data))
16
+ end
61
17
 
62
- def test_scale_denom_affects_written_image
63
- pre_width = @reader.width
64
- pre_height = @reader.height
18
+ def test_in_color_model
19
+ assert_equal :YCbCr, @reader.in_color_model
20
+ end
65
21
 
66
- writer = JPEGWriter.new(@reader)
67
- @reader.scale_denom = 2
22
+ def test_set_in_color_model
23
+ @reader.in_color_model = :RGB
24
+ assert_equal :RGB, @reader.in_color_model
68
25
 
69
- new_velvet_io = StringIO.new(writer.data)
26
+ assert_raises(RuntimeError){ @reader.in_color_model = "foobar" }
27
+ end
70
28
 
71
- new_velvet_reader = JPEGReader.new(new_velvet_io)
29
+ def test_set_out_color_model
30
+ @reader.color_model = :YCbCr
31
+ assert_equal :YCbCr, @reader.color_model
72
32
 
73
- assert new_velvet_reader.width < pre_width
74
- assert new_velvet_reader.height < pre_height
75
- end
76
-
77
- def test_dct_method
78
- assert_equal JPEGReader::DEFAULT_DCT, @reader.dct_method
79
- end
80
-
81
- def test_set_dct_method
82
- @reader.dct_method = :IFAST
83
-
84
- assert_equal :IFAST, @reader.dct_method
85
- end
86
-
87
- def test_markers_read_by_default
88
- refute_empty @reader[:APP0]
89
- end
90
-
91
- def test_empty_marker_prevents_reads
92
- @io_in.rewind
93
- r = JPEGReader.new @io_in, []
94
- assert_empty r[:APP0]
95
- end
96
-
97
- def test_marker_read
98
- assert_match(/^JFIF/, @reader[:APP0].first)
99
- end
100
-
101
- def test_no_configuration_after_initiated
102
- @reader.each do |sl|
33
+ assert_raises(RuntimeError){ @reader.color_model = "foobar" }
34
+ end
35
+
36
+ def test_scale_num
37
+ assert @reader.scale_num > 0
38
+ end
39
+
40
+ def test_set_scale_num
41
+ @reader.scale_num = 5
42
+ assert_equal 5, @reader.scale_num
43
+ end
44
+
45
+ unless LIB_TURBO
46
+ def test_scale_denom
47
+ assert @reader.scale_denom > 0
48
+ end
49
+
50
+ def test_set_scale_denom
51
+ @reader.scale_denom = 8
52
+ assert_equal 8, @reader.scale_denom
53
+ end
54
+
55
+ def test_scale_denom_affects_image_size
56
+ pre_width = @reader.width
57
+ pre_height = @reader.height
58
+
59
+ @reader.scale_denom = 2
60
+
61
+ assert @reader.width < pre_width
62
+ assert @reader.height < pre_height
63
+ end
64
+
65
+ def test_scale_denom_affects_written_image
66
+ pre_width = @reader.width
67
+ pre_height = @reader.height
68
+
69
+ @reader.scale_denom = 2
70
+
71
+ io_out = StringIO.new
72
+ JPEG.write(@reader, io_out)
73
+ io_out.rewind
74
+
75
+ new_velvet_reader = Reader.new(io_out)
76
+
77
+ assert new_velvet_reader.width < pre_width
78
+ assert new_velvet_reader.height < pre_height
79
+ end
80
+ end
81
+
82
+ def test_dct_method
83
+ assert_equal Reader::DEFAULT_DCT, @reader.dct_method
84
+ end
85
+
86
+ def test_set_dct_method
87
+ @reader.dct_method = :IFAST
88
+ assert_equal :IFAST, @reader.dct_method
89
+ end
90
+
91
+ def test_markers_read_by_default
92
+ refute_empty @reader[:APP0]
93
+ end
94
+
95
+ def test_empty_marker_prevents_reads
96
+ r = Reader.new StringIO.new(@data), []
97
+ assert_empty r[:APP0]
98
+ end
99
+
100
+ def test_marker_content
101
+ assert_match(/^JFIF/, @reader[:APP0].first)
102
+ end
103
+
104
+ def test_no_configuration_after_initiated
105
+ @reader.gets
103
106
  assert_raises(RuntimeError) { @reader.dct_method = :IFAST }
104
107
  assert_raises(RuntimeError) { @reader.scale_denom = 4 }
105
- break
106
108
  end
107
109
  end
108
110
  end
@@ -1,4 +1,5 @@
1
1
  require 'helper'
2
+ require 'writer_tests'
2
3
 
3
4
  module Axon
4
5
  class TestJPEGWriter < AxonTestCase
@@ -6,21 +7,107 @@ module Axon
6
7
 
7
8
  def setup
8
9
  super
9
- @writerclass = JPEGWriter
10
- @writer = @image.to_jpeg
10
+ @mod = JPEG
11
11
  end
12
12
 
13
13
  def test_output_has_jpeg_header
14
- string = @writer.data
15
-
16
- assert_equal "\xFF\xD8\xFF\xE0", string[0..3]
17
- assert_equal "JFIF\x00", string[6..10]
14
+ JPEG.write(@image, @io_out)
15
+ assert_equal "\xFF\xD8\xFF\xE0", @io_out.string[0..3]
16
+ assert_equal "JFIF\x00", @io_out.string[6..10]
18
17
  end
19
18
 
20
- def test_bad_bufsize
19
+ def test_invalid_bufsize
21
20
  assert_raises RuntimeError do
22
- @writer.write(@io_out, 0)
21
+ JPEG.write(@image, @io_out, :bufsize => 0)
22
+ end
23
+
24
+ assert_raises RuntimeError do
25
+ JPEG.write(@image, @io_out, :bufsize => -5)
26
+ end
27
+ end
28
+
29
+ def test_symbol_bufsize
30
+ assert_raises TypeError do
31
+ JPEG.write(@image, @io_out, :bufsize => :foo)
32
+ end
33
+ end
34
+
35
+ def test_quality_filesize
36
+ file_sizes = {}
37
+ [-10, 0, 1, 50, 100, 130].each do |q|
38
+ im = Solid.new(100, 200)
39
+ io = StringIO.new
40
+ JPEG.write(im, io, :quality => q)
41
+ file_sizes[q] = io.size
23
42
  end
43
+
44
+ assert file_sizes[-10] > 1
45
+
46
+ # libjpeg treats values under 1 as 1
47
+ assert_equal file_sizes[-10], file_sizes[0]
48
+ assert_equal file_sizes[0], file_sizes[1]
49
+
50
+ assert file_sizes[50] > file_sizes[1]
51
+ assert file_sizes[100] > file_sizes[50]
52
+
53
+ # libjpeg treats values over 100 as 100
54
+ assert_equal file_sizes[130], file_sizes[100]
55
+ end
56
+
57
+ def test_symbol_quality
58
+ assert_raises TypeError do
59
+ JPEG.write(@image, @io_out, :quality => :foo)
60
+ end
61
+ end
62
+
63
+ def test_exif_roundtrip
64
+ random_data = ""
65
+ (100).times do
66
+ random_data << [rand].pack('d') # 800 bytes random data
67
+ end
68
+
69
+ io_out = StringIO.new
70
+ JPEG.write(@image, io_out, :exif => random_data)
71
+ io_out.rewind
72
+
73
+ image_with_exif = JPEG::Reader.new(io_out)
74
+ assert_equal random_data, image_with_exif.exif
75
+ end
76
+
77
+ def test_exif_with_icc_roundtrip
78
+ random_icc_data = ""
79
+ (2**16).times do # a little larger than one jpeg segment
80
+ random_icc_data << [rand].pack('d') # 8 bytes random data
81
+ end
82
+
83
+ random_exif_data = ""
84
+ (100).times do
85
+ random_exif_data << [rand].pack('d') # 800 bytes random data
86
+ end
87
+
88
+ io_out = StringIO.new
89
+ JPEG.write(@image, io_out, :icc_profile => random_icc_data,
90
+ :exif => random_exif_data)
91
+ io_out.rewind
92
+
93
+ image_with_exif_and_icc = JPEG::Reader.new(io_out)
94
+
95
+ assert_equal random_exif_data, image_with_exif_and_icc.exif
96
+ assert_equal random_icc_data, image_with_exif_and_icc.icc_profile
97
+ end
98
+
99
+ def test_large_icc_roundtrip
100
+ random_data = ""
101
+ (2**16).times do # a little larger than one jpeg segment
102
+ random_data << [rand].pack('d') # 8 bytes random data
103
+ end
104
+
105
+ io_out = StringIO.new
106
+ JPEG.write(@image, io_out, :icc_profile => random_data)
107
+ io_out.rewind
108
+
109
+ image_with_icc = JPEG::Reader.new(io_out)
110
+ assert_equal random_data, image_with_icc.icc_profile
24
111
  end
25
112
  end
26
113
  end
@@ -1,13 +1,37 @@
1
1
  require 'helper'
2
+ require 'scaler_tests'
2
3
 
3
4
  module Axon
4
5
  class TestNearestNeighborScaler < AxonTestCase
5
- def test_resize_ratio
6
- @image.scale_nearest_neighbor(0.9).write_jpeg(@io_out)
6
+ include ScalerTests
7
+
8
+ def setup
9
+ super
10
+ @scalerclass = NearestNeighborScaler
11
+ @scalertestclass = TestNearestNeighborScaler
7
12
  end
8
13
 
9
- def test_resize_dimensions
10
- @image.scale_nearest_neighbor(100, 200).write_jpeg(@io_out)
14
+ class TestNearestNeighborScaler
15
+ def initialize(original, width, height)
16
+ @original = original
17
+ @scale_x_inv = original.width / width.to_f
18
+ @scale_y_inv = original.height / height.to_f
19
+ @data = []
20
+ @original.height.times do
21
+ sl = @original.gets
22
+ @data << sl + sl[-@original.components, @original.components]
23
+ end
24
+ @data << @data.last
25
+ end
26
+
27
+ def calc(x, y)
28
+ smp_x = (x * @scale_x_inv).floor
29
+ smp_y = (y * @scale_y_inv).floor
30
+ cmp = @original.components
31
+ @data[smp_y][smp_x * cmp, cmp].chars.map do |c|
32
+ c.respond_to?(:ord) ? c.ord : c[0]
33
+ end
34
+ end
11
35
  end
12
36
  end
13
37
  end
@@ -1,15 +1,19 @@
1
1
  require 'helper'
2
+ require 'reader_tests'
2
3
 
3
4
  module Axon
4
- class TestPNGReader < AxonTestCase
5
- include ReaderTests
5
+ module PNG
6
+ class TestPNGReader < AxonTestCase
7
+ include ReaderTests
6
8
 
7
- def setup
8
- super
9
- @readerclass = PNGReader
10
- @data = @image.to_png.data
11
- @io_in = StringIO.new @data
12
- @reader = PNGReader.new(@io_in)
9
+ def setup
10
+ super
11
+ io = StringIO.new
12
+ PNG.write(@image, io)
13
+ @data = io.string
14
+ @readerclass = Reader
15
+ @reader = Reader.new(StringIO.new(@data))
16
+ end
13
17
  end
14
18
  end
15
19
  end
@@ -1,13 +1,15 @@
1
1
  require 'helper'
2
+ require 'writer_tests'
2
3
 
3
4
  module Axon
4
- class TestPNGWriter < AxonTestCase
5
- include WriterTests
5
+ module PNG
6
+ class TestPNGWriter < AxonTestCase
7
+ include WriterTests
6
8
 
7
- def setup
8
- super
9
- @writerclass = PNGWriter
10
- @writer = @image.to_png
9
+ def setup
10
+ super
11
+ @mod = PNG
12
+ end
11
13
  end
12
14
  end
13
15
  end
data/test/writer_tests.rb CHANGED
@@ -1,178 +1,196 @@
1
1
  module Axon
2
2
  module WriterTests
3
3
  def test_writes_something
4
- @writer.write(@io_out)
4
+ @mod.write(@image, @io_out)
5
5
  refute @io_out.string.empty?
6
6
  end
7
7
 
8
- class ZeroHeightImage < Solid
9
- def height; 0; end
8
+ def test_returns_bytes_written
9
+ ret = @mod.write(@image, @io_out)
10
+ assert_equal @io_out.size, ret
10
11
  end
11
12
 
12
- class NegativeHeightImage < Solid
13
- def height; -1; end
13
+ def test_invalid_numeric_height
14
+ [0, -100, 0.0001].each do |h|
15
+ assert_raises RuntimeError do
16
+ @mod.write(CustomHeightImage.new(h), @io_out)
17
+ end
18
+ end
14
19
  end
15
20
 
16
- class NilHeightImage < Solid
17
- def height; nil; end
21
+ def test_invalid_height_type
22
+ [nil, :foo].each do |h|
23
+ assert_raises TypeError, "should throw a TypeError when given a #{h.class} for height." do
24
+ @mod.write(CustomHeightImage.new(h), @io_out)
25
+ end
26
+ end
18
27
  end
19
28
 
20
- def test_bad_dimension
21
- i = ZeroHeightImage.new 10, 15, @velvet
22
- writer = @writerclass.new(i)
23
-
24
- assert_raises RuntimeError do
25
- writer.write(@io_out)
29
+ def test_height_raises_exception
30
+ im = CustomHeightImage.new(Proc.new{ raise CustomError })
31
+ assert_raises CustomError do
32
+ @mod.write(im, @io_out)
26
33
  end
27
-
28
- i = NegativeHeightImage.new 10, 15, @velvet
29
- writer = @writerclass.new(i)
34
+ end
30
35
 
31
- assert_raises RuntimeError do
32
- writer.write(@io_out)
36
+ def test_invalid_numeric_width
37
+ [0, -100, 0.0001].each do |w|
38
+ assert_raises RuntimeError do
39
+ @mod.write(CustomWidthImage.new(w), @io_out)
40
+ end
33
41
  end
42
+ end
34
43
 
35
- i = NilHeightImage.new 10, 15, @velvet
36
- writer = @writerclass.new(i)
37
-
38
- assert_raises TypeError do
39
- writer.write(@io_out)
44
+ def test_invalid_width_type
45
+ [nil, :foo].each do |w|
46
+ assert_raises TypeError, "should throw a TypeError when given a #{w.class} for width." do
47
+ @mod.write(CustomWidthImage.new(w), @io_out)
48
+ end
40
49
  end
41
50
  end
42
51
 
43
- class NilColorModelImage < Solid
44
- def color_model; nil; end
52
+ def test_width_raises_exception
53
+ im = CustomWidthImage.new(Proc.new{ raise CustomError })
54
+ assert_raises CustomError do
55
+ @mod.write(im, @io_out)
56
+ end
45
57
  end
46
58
 
47
- def test_bad_color_model
48
- i = NilColorModelImage.new 10, 15, @velvet
49
- writer = @writerclass.new(i)
50
-
51
- assert_raises RuntimeError do
52
- writer.write(@io_out)
59
+ def test_nil_image_color_model
60
+ assert_raises TypeError do
61
+ @mod.write(CustomColorModelImage.new(nil), @io_out)
53
62
  end
54
63
  end
55
64
 
56
- class TooManyComponentsImage < Solid
57
- def components; 15; end
65
+ def test_invalid_color_model
66
+ assert_raises RuntimeError do
67
+ @mod.write(CustomColorModelImage.new(:foo), @io_out)
68
+ end
58
69
  end
59
70
 
60
- class ZeroComponentsImage < Solid
61
- def components; 0; end
71
+ def test_color_model_raises_exception
72
+ im = CustomColorModelImage.new(Proc.new{ raise CustomError })
73
+ assert_raises CustomError do
74
+ @mod.write(im, @io_out)
75
+ end
62
76
  end
63
77
 
64
- class NilComponentsImage < Solid
65
- def components; nil; end
78
+ def test_invalid_image_components
79
+ [0, -100, 0.0001, 15].each do |w|
80
+ assert_raises RuntimeError do
81
+ @mod.write(CustomComponentsImage.new(w), @io_out)
82
+ end
83
+ end
66
84
  end
67
85
 
68
- def test_bad_components
69
- i = NilComponentsImage.new 10, 15, @velvet
70
- writer = @writerclass.new(i)
71
-
72
- assert_raises TypeError do
73
- writer.write(@io_out)
86
+ def test_invalid_components_type
87
+ [nil, :foo].each do |w|
88
+ assert_raises TypeError do
89
+ @mod.write(CustomComponentsImage.new(w), @io_out)
90
+ end
74
91
  end
92
+ end
75
93
 
76
- i = TooManyComponentsImage.new 10, 15, @velvet
77
- writer = @writerclass.new(i)
78
-
79
- assert_raises RuntimeError do
80
- writer.write(@io_out)
94
+ def test_invalid_components_type
95
+ [nil, :foo].each do |w|
96
+ assert_raises TypeError, "should throw a TypeError when given a #{w.class} for components." do
97
+ @mod.write(CustomComponentsImage.new(w), @io_out)
98
+ end
81
99
  end
100
+ end
82
101
 
83
- i = ZeroComponentsImage.new 10, 15, @velvet
84
- writer = @writerclass.new(i)
85
-
86
- assert_raises RuntimeError do
87
- writer.write(@io_out)
102
+ def test_components_raises_exception
103
+ im = CustomComponentsImage.new(Proc.new{ raise CustomError })
104
+ assert_raises CustomError do
105
+ @mod.write(im, @io_out)
88
106
  end
89
107
  end
90
-
91
- class RaisingIO
92
- def write(data)
93
- raise 'hell'
108
+
109
+ def test_invalid_lineno_type
110
+ [-1, nil, :foo].each do |l|
111
+ # we don't know if the writer will care but test anyways to detect
112
+ # interpreter crashes and mem leaks.
113
+ @mod.write(CustomLinenoImage.new(l), @io_out)
94
114
  end
95
115
  end
96
-
97
- def test_io_raises_exception
98
- io = RaisingIO.new
99
-
100
- assert_raises RuntimeError do
101
- @writer.write(io)
116
+
117
+ def test_odd_gets_type
118
+ [nil, :foo, 1234].each do |l|
119
+ assert_raises RuntimeError do
120
+ @mod.write(CustomGetsImage.new(l), @io_out)
121
+ end
102
122
  end
103
123
  end
104
-
105
- class NilImage < Solid
106
- def each
107
- 10.times{ yield nil }
124
+
125
+ def test_gets_raises_exception_immediately
126
+ im = CustomGetsImage.new(Proc.new{ raise CustomError })
127
+ assert_raises CustomError do
128
+ @mod.write(im, @io_out)
108
129
  end
109
130
  end
110
131
 
111
- def test_image_scanlines_yield_nil
112
- image = NilImage.new(10, 15, @velvet)
113
- writer = @writerclass.new(image)
114
-
115
- assert_raises RuntimeError do
116
- writer.write(@io_out)
132
+ def test_gets_raises_exception_later
133
+ proc = Proc.new{ |im| im.lineno > 3 ? raise(CustomError) : im.gets }
134
+ i = CustomGetsImage.new(proc)
135
+ assert_raises CustomError do
136
+ @mod.write(i, @io_out)
117
137
  end
138
+ assert_equal 4, i.lineno
118
139
  end
119
140
 
120
- class TooManyScanLinesImage < Solid
121
- def each
122
- super
123
- yield(@color * width)
141
+ def test_gets_return_is_too_short_immediately
142
+ proc = Proc.new{ |im| im.gets[2, -1] }
143
+ assert_raises RuntimeError do
144
+ @mod.write(CustomGetsImage.new(proc), @io_out)
124
145
  end
125
146
  end
126
147
 
127
- def test_image_yields_too_many_scanlines
128
- image = TooManyScanLinesImage.new 10, 15, @velvet
129
- writer = @writerclass.new(image)
130
- writer.write(@io_out)
131
- end
132
-
133
- class TooFewScanLinesImage < Solid
134
- def each
135
- yield(@color * width)
148
+ def test_gets_return_is_too_short_later
149
+ proc = Proc.new{ |im| im.lineno > 3 ? im.gets[2, -1] : im.gets }
150
+ i = CustomGetsImage.new(proc)
151
+ assert_raises RuntimeError do
152
+ @mod.write(i, @io_out)
136
153
  end
154
+ assert_equal 5, i.lineno
137
155
  end
138
156
 
139
- def test_image_yields_too_few_scanlines
140
- image = TooFewScanLinesImage.new 10, 15, @velvet
141
- writer = @writerclass.new(image)
142
-
157
+ def test_gets_return_is_too_long_immediately
158
+ proc = Proc.new{ |im| im.gets * 2 }
143
159
  assert_raises RuntimeError do
144
- writer.write(@io_out)
160
+ @mod.write(CustomGetsImage.new(proc), @io_out)
145
161
  end
146
162
  end
147
163
 
148
- class RaisingImage < Solid
149
- def each
150
- raise 'chickens'
164
+ def test_gets_return_is_too_long_later
165
+ proc = Proc.new{ |im| im.lineno > 3 ? im.gets * 2 : im.gets }
166
+ i = CustomGetsImage.new(proc)
167
+ assert_raises RuntimeError do
168
+ @mod.write(i, @io_out)
151
169
  end
170
+ assert_equal 5, i.lineno
152
171
  end
153
-
154
- def test_image_scanlines_raises_exception
155
- image = RaisingImage.new(10, 15, @velvet)
156
- writer = @writerclass.new(image)
157
172
 
158
- assert_raises RuntimeError do
159
- writer.write(@io_out)
173
+ def test_io_raises_exception_immediately
174
+ io = CustomIO.new(Proc.new{ raise CustomError })
175
+ assert_raises CustomError do
176
+ @mod.write(@image, io)
160
177
  end
161
178
  end
162
-
163
- class BigWidthImage < Solid
164
- def each
165
- sl = @color * width * 10
166
- height.times { yield sl }
179
+
180
+ def test_io_returns_invalid_type
181
+ [nil, :foo, "bar"].each do |r|
182
+ im = Solid.new(200, 100)
183
+ assert_raises TypeError, "should get a TypeError when IO#write returns a #{r.class}." do
184
+ @mod.write(im, CustomIO.new(r))
185
+ end
167
186
  end
168
187
  end
169
-
170
- def test_image_scanlines_returns_too_much
171
- image = BigWidthImage.new(10, 15, @velvet)
172
- writer = @writerclass.new(image)
173
188
 
174
- assert_raises RuntimeError do
175
- writer.write(@io_out)
189
+ def test_io_returns_invalid_length
190
+ [0, -1, -100, 2000, 1].each do |r|
191
+ assert_raises RuntimeError do
192
+ @mod.write(@image, CustomIO.new(r))
193
+ end
176
194
  end
177
195
  end
178
196
  end