axon 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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