ruby_spriter 0.6.6 → 0.6.7.1

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.
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubySpriter::BatchProcessor do
6
+ let(:test_dir) { 'E:/test/videos' }
7
+ let(:output_dir) { 'E:/test/output' }
8
+ let(:video1) { File.join(test_dir, 'video1.mp4') }
9
+ let(:video2) { File.join(test_dir, 'video2.mp4') }
10
+ let(:video3) { File.join(test_dir, 'video3.mp4') }
11
+
12
+ let(:options) do
13
+ {
14
+ batch: true,
15
+ dir: test_dir,
16
+ columns: 4,
17
+ frame_count: 16,
18
+ max_width: 320,
19
+ overwrite: false,
20
+ debug: false
21
+ }
22
+ end
23
+
24
+ before do
25
+ # Mock file system
26
+ allow(Dir).to receive(:exist?).and_return(true)
27
+ allow(File).to receive(:exist?).and_return(true)
28
+ allow(File).to receive(:directory?).with(test_dir).and_return(true)
29
+ allow(Dir).to receive(:glob).with(File.join(test_dir, '*.mp4')).and_return([video1, video2, video3])
30
+ end
31
+
32
+ describe '#initialize' do
33
+ it 'raises error if directory does not exist' do
34
+ allow(File).to receive(:directory?).with(test_dir).and_return(false)
35
+
36
+ expect {
37
+ described_class.new(options)
38
+ }.to raise_error(RubySpriter::ValidationError, /Directory not found/)
39
+ end
40
+
41
+ it 'initializes with valid options' do
42
+ processor = described_class.new(options)
43
+ expect(processor.options).to eq(options)
44
+ end
45
+ end
46
+
47
+ describe '#find_videos' do
48
+ it 'finds all MP4 files in directory' do
49
+ processor = described_class.new(options)
50
+ videos = processor.find_videos
51
+
52
+ expect(videos).to eq([video1, video2, video3])
53
+ end
54
+
55
+ it 'raises error if no videos found' do
56
+ allow(Dir).to receive(:glob).with(File.join(test_dir, '*.mp4')).and_return([])
57
+
58
+ processor = described_class.new(options)
59
+
60
+ expect {
61
+ processor.find_videos
62
+ }.to raise_error(RubySpriter::ValidationError, /No MP4 files found/)
63
+ end
64
+ end
65
+
66
+ describe '#process' do
67
+ let(:video_processor_mock) { instance_double(RubySpriter::VideoProcessor) }
68
+
69
+ before do
70
+ allow(RubySpriter::VideoProcessor).to receive(:new).and_return(video_processor_mock)
71
+ allow(video_processor_mock).to receive(:create_spritesheet).and_return(
72
+ { output_file: 'output.png', columns: 4, rows: 4, frames: 16 }
73
+ )
74
+ allow(File).to receive(:exist?).and_return(false) # No existing files
75
+ allow(RubySpriter::Utils::FileHelper).to receive(:spritesheet_filename) do |video|
76
+ video.gsub('.mp4', '_spritesheet.png')
77
+ end
78
+ end
79
+
80
+ it 'processes all videos in directory' do
81
+ processor = described_class.new(options)
82
+
83
+ expect(video_processor_mock).to receive(:create_spritesheet).exactly(3).times
84
+
85
+ results = processor.process
86
+
87
+ expect(results[:processed_count]).to eq(3)
88
+ expect(results[:outputs].length).to eq(3)
89
+ end
90
+
91
+ it 'outputs to same directory by default' do
92
+ processor = described_class.new(options)
93
+
94
+ expected_output1 = File.join(test_dir, 'video1_spritesheet.png')
95
+ expect(video_processor_mock).to receive(:create_spritesheet).with(video1, expected_output1)
96
+
97
+ processor.process
98
+ end
99
+
100
+ it 'outputs to specified outputdir when provided' do
101
+ options_with_output = options.merge(outputdir: output_dir)
102
+ processor = described_class.new(options_with_output)
103
+
104
+ allow(File).to receive(:directory?).with(output_dir).and_return(true)
105
+
106
+ expected_output1 = File.join(output_dir, 'video1_spritesheet.png')
107
+ expect(video_processor_mock).to receive(:create_spritesheet).with(video1, expected_output1)
108
+
109
+ processor.process
110
+ end
111
+
112
+ it 'creates output directory if it does not exist' do
113
+ options_with_output = options.merge(outputdir: output_dir)
114
+ processor = described_class.new(options_with_output)
115
+
116
+ allow(File).to receive(:directory?).with(output_dir).and_return(false)
117
+ expect(FileUtils).to receive(:mkdir_p).with(output_dir)
118
+
119
+ processor.process
120
+ end
121
+
122
+ it 'enforces unique filenames unless overwrite is true' do
123
+ allow(File).to receive(:exist?).with(/video1_spritesheet\.png/).and_return(true)
124
+ allow(RubySpriter::Utils::FileHelper).to receive(:ensure_unique_output).and_call_original
125
+ allow(RubySpriter::Utils::FileHelper).to receive(:unique_filename).and_return('video1_spritesheet_20251024_123456_789.png')
126
+
127
+ processor = described_class.new(options)
128
+
129
+ expect(RubySpriter::Utils::FileHelper).to receive(:ensure_unique_output).at_least(:once)
130
+
131
+ processor.process
132
+ end
133
+
134
+ it 'continues processing after error in one video' do
135
+ processor = described_class.new(options)
136
+
137
+ allow(video_processor_mock).to receive(:create_spritesheet).and_raise(RubySpriter::ProcessingError, 'Test error')
138
+
139
+ results = processor.process
140
+
141
+ expect(results[:processed_count]).to eq(0)
142
+ expect(results[:errors].length).to eq(3)
143
+ end
144
+ end
145
+
146
+ describe '#consolidate_results' do
147
+ let(:spritesheet1) { File.join(test_dir, 'video1_spritesheet.png') }
148
+ let(:spritesheet2) { File.join(test_dir, 'video2_spritesheet.png') }
149
+ let(:spritesheet3) { File.join(test_dir, 'video3_spritesheet.png') }
150
+ let(:consolidator_mock) { instance_double(RubySpriter::Consolidator) }
151
+
152
+ before do
153
+ allow(RubySpriter::Consolidator).to receive(:new).and_return(consolidator_mock)
154
+ allow(consolidator_mock).to receive(:consolidate).and_return(
155
+ { output_file: 'consolidated.png', columns: 4, rows: 12, frames: 48 }
156
+ )
157
+ end
158
+
159
+ it 'consolidates all spritesheets when batch_consolidate is true' do
160
+ options_with_consolidate = options.merge(batch_consolidate: true)
161
+ processor = described_class.new(options_with_consolidate)
162
+
163
+ outputs = [spritesheet1, spritesheet2, spritesheet3]
164
+
165
+ expect(consolidator_mock).to receive(:consolidate) do |files, output|
166
+ expect(files).to eq(outputs)
167
+ expect(output).to include('batch_consolidated_spritesheet')
168
+ expect(output).to end_with('.png')
169
+ end
170
+
171
+ processor.consolidate_results(outputs)
172
+ end
173
+
174
+ it 'uses outputdir for consolidated file if specified' do
175
+ options_with_consolidate = options.merge(batch_consolidate: true, outputdir: output_dir)
176
+ processor = described_class.new(options_with_consolidate)
177
+
178
+ allow(File).to receive(:directory?).with(output_dir).and_return(true)
179
+
180
+ outputs = [spritesheet1, spritesheet2, spritesheet3]
181
+
182
+ expect(consolidator_mock).to receive(:consolidate) do |files, output|
183
+ expect(output).to start_with(output_dir)
184
+ end
185
+
186
+ processor.consolidate_results(outputs)
187
+ end
188
+
189
+ it 'does not consolidate when batch_consolidate is false' do
190
+ processor = described_class.new(options)
191
+
192
+ outputs = [spritesheet1, spritesheet2, spritesheet3]
193
+
194
+ expect(consolidator_mock).not_to receive(:consolidate)
195
+
196
+ result = processor.consolidate_results(outputs)
197
+ expect(result).to be_nil
198
+ end
199
+ end
200
+ end
@@ -77,6 +77,21 @@ RSpec.describe RubySpriter::CLI do
77
77
  expect { described_class.start(['-h']) }.to output(/Usage: ruby_spriter/).to_stdout
78
78
  end.to raise_error(SystemExit)
79
79
  end
80
+
81
+ it 'shows mode-specific help hints' do
82
+ output = StringIO.new
83
+ $stdout = output
84
+
85
+ begin
86
+ described_class.start(['--help'])
87
+ rescue SystemExit
88
+ # Expected
89
+ ensure
90
+ $stdout = STDOUT
91
+ end
92
+
93
+ expect(output.string).to include('Get mode-specific help:')
94
+ end
80
95
  end
81
96
 
82
97
  describe '--version flag' do
@@ -676,6 +691,141 @@ RSpec.describe RubySpriter::CLI do
676
691
  describe '--video flag' do
677
692
  let(:fixture_video) { File.join(__dir__, '..', 'fixtures', 'test_video.mp4') }
678
693
 
694
+ describe 'context-sensitive help' do
695
+ it 'shows video mode help with --help' do
696
+ output = StringIO.new
697
+ $stdout = output
698
+
699
+ begin
700
+ described_class.start(['--video', '--help'])
701
+ rescue SystemExit
702
+ # Expected
703
+ ensure
704
+ $stdout = STDOUT
705
+ end
706
+
707
+ expect(output.string).to include('Video Mode')
708
+ end
709
+
710
+ it 'shows parent-child option hierarchy in video mode help' do
711
+ output = StringIO.new
712
+ $stdout = output
713
+
714
+ begin
715
+ described_class.start(['--video', '--help'])
716
+ rescue SystemExit
717
+ # Expected
718
+ ensure
719
+ $stdout = STDOUT
720
+ end
721
+
722
+ # Check for parent options
723
+ expect(output.string).to include('-s, --scale PERCENT')
724
+ expect(output.string).to include('-r, --remove-bg')
725
+
726
+ # Check for child options with hierarchy marker
727
+ expect(output.string).to include('└─ Interpolation:')
728
+ expect(output.string).to include('└─ Sharpen radius')
729
+ expect(output.string).to include('└─ Use fuzzy select')
730
+ expect(output.string).to include('└─ Feather radius')
731
+ expect(output.string).to include('└─ Grow selection')
732
+
733
+ # Check that --order mentions BOTH requirement
734
+ expect(output.string).to match(/order.*BOTH.*--scale.*AND.*--remove-bg/i)
735
+ end
736
+
737
+ it 'shows --sharpen as standalone option in video mode help' do
738
+ output = StringIO.new
739
+ $stdout = output
740
+
741
+ begin
742
+ described_class.start(['--video', '--help'])
743
+ rescue SystemExit
744
+ # Expected
745
+ ensure
746
+ $stdout = STDOUT
747
+ end
748
+
749
+ # --sharpen should be a standalone parent option (not indented under --scale)
750
+ expect(output.string).to match(/^ --sharpen\s+Apply unsharp mask/)
751
+
752
+ # --sharpen modifiers should be children under --sharpen
753
+ expect(output.string).to include('└─ Sharpen radius')
754
+ expect(output.string).to include('└─ Sharpen gain')
755
+ expect(output.string).to include('└─ Sharpen threshold')
756
+
757
+ # --interpolation should ONLY be under --scale (not under --sharpen)
758
+ lines = output.string.lines
759
+ sharpen_line_idx = lines.index { |l| l.include?('--sharpen') && l.include?('Apply unsharp mask') }
760
+ scale_line_idx = lines.index { |l| l.include?('--scale PERCENT') }
761
+ interpolation_line_idx = lines.index { |l| l.include?('└─ Interpolation') }
762
+
763
+ # Interpolation should come after scale, not after sharpen
764
+ expect(interpolation_line_idx).to be > scale_line_idx
765
+ expect(interpolation_line_idx).to be < sharpen_line_idx
766
+ end
767
+
768
+ it 'shows image mode help with --help' do
769
+ output = StringIO.new
770
+ $stdout = output
771
+
772
+ begin
773
+ described_class.start(['--image', '--help'])
774
+ rescue SystemExit
775
+ # Expected
776
+ ensure
777
+ $stdout = STDOUT
778
+ end
779
+
780
+ expect(output.string).to include('Image Mode')
781
+ end
782
+
783
+ it 'shows consolidate mode help with --help' do
784
+ output = StringIO.new
785
+ $stdout = output
786
+
787
+ begin
788
+ described_class.start(['--consolidate', '--help'])
789
+ rescue SystemExit
790
+ # Expected
791
+ ensure
792
+ $stdout = STDOUT
793
+ end
794
+
795
+ expect(output.string).to include('Consolidate Mode')
796
+ end
797
+
798
+ it 'shows batch mode help with --help' do
799
+ output = StringIO.new
800
+ $stdout = output
801
+
802
+ begin
803
+ described_class.start(['--batch', '--help'])
804
+ rescue SystemExit
805
+ # Expected
806
+ ensure
807
+ $stdout = STDOUT
808
+ end
809
+
810
+ expect(output.string).to include('Batch Mode')
811
+ end
812
+
813
+ it 'shows split mode help with --help' do
814
+ output = StringIO.new
815
+ $stdout = output
816
+
817
+ begin
818
+ described_class.start(['--split', '--help'])
819
+ rescue SystemExit
820
+ # Expected
821
+ ensure
822
+ $stdout = STDOUT
823
+ end
824
+
825
+ expect(output.string).to include('Split Mode')
826
+ end
827
+ end
828
+
679
829
  describe 'argument parsing' do
680
830
  it 'sets video option with --video flag' do
681
831
  processor_double = instance_double(RubySpriter::Processor)
@@ -1410,6 +1560,7 @@ RSpec.describe RubySpriter::CLI do
1410
1560
  })
1411
1561
 
1412
1562
  processor = RubySpriter::Processor.new(
1563
+ consolidate_mode: true,
1413
1564
  consolidate: [spritesheet_4x2, spritesheet_6x2],
1414
1565
  overwrite: false
1415
1566
  )
@@ -1441,6 +1592,7 @@ RSpec.describe RubySpriter::CLI do
1441
1592
  })
1442
1593
 
1443
1594
  processor = RubySpriter::Processor.new(
1595
+ consolidate_mode: true,
1444
1596
  consolidate: [spritesheet_4x2, spritesheet_6x2],
1445
1597
  overwrite: true
1446
1598
  )
@@ -1453,6 +1605,97 @@ RSpec.describe RubySpriter::CLI do
1453
1605
  expect { processor.run }.to output(/SUCCESS/).to_stdout
1454
1606
  end
1455
1607
  end
1608
+
1609
+ describe 'directory-based consolidation' do
1610
+ let(:test_dir) { File.join(@test_dir, 'consolidate_dir') }
1611
+
1612
+ before do
1613
+ FileUtils.mkdir_p(test_dir)
1614
+ # Copy fixture spritesheets to test directory
1615
+ FileUtils.cp(spritesheet_4x2, File.join(test_dir, 'sprite1.png'))
1616
+ FileUtils.cp(spritesheet_6x2, File.join(test_dir, 'sprite2.png'))
1617
+ end
1618
+
1619
+ it 'accepts --dir option with --consolidate' do
1620
+ processor_double = instance_double(RubySpriter::Processor)
1621
+ allow(processor_double).to receive(:run)
1622
+
1623
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1624
+ expect(options[:consolidate]).to be_nil
1625
+ expect(options[:dir]).to eq(test_dir)
1626
+ processor_double
1627
+ end
1628
+
1629
+ described_class.start(['--consolidate', '--dir', test_dir])
1630
+ end
1631
+
1632
+ it 'validates directory exists' do
1633
+ expect do
1634
+ described_class.start(['--consolidate', '--dir', 'nonexistent_directory'])
1635
+ end.to raise_error(RubySpriter::ValidationError, /Directory not found/)
1636
+ end
1637
+
1638
+ it 'cannot use --dir with comma-separated file list' do
1639
+ expect do
1640
+ described_class.start(['--consolidate', "#{spritesheet_4x2},#{spritesheet_6x2}", '--dir', test_dir])
1641
+ end.to raise_error(RubySpriter::ValidationError, /Cannot use --dir with comma-separated file list/)
1642
+ end
1643
+
1644
+ it 'works with --output option' do
1645
+ processor_double = instance_double(RubySpriter::Processor)
1646
+ allow(processor_double).to receive(:run)
1647
+
1648
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1649
+ expect(options[:dir]).to eq(test_dir)
1650
+ expect(options[:output]).to eq('custom_output.png')
1651
+ processor_double
1652
+ end
1653
+
1654
+ described_class.start(['--consolidate', '--dir', test_dir, '--output', 'custom_output.png'])
1655
+ end
1656
+
1657
+ it 'works with --outputdir option' do
1658
+ output_dir = File.join(@test_dir, 'output')
1659
+ FileUtils.mkdir_p(output_dir)
1660
+
1661
+ processor_double = instance_double(RubySpriter::Processor)
1662
+ allow(processor_double).to receive(:run)
1663
+
1664
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1665
+ expect(options[:dir]).to eq(test_dir)
1666
+ expect(options[:outputdir]).to eq(output_dir)
1667
+ processor_double
1668
+ end
1669
+
1670
+ described_class.start(['--consolidate', '--dir', test_dir, '--outputdir', output_dir])
1671
+ end
1672
+
1673
+ it 'works with --overwrite option' do
1674
+ processor_double = instance_double(RubySpriter::Processor)
1675
+ allow(processor_double).to receive(:run)
1676
+
1677
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1678
+ expect(options[:dir]).to eq(test_dir)
1679
+ expect(options[:overwrite]).to eq(true)
1680
+ processor_double
1681
+ end
1682
+
1683
+ described_class.start(['--consolidate', '--dir', test_dir, '--overwrite'])
1684
+ end
1685
+
1686
+ it 'works with --max-compress option' do
1687
+ processor_double = instance_double(RubySpriter::Processor)
1688
+ allow(processor_double).to receive(:run)
1689
+
1690
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1691
+ expect(options[:dir]).to eq(test_dir)
1692
+ expect(options[:max_compress]).to eq(true)
1693
+ processor_double
1694
+ end
1695
+
1696
+ described_class.start(['--consolidate', '--dir', test_dir, '--max-compress'])
1697
+ end
1698
+ end
1456
1699
  end
1457
1700
 
1458
1701
  describe 'error handling' do
@@ -1501,5 +1744,149 @@ RSpec.describe RubySpriter::CLI do
1501
1744
  described_class.start(['--image', 'test.png', '--split', '4:4', '--override-md'])
1502
1745
  end
1503
1746
  end
1747
+
1748
+ describe '--extract option' do
1749
+ it 'parses extract option with comma-separated frame numbers' do
1750
+ processor_double = instance_double(RubySpriter::Processor)
1751
+ allow(processor_double).to receive(:run)
1752
+
1753
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1754
+ expect(options[:extract]).to eq('1,2,4,5,8')
1755
+ processor_double
1756
+ end
1757
+
1758
+ described_class.start(['--image', 'test.png', '--extract', '1,2,4,5,8'])
1759
+ end
1760
+
1761
+ it 'allows duplicate frame numbers' do
1762
+ processor_double = instance_double(RubySpriter::Processor)
1763
+ allow(processor_double).to receive(:run)
1764
+
1765
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1766
+ expect(options[:extract]).to eq('1,1,2,2,3,3')
1767
+ processor_double
1768
+ end
1769
+
1770
+ described_class.start(['--image', 'test.png', '--extract', '1,1,2,2,3,3'])
1771
+ end
1772
+
1773
+ it 'cannot be used with --split' do
1774
+ expect do
1775
+ described_class.start(['--image', 'test.png', '--extract', '1,2,3', '--split', '4:4'])
1776
+ end.to raise_error(RubySpriter::ValidationError, /--extract and --split are mutually exclusive/)
1777
+ end
1778
+ end
1779
+
1780
+ describe '--columns option' do
1781
+ it 'parses columns option for extraction grid' do
1782
+ processor_double = instance_double(RubySpriter::Processor)
1783
+ allow(processor_double).to receive(:run)
1784
+
1785
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1786
+ expect(options[:columns]).to eq(3)
1787
+ processor_double
1788
+ end
1789
+
1790
+ described_class.start(['--image', 'test.png', '--extract', '1,2,3', '--columns', '3'])
1791
+ end
1792
+
1793
+ it 'works without --extract (for future use)' do
1794
+ processor_double = instance_double(RubySpriter::Processor)
1795
+ allow(processor_double).to receive(:run)
1796
+
1797
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1798
+ expect(options[:columns]).to eq(5)
1799
+ processor_double
1800
+ end
1801
+
1802
+ described_class.start(['--image', 'test.png', '--columns', '5'])
1803
+ end
1804
+ end
1805
+
1806
+ describe '--save-frames option' do
1807
+ it 'sets save_frames option to true' do
1808
+ processor_double = instance_double(RubySpriter::Processor)
1809
+ allow(processor_double).to receive(:run)
1810
+
1811
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1812
+ expect(options[:save_frames]).to eq(true)
1813
+ processor_double
1814
+ end
1815
+
1816
+ described_class.start(['--image', 'test.png', '--extract', '1,2,3', '--save-frames'])
1817
+ end
1818
+
1819
+ it 'can be used without --extract' do
1820
+ processor_double = instance_double(RubySpriter::Processor)
1821
+ allow(processor_double).to receive(:run)
1822
+
1823
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1824
+ expect(options[:save_frames]).to eq(true)
1825
+ processor_double
1826
+ end
1827
+
1828
+ described_class.start(['--image', 'test.png', '--split', '4:4', '--save-frames'])
1829
+ end
1830
+ end
1831
+
1832
+ describe '--add-meta option' do
1833
+ it 'parses add-meta option with R:C format' do
1834
+ processor_double = instance_double(RubySpriter::Processor)
1835
+ allow(processor_double).to receive(:run)
1836
+
1837
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1838
+ expect(options[:add_meta]).to eq('4:4')
1839
+ processor_double
1840
+ end
1841
+
1842
+ described_class.start(['--image', 'test.png', '--add-meta', '4:4'])
1843
+ end
1844
+
1845
+ it 'cannot be combined with --scale' do
1846
+ expect do
1847
+ described_class.start(['--image', 'test.png', '--add-meta', '4:4', '--scale', '50'])
1848
+ end.to raise_error(RubySpriter::ValidationError, /--add-meta cannot be combined with processing options/)
1849
+ end
1850
+
1851
+ it 'cannot be combined with --remove-bg' do
1852
+ expect do
1853
+ described_class.start(['--image', 'test.png', '--add-meta', '4:4', '--remove-bg'])
1854
+ end.to raise_error(RubySpriter::ValidationError, /--add-meta cannot be combined with processing options/)
1855
+ end
1856
+
1857
+ it 'cannot be combined with --sharpen' do
1858
+ expect do
1859
+ described_class.start(['--image', 'test.png', '--add-meta', '4:4', '--sharpen'])
1860
+ end.to raise_error(RubySpriter::ValidationError, /--add-meta cannot be combined with processing options/)
1861
+ end
1862
+ end
1863
+
1864
+ describe '--overwrite-meta option' do
1865
+ it 'sets overwrite_meta option to true' do
1866
+ processor_double = instance_double(RubySpriter::Processor)
1867
+ allow(processor_double).to receive(:run)
1868
+
1869
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1870
+ expect(options[:overwrite_meta]).to eq(true)
1871
+ processor_double
1872
+ end
1873
+
1874
+ described_class.start(['--image', 'test.png', '--add-meta', '4:4', '--overwrite-meta'])
1875
+ end
1876
+ end
1877
+
1878
+ describe '--frames option for partial grids' do
1879
+ it 'parses frames option with integer value' do
1880
+ processor_double = instance_double(RubySpriter::Processor)
1881
+ allow(processor_double).to receive(:run)
1882
+
1883
+ allow(RubySpriter::Processor).to receive(:new) do |options|
1884
+ expect(options[:frame_count]).to eq(14)
1885
+ processor_double
1886
+ end
1887
+
1888
+ described_class.start(['--image', 'test.png', '--add-meta', '4:4', '--frames', '14'])
1889
+ end
1890
+ end
1504
1891
  end
1505
1892
  end