timecode 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ === 0.1.5 / 2009-01-14
2
+
3
+ * Tests remade using test/spec
4
+
1
5
  === 0.1.4 / 2000-01-01
2
6
 
3
7
  * Expanded test coverage
data/Manifest.txt CHANGED
@@ -1,6 +1,8 @@
1
1
  History.txt
2
2
  Manifest.txt
3
3
  README.txt
4
+ SPECS.txt
4
5
  Rakefile
5
6
  lib/timecode.rb
6
- test/test_timecode.rb
7
+ test/test_timecode.rb
8
+ timecode.gemspec
data/Rakefile CHANGED
@@ -1,15 +1,15 @@
1
1
  require 'rubygems'
2
- require './lib/timecode.rb'
3
2
  require 'hoe'
3
+ require './lib/timecode.rb'
4
4
 
5
- Class.new(Hoe) do
6
- def extra_deps
7
- @extra_deps.reject! { |x| Array(x).first == 'hoe' }
8
- @extra_deps
9
- end
10
- end.new('timecode', Timecode::VERSION) do |p|
5
+ Hoe.new('timecode', Timecode::VERSION) do |p|
11
6
  p.developer('Julik', 'me@julik.nl')
12
7
  p.extra_deps.reject! {|e| e[0] == 'hoe' }
13
- p.rubyforge_name = 'wiretap'
8
+ p.extra_deps << 'test-spec'
9
+ p.rubyforge_name = 'guerilla-di'
14
10
  p.remote_rdoc_dir = 'timecode'
11
+ end
12
+
13
+ task "specs" do
14
+ `specrb test/* --rdox > SPECS.txt`
15
15
  end
data/SPECS.txt ADDED
@@ -0,0 +1,74 @@
1
+
2
+ == Timecode on instantiation should
3
+ * be instantable from int
4
+ * always coerce FPS to float
5
+ * create a zero TC with no arguments
6
+
7
+ == An existing Timecode should
8
+ * report that the framerates are in delta
9
+ * validate equality based on delta
10
+ * report total as it's to_i
11
+ * support hours
12
+ * support minutes
13
+ * support seconds
14
+ * support frames
15
+ * report frame_interval as a float
16
+
17
+ == A Timecode of zero should
18
+ * properly respond to zero?
19
+
20
+ == An existing TImecode on inspection should
21
+ * properly present himself via inspect
22
+ * properly print itself
23
+
24
+ == An existing Timecode used within ranges should
25
+ * properly provide successive value that is one frame up
26
+ * work as a range member
27
+
28
+ == A Timecode on conversion should
29
+ * copy itself with a different framerate
30
+
31
+ == A Timecode on calculations should
32
+ * support addition
33
+ * should raise on addition if framerates do not match
34
+ * when added with an integer instead calculate on total
35
+ * support subtraction
36
+ * on subtraction of an integer instead calculate on total
37
+ * raise when subtracting a Timecode with a different framerate
38
+ * support multiplication
39
+ * raise when the resultig Timecode is negative
40
+ * yield a Timecode when divided by an Integer
41
+ * yield a number when divided by another Timecode
42
+
43
+ == A Timecode used with fractional number of seconds
44
+ * should properly return fractional seconds
45
+ * properly translate to frames when instantiated from fractional seconds
46
+
47
+ == Timecode.at() should
48
+ * disallow more than 99 hrs
49
+ * disallow more than 59 minutes
50
+ * disallow more than 59 seconds
51
+ * disallow more frames than what the framerate permits
52
+ * propery accept usable values
53
+
54
+ == Timecode.parse() should
55
+ * handle complete SMPTE timecode
56
+ * refuse to handle timecode that is out of range for the framerate
57
+ * parse a row of numbers as parts of a timecode starting from the right
58
+ * parse a number with f suffix as frames
59
+ * parse a number with s suffix as seconds
60
+ * parse a number with m suffix as minutes
61
+ * parse a number with h suffix as hours
62
+ * parse different suffixes as a sum of elements
63
+ * parse timecode with fractional second instead of frames
64
+ * raise on improper format
65
+
66
+ == Timecode.soft_parse should
67
+ * parse the timecode
68
+ * not raise on improper format and return zero TC instead
69
+
70
+ == Timecode with unsigned integer conversions should
71
+ * parse from a 4x4bits packed 32bit unsigned int
72
+ * properly convert itself back to 4x4 bits 32bit unsigned int
73
+
74
+ 48 specifications (80 requirements), 0 failures
data/lib/timecode.rb CHANGED
@@ -12,15 +12,23 @@
12
12
  # :mapping => [%w(source_tc_frames total), %w(tape_fps fps)]
13
13
 
14
14
  class Timecode
15
- VERSION = '0.1.4'
15
+ VERSION = '0.1.5'
16
16
 
17
17
  include Comparable
18
18
 
19
19
  DEFAULT_FPS = 25.0
20
- ALLOWED_FPS_DELTA = 0.001
21
- COMPLETE_TC_RE = /^(\d{1,2}):(\d{1,2}):(\d{1,2}):(\d{1,2})$/
20
+
21
+ #:stopdoc:
22
+ NTSC_FPS = (30.0 * 1000 / 1001).freeze
23
+ ALLOWED_FPS_DELTA = (0.001).freeze
24
+
25
+ COMPLETE_TC_RE = /^(\d{2}):(\d{2}):(\d{2}):(\d{2})$/
26
+ DF_TC_RE = /^(\d{1,2}):(\d{1,2}):(\d{1,2});(\d{2})$/
27
+ FRACTIONAL_TC_RE = /^(\d{2}):(\d{2}):(\d{2}).(\d{1,8})$/
28
+
22
29
  WITH_FRACTIONS_OF_SECOND = "%02d:%02d:%02d.%02d"
23
30
  WITH_FRAMES = "%02d:%02d:%02d:%02d"
31
+ #:startdoc:
24
32
 
25
33
  # All Timecode lib errors inherit from this
26
34
  class Error < RuntimeError; end
@@ -31,27 +39,19 @@ class Timecode
31
39
  # Gets raised if timecode is out of range (like 100 hours long)
32
40
  class RangeError < Error; end
33
41
 
34
- # Self-explanatory
35
- class NonPositiveFps < RangeError; end
36
-
37
- # Gets raised when you divide by zero
38
- class TcIsZero < ZeroDivisionError; end
39
-
40
42
  # Gets raised when a timecode cannot be parsed
41
43
  class CannotParse < Error; end
42
44
 
43
45
  # Gets raised when you try to compute two timecodes with different framerates together
44
46
  class WrongFramerate < ArgumentError; end
45
-
46
- # Well well...
47
- class MethodRequiresTimecode < ArgumentError; end
48
47
 
49
48
  # Initialize a new Timecode object with a certain amount of frames and a framerate
50
49
  # will be interpreted as the total number of frames
51
50
  def initialize(total = 0, fps = DEFAULT_FPS)
52
- raise RangeError, "Timecode cannot be negative" if total.to_f < 0
53
51
  raise WrongFramerate, "FPS cannot be zero" if fps.zero?
54
-
52
+
53
+ # If total is a string, use parse
54
+ raise RangeError, "Timecode cannot be negative" if total.to_i < 0
55
55
  # Always cast framerate to float, and num of rames to integer
56
56
  @total, @fps = total.to_i, fps.to_f
57
57
  @value = validate!
@@ -62,10 +62,13 @@ class Timecode
62
62
  "#<Timecode:%s (%dF@%.2f)>" % [to_s, total, fps]
63
63
  end
64
64
 
65
- TIME_FIELDS = 7 # :nodoc:
66
-
67
65
  class << self
68
66
 
67
+ # Use initialize for integers and parsing for strings
68
+ def new(from, fps = DEFAULT_FPS)
69
+ from.is_a?(String) ? parse(from, fps) : super(from, fps)
70
+ end
71
+
69
72
  # Parse timecode and return zero if none matched
70
73
  def soft_parse(input, with_fps = DEFAULT_FPS)
71
74
  parse(input) rescue new(0, with_fps)
@@ -78,13 +81,19 @@ class Timecode
78
81
  # * 00:00:00:00 - will be parsed as zero TC
79
82
  def parse(input, with_fps = DEFAULT_FPS)
80
83
  # Drop frame goodbye
81
- raise TimecodeLibError, "We do not support drop frame" if (input =~ /\;/)
84
+ raise Error, "We do not support drop frame" if (input =~ /\;/)
82
85
 
83
86
  hrs, mins, secs, frames = 0,0,0,0
84
87
  atoms = []
85
88
 
89
+ # 00:00:00:00
90
+ if (input =~ COMPLETE_TC_RE)
91
+ atoms = input.scan(COMPLETE_TC_RE).to_a.flatten
92
+ # 00:00:00.0
93
+ elsif input =~ FRACTIONAL_TC_RE
94
+ parse_with_fractional_seconds(input, with_fps)
86
95
  # 10h 20m 10s 1f
87
- if input =~ /\s/
96
+ elsif input =~ /\s/
88
97
  return input.split.map{|part| parse(part, with_fps) }.inject { |sum, p| sum + p.total }
89
98
  # 10s
90
99
  elsif input =~ /^(\d+)s$/
@@ -105,8 +114,6 @@ class Timecode
105
114
  atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
106
115
  atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
107
116
  atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
108
- elsif (input =~ COMPLETE_TC_RE)
109
- atoms = input.scan(COMPLETE_TC_RE).to_a.flatten
110
117
  else
111
118
  raise CannotParse, "Cannot parse #{input} into timecode, no match"
112
119
  end
@@ -146,7 +153,7 @@ class Timecode
146
153
  seconds_per_frame = 1.0 / fps.to_f
147
154
  frame_idx = (fraction_part / seconds_per_frame).floor
148
155
 
149
- tc_with_frameno = tc_with_fractions_of_second.gsub(fraction_expr, ":#{frame_idx}")
156
+ tc_with_frameno = tc_with_fractions_of_second.gsub(fraction_expr, ":%02d" % frame_idx)
150
157
 
151
158
  parse(tc_with_frameno, fps)
152
159
  end
@@ -321,7 +328,7 @@ class Timecode
321
328
  raise RangeError, "Timecode cannot be longer that 99 hrs" if hrs > 99
322
329
  raise RangeError, "More than 59 minutes" if mins > 59
323
330
  raise RangeError, "More than 59 seconds" if secs > 59
324
- raise TimecodeLibError, "More than #{@fps.to_s} frames (#{frames}) in the last second" if frames >= @fps
331
+ raise RangeError, "More than #{@fps.to_s} frames (#{frames}) in the last second" if frames >= @fps
325
332
 
326
333
  [hrs, mins, secs, frames]
327
334
  end
@@ -1,308 +1,318 @@
1
1
  require 'test/unit'
2
+ require 'rubygems'
3
+ require 'test/spec'
4
+
2
5
  require File.dirname(__FILE__) + '/../lib/timecode'
3
6
 
4
7
 
5
- class TimecodeTest < Test::Unit::TestCase
8
+ context "Timecode on instantiation should" do
6
9
 
7
- def test_timecode_with_nil_gives_zero
8
- assert_equal Timecode.new(0), Timecode.new(nil)
10
+ specify "be instantable from int" do
11
+ tc = Timecode.new(10)
12
+ tc.should.be.kind_of Timecode
13
+ tc.total.should.equal 10
9
14
  end
10
15
 
11
- def test_fps_always_coerced_to_float
12
- t = Timecode.new(10, 25)
13
- assert_kind_of Float, t.fps
16
+ specify "always coerce FPS to float" do
17
+ Timecode.new(10, 24).fps.should.be.kind_of(Float)
18
+ Timecode.new(10, 25.0).fps.should.be.kind_of(Float)
19
+ end
20
+
21
+ specify "create a zero TC with no arguments" do
22
+ Timecode.new(nil).should.be.zero?
23
+ end
24
+ end
14
25
 
15
- t = Timecode.new(10, 25.0)
16
- assert_kind_of Float, t.fps
26
+ context "An existing Timecode should" do
27
+
28
+ before do
29
+ @five_seconds = Timecode.new(5*25, 25)
30
+ @one_and_a_half_film = (90 * 60) * 24
31
+ @film_tc = Timecode.new(@one_and_a_half_film, 24)
17
32
  end
18
33
 
19
- def test_framerate_in_delta
34
+
35
+ specify "report that the framerates are in delta" do
20
36
  tc = Timecode.new(1)
21
- assert tc.framerate_in_delta(25.0000000000000001, 25.0000000000000003)
37
+ tc.framerate_in_delta(25.0000000000000001, 25.0000000000000003).should.equal(true)
22
38
  end
23
-
24
- def test_equality_validated_based_on_deltas
39
+
40
+ specify "validate equality based on delta" do
25
41
  t1, t2 = Timecode.new(10, 25.0000000000000000000000000001), Timecode.new(10, 25.0000000000000000000000000002)
26
- assert t1 == t2
42
+ t1.should.equal(t2)
27
43
  end
28
44
 
29
- def test_to_i_is_total
30
- assert_equal 10, Timecode.new(10).to_i
45
+ specify "report total as it's to_i" do
46
+ Timecode.new(10).to_i.should.equal(10)
31
47
  end
32
48
 
33
- def test_inspect
34
- tc = Timecode.new(10, 25)
35
- assert_equal "#<Timecode:00:00:00:10 (10F@25.00)>", tc.inspect
49
+ specify "support hours" do
50
+ @five_seconds.should.respond_to :hours
51
+ @five_seconds.hours.should.equal 0
52
+ @film_tc.hours.should.equal 1
53
+ end
54
+
55
+ specify "support minutes" do
56
+ @five_seconds.should.respond_to :minutes
57
+ @five_seconds.minutes.should.equal 0
58
+ @film_tc.minutes.should.equal 30
59
+ end
60
+
61
+ specify "support seconds" do
62
+ @five_seconds.should.respond_to :seconds
63
+ @five_seconds.seconds.should.equal 5
64
+ @film_tc.seconds.should.equal 0
36
65
  end
37
66
 
38
- def test_equality_on_succ
39
- assert_equal Timecode.parse("05:43:02:01"), Timecode.parse("05:43:02:00").succ
67
+ specify "support frames" do
68
+ @film_tc.frames.should.equal 0
40
69
  end
41
70
 
42
- def test_basics
43
- five_seconds_of_pal = 5 * 25
44
-
45
- tc = Timecode.new(five_seconds_of_pal, 25)
46
- assert_equal 0, tc.hours
47
- assert_equal 0, tc.minutes
48
- assert_equal 5, tc.seconds
49
- assert_equal 0, tc.frames
50
- assert_equal five_seconds_of_pal, tc.total
51
- assert_equal "00:00:05:00", tc.to_s
71
+ specify "report frame_interval as a float" do
72
+ tc = Timecode.new(10)
73
+ tc.should.respond_to :frame_interval
52
74
 
53
- one_and_a_half_hour_of_hollywood = (90 * 60) * 24
54
-
55
- film_tc = Timecode.new(one_and_a_half_hour_of_hollywood, 24)
56
- assert_equal 1, film_tc.hours
57
- assert_equal 30, film_tc.minutes
58
- assert_equal 0, film_tc.seconds
59
- assert_equal 0, film_tc.frames
60
- assert_equal one_and_a_half_hour_of_hollywood, film_tc.total
61
- assert_equal "01:30:00:00", film_tc.to_s
62
-
63
- assert_equal "01:30:00:04", (film_tc + 4).to_s
64
- assert_equal "01:30:01:04", (film_tc + 28).to_s
75
+ tc.frame_interval.should.be.close 0.04, 0.0001
76
+ tc = Timecode.new(10, 30)
77
+ tc.frame_interval.should.be.close 0.03333, 0.0001
78
+ end
79
+
80
+ end
65
81
 
66
- assert_raise(Timecode::WrongFramerate) do
67
- tc + film_tc
68
- end
69
-
70
- two_seconds_and_five_frames_of_pal = ((2 * 25) + 5)
71
- pal_tc = Timecode.new(two_seconds_and_five_frames_of_pal, 25)
72
- assert_nothing_raised do
73
- added_tc = pal_tc + tc
74
- assert_equal "00:00:07:05", added_tc.to_s
75
- end
82
+ context "A Timecode of zero should" do
83
+ specify "properly respond to zero?" do
84
+ Timecode.new(0).should.respond_to :zero?
85
+ Timecode.new(0).should.be.zero
86
+ Timecode.new(1).should.not.be.zero
87
+ end
88
+ end
76
89
 
90
+ context "An existing TImecode on inspection should" do
91
+ specify "properly present himself via inspect" do
92
+ Timecode.new(10, 25).inspect.should.equal "#<Timecode:00:00:00:10 (10F@25.00)>"
77
93
  end
78
94
 
79
- def test_succ
80
- assert_equal Timecode.new(23), Timecode.new(22).succ
95
+ specify "properly print itself" do
96
+ Timecode.new(5, 25).to_s.should.equal "00:00:00:05"
81
97
  end
82
-
83
- def test_zero
84
- assert Timecode.new(0).zero?
85
- assert Timecode.parse("00:00:00:00").zero?
86
- assert !Timecode.new(1).zero?
87
- assert !Timecode.new(1000).zero?
98
+ end
99
+
100
+ context "An existing Timecode used within ranges should" do
101
+ specify "properly provide successive value that is one frame up" do
102
+ Timecode.new(10).succ.total.should.equal 11
103
+ Timecode.new(22).succ.should.equal Timecode.new(23)
88
104
  end
89
105
 
90
- def test_timecode_rangeable
106
+ specify "work as a range member" do
91
107
  r = Timecode.new(10)...Timecode.new(20)
92
- assert_equal 10, r.to_a.length
93
- assert_equal Timecode.new(14), r.to_a[4]
108
+ r.to_a.length.should.equal 10
109
+ r.to_a[4].should.equal Timecode.new(14)
94
110
  end
95
111
 
96
- def test_frame_interval
97
- tc = Timecode.new(10)
98
- assert_in_delta tc.frame_interval, 0.04, 0.0001
99
-
100
- tc = Timecode.new(10, 30)
101
- assert_in_delta tc.frame_interval, 0.03333, 0.0001
102
- end
103
112
  end
104
113
 
105
- class ConversionTest < Test::Unit::TestCase
106
- def test_convert_25_at_24
107
- tc = Timecode.new(40, 25)
114
+ context "A Timecode on conversion should" do
115
+ specify "copy itself with a different framerate" do
116
+ tc = Timecode.new(40,25)
108
117
  at24 = tc.convert(24)
109
- assert_equal tc.total, at24.total
118
+ at24.total.should.equal 40
110
119
  end
111
120
  end
112
121
 
113
- class CalculationsTest < Test::Unit::TestCase
114
- def test_plus
122
+ context "A Timecode on calculations should" do
123
+
124
+ specify "support addition" do
115
125
  a, b = Timecode.new(24, 25.000000000000001), Timecode.new(22, 25.000000000000002)
116
- assert_equal Timecode.new(24 + 22, 25.000000000000001), (a + b)
126
+ (a + b).should.equal Timecode.new(24 + 22, 25.000000000000001)
117
127
  end
118
128
 
119
- def test_plus_with_different_framerates_should_raise
120
- assert_raise(Timecode::WrongFramerate) { Timecode.new(10, 25) + Timecode.new(10, 30) }
129
+ specify "should raise on addition if framerates do not match" do
130
+ lambda{ Timecode.new(10, 25) + Timecode.new(10, 30) }.should.raise(Timecode::WrongFramerate)
121
131
  end
122
-
123
- def test_plus_with_int_gives_a_timecode
124
- assert_equal Timecode.new(10), Timecode.new(5) + 5
132
+
133
+ specify "when added with an integer instead calculate on total" do
134
+ (Timecode.new(5) + 5).should.equal(Timecode.new(10))
125
135
  end
126
136
 
127
- def test_min
137
+ specify "support subtraction" do
128
138
  a, b = Timecode.new(10), Timecode.new(4)
129
- assert_equal Timecode.new(6), a - b
139
+ (a - b).should.equal Timecode.new(6)
130
140
  end
131
141
 
132
- def test_min_with_int_gives_a_timecode
133
- assert_equal Timecode.new(10), Timecode.new(15) - 5
142
+ specify "on subtraction of an integer instead calculate on total" do
143
+ (Timecode.new(15) - 5).should.equal Timecode.new(10)
134
144
  end
135
-
136
- def test_min_with_different_framerates_should_raise
137
- assert_raise(Timecode::WrongFramerate) { Timecode.new(10, 25) - Timecode.new(10, 30) }
145
+
146
+ specify "raise when subtracting a Timecode with a different framerate" do
147
+ lambda { Timecode.new(10, 25) - Timecode.new(10, 30) }.should.raise(Timecode::WrongFramerate)
138
148
  end
139
149
 
140
- def test_mult
141
- assert_equal Timecode.new(100), Timecode.new(10) * 10
150
+ specify "support multiplication" do
151
+ (Timecode.new(10) * 10).should.equal(Timecode.new(100))
142
152
  end
143
153
 
144
- def test_mult_by_neg_number_should_raise
145
- assert_raise(Timecode::RangeError) { Timecode.new(10) * -200 }
154
+ specify "raise when the resultig Timecode is negative" do
155
+ lambda { Timecode.new(10) * -200 }.should.raise(Timecode::RangeError)
146
156
  end
147
157
 
148
- def test_div_by_int_gives_a_timecode
158
+ specify "yield a Timecode when divided by an Integer" do
149
159
  v = Timecode.new(200) / 20
150
- assert_kind_of Timecode, v
151
- assert_equal Timecode.new(10), v
160
+ v.should.be.kind_of(Timecode)
161
+ v.should.equal Timecode.new(10)
152
162
  end
153
-
154
- def test_div_by_timecode_gives_an_int
163
+
164
+ specify "yield a number when divided by another Timecode" do
155
165
  v = Timecode.new(200) / Timecode.new(20)
156
- assert_kind_of Numeric, v
157
- assert_equal 10, v
166
+ v.should.be.kind_of(Numeric)
167
+ v.should.equal 10
158
168
  end
169
+ end
170
+
171
+ context "A Timecode used with fractional number of seconds" do
159
172
 
160
- def test_tc_with_frames_as_fraction
173
+ specify "should properly return fractional seconds" do
161
174
  tc = Timecode.new(100 -1, fps = 25)
162
- assert_equal 24, tc.frames
163
- assert_equal "00:00:03.96", tc.with_frames_as_fraction
164
- assert_equal "00:00:03.96", tc.with_fractional_seconds
165
- end
166
-
167
- def test_float_framerate
168
- tc = Timecode.new(25, 12.5)
169
- assert_equal "00:00:02:00", tc.to_s
175
+ tc.frames.should.equal 24
176
+
177
+ tc.with_frames_as_fraction.should.equal "00:00:03.96"
178
+ tc.with_fractional_seconds.should.equal "00:00:03.96"
170
179
  end
171
180
 
172
- def test_from_seconds
181
+ specify "properly translate to frames when instantiated from fractional seconds" do
173
182
  fraction = 7.1
174
183
  tc = Timecode.from_seconds(fraction, 10)
175
- assert_equal "00:00:07:01", tc.to_s
184
+ tc.to_s.should.equal "00:00:07:01"
176
185
 
177
186
  fraction = 7.5
178
187
  tc = Timecode.from_seconds(fraction, 10)
179
- assert_equal "00:00:07:05", tc.to_s
188
+ tc.to_s.should.equal "00:00:07:05"
180
189
 
181
190
  fraction = 7.16
182
191
  tc = Timecode.from_seconds(fraction, 12.5)
183
- assert_equal "00:00:07:02", tc.to_s
192
+ tc.to_s.should.equal "00:00:07:02"
184
193
  end
185
194
 
186
195
  end
187
196
 
188
- class TestAt < Test::Unit::TestCase
197
+ context "Timecode.at() should" do
189
198
 
190
- def test_at_disallows_more_than_99_hrs
191
- assert_nothing_raised { Timecode.at(99,0,0,0) }
192
- assert_raise(Timecode::RangeError) { Timecode.at(100,0,0,0) }
199
+ specify "disallow more than 99 hrs" do
200
+ lambda{ Timecode.at(99,0,0,0) }.should.not.raise
201
+ lambda{ Timecode.at(100,0,0,0) }.should.raise(Timecode::RangeError)
193
202
  end
194
-
195
- def test_at_disallows_more_than_59_mins
196
- assert_raise(Timecode::RangeError) { Timecode.at(1,60,0,0) }
203
+
204
+ specify "disallow more than 59 minutes" do
205
+ lambda{ Timecode.at(1,60,0,0) }.should.raise(Timecode::RangeError)
197
206
  end
198
207
 
199
- def test_at_disallows_more_than_59_secs
200
- assert_raise(Timecode::RangeError) { Timecode.at(1,0,60,0) }
208
+ specify "disallow more than 59 seconds" do
209
+ lambda{ Timecode.at(1,0,60,0) }.should.raise(Timecode::RangeError)
201
210
  end
202
-
203
- def test_at_disallows_more_frames_than_framerate
204
- assert_raise(Timecode::RangeError) { Timecode.at(1,0,60,25, 25) }
205
- assert_raise(Timecode::RangeError) { Timecode.at(1,0,60,32, 30) }
211
+
212
+ specify "disallow more frames than what the framerate permits" do
213
+ lambda{ Timecode.at(1,0,60,25, 25) }.should.raise(Timecode::RangeError)
214
+ lambda{ Timecode.at(1,0,60,32, 30) }.should.raise(Timecode::RangeError)
215
+ end
216
+
217
+ specify "propery accept usable values" do
218
+ Timecode.at(20, 20, 10, 5).to_s.should.equal "20:20:10:05"
206
219
  end
207
-
208
220
  end
209
221
 
210
- class TestParsing < Test::Unit::TestCase
211
222
 
212
- def test_parse_simple
223
+ context "Timecode.parse() should" do
224
+
225
+ specify "handle complete SMPTE timecode" do
213
226
  simple_tc = "00:10:34:10"
214
-
215
- assert_nothing_raised do
216
- @tc = Timecode.parse(simple_tc)
217
- assert_equal simple_tc, @tc.to_s
218
- end
227
+ Timecode.parse(simple_tc).to_s.should.equal(simple_tc)
228
+ end
229
+
230
+ specify "handle complete SMPTE timecode via new" do
231
+ simple_tc = "00:10:34:10"
232
+ Timecode.new(simple_tc).to_s.should.equal(simple_tc)
233
+ end
219
234
 
235
+ specify "refuse to handle timecode that is out of range for the framerate" do
220
236
  bad_tc = "00:76:89:30"
221
- unknown_gobbledygook = "this is insane"
222
-
223
- assert_raise(Timecode::CannotParse) do
224
- tc = Timecode.parse(unknown_gobbledygook, 25)
225
- end
226
-
227
- assert_raise(Timecode::RangeError) do
228
- Timecode.parse(bad_tc, 25)
229
- end
237
+ lambda { Timecode.parse(bad_tc, 25) }.should.raise(Timecode::RangeError)
230
238
  end
231
239
 
232
- def test_parse_from_numbers
233
- assert_equal Timecode.new(10), Timecode.parse("10")
234
- assert_equal Timecode.new(60), Timecode.parse("210")
235
- assert_equal "10:10:10:10", Timecode.parse("10101010").to_s
240
+ specify "parse a row of numbers as parts of a timecode starting from the right" do
241
+ Timecode.parse("10").should.equal Timecode.new(10)
242
+ Timecode.parse("210").should.equal Timecode.new(60)
243
+ Timecode.parse("10101010").to_s.should.equal "10:10:10:10"
236
244
  end
237
245
 
238
- def test_parse_with_f
239
- assert_equal Timecode.new(60), Timecode.parse("60f")
246
+ specify "parse a number with f suffix as frames" do
247
+ Timecode.parse("60f").should.equal Timecode.new(60)
240
248
  end
241
249
 
242
- def test_parse_s
243
- assert_equal Timecode.new(50, 25), Timecode.parse("2s", 25)
244
- assert_equal Timecode.new(60, 30), Timecode.parse("2s", 30)
245
- assert_not_equal Timecode.new(60, 25), Timecode.parse("2s", 30)
250
+ specify "parse a number with s suffix as seconds" do
251
+ Timecode.parse("2s", 25).should.equal Timecode.new(50, 25)
252
+ Timecode.parse("2s", 30).should.equal Timecode.new(60, 30)
246
253
  end
247
254
 
248
- def test_parse_m
249
- assert_equal Timecode.new(25 * 60 * 3), Timecode.parse("3m")
255
+ specify "parse a number with m suffix as minutes" do
256
+ Timecode.parse("3m").should.equal Timecode.new(25 * 60 * 3)
250
257
  end
251
-
252
- def test_parse_h
253
- assert_equal Timecode.new(25 * 60 * 60 * 3), Timecode.parse("3h")
258
+
259
+ specify "parse a number with h suffix as hours" do
260
+ Timecode.parse("3h").should.equal Timecode.new(25 * 60 * 60 * 3)
254
261
  end
255
262
 
256
- def test_parse_from_elements
257
- assert_equal '01:00:00:04', Timecode.parse("1h 4f").to_s
258
- assert_equal '01:00:00:04', Timecode.parse("4f 1h").to_s
259
- assert_equal '01:00:01:04', Timecode.parse("29f 1h").to_s
263
+ specify "parse different suffixes as a sum of elements" do
264
+ Timecode.parse("1h 4f").to_s.should.equal '01:00:00:04'
265
+ Timecode.parse("4f 1h").to_s.should.equal '01:00:00:04'
266
+ Timecode.parse("29f 1h").to_s.should.equal '01:00:01:04'
260
267
  end
261
268
 
262
- def test_parse_fractional_tc
269
+ specify "parse timecode with fractional second instead of frames" do
263
270
  fraction = "00:00:07.1"
264
271
  tc = Timecode.parse_with_fractional_seconds(fraction, 10)
265
- assert_equal "00:00:07:01", tc.to_s
272
+ tc.to_s.should.equal "00:00:07:01"
266
273
 
267
274
  fraction = "00:00:07.5"
268
275
  tc = Timecode.parse_with_fractional_seconds(fraction, 10)
269
- assert_equal "00:00:07:05", tc.to_s
276
+ tc.to_s.should.equal "00:00:07:05"
270
277
 
271
278
  fraction = "00:00:07.04"
272
279
  tc = Timecode.parse_with_fractional_seconds(fraction, 12.5)
273
- assert_equal "00:00:07:00", tc.to_s
280
+ tc.to_s.should.equal "00:00:07:00"
274
281
 
275
282
  fraction = "00:00:07.16"
276
283
  tc = Timecode.parse_with_fractional_seconds(fraction, 12.5)
277
- assert_equal "00:00:07:02", tc.to_s
284
+ tc.to_s.should.equal "00:00:07:02"
278
285
  end
279
286
 
280
- # def test_parse_with_calculation
281
- # tc = Timecode.parse_with_calculation("00:00:00:15 +2f")
282
- # assert_equal Timecode.new(17), tc
283
- # end
287
+ specify "raise on improper format" do
288
+ lambda { Timecode.parse("Meaningless nonsense", 25) }.should.raise Timecode::CannotParse
289
+ lambda { Timecode.parse("", 25) }.should.raise Timecode::CannotParse
290
+ end
284
291
 
285
- def test_soft_parse_does_not_raise_and_createz_zeroed_tc
286
- assert_nothing_raised do
287
- tc = Timecode.soft_parse("Meaningless nonsense", 25)
288
- assert tc.zero?
289
- end
292
+ end
293
+
294
+ context "Timecode.soft_parse should" do
295
+ specify "parse the timecode" do
296
+ Timecode.soft_parse('200').to_s.should.equal "00:00:02:00"
290
297
  end
291
298
 
292
- def test_parse_raoses_on_improper_format
293
- assert_raise(Timecode::CannotParse) { Timecode.parse("Meaningless nonsense", 25) }
294
- assert_raise(Timecode::CannotParse) { Timecode.parse("", 25) }
299
+ specify "not raise on improper format and return zero TC instead" do
300
+ lambda do
301
+ tc = Timecode.soft_parse("Meaningless nonsense", 25)
302
+ tc.should.be.zero?
303
+ end.should.not.raise
295
304
  end
296
305
  end
297
306
 
298
- class TestUintConversion < Test::Unit::TestCase
299
- def test_from_uint
307
+ context "Timecode with unsigned integer conversions should" do
308
+
309
+ specify "parse from a 4x4bits packed 32bit unsigned int" do
300
310
  uint, tc = 87310853, Timecode.at(5,34,42,5)
301
- assert_equal tc, Timecode.from_uint(uint)
311
+ Timecode.from_uint(uint).should.equal tc
302
312
  end
303
313
 
304
- def test_to_uint
314
+ specify "properly convert itself back to 4x4 bits 32bit unsigned int" do
305
315
  uint, tc = 87310853, Timecode.at(5,34,42,5)
306
- assert_equal uint, tc.to_uint
316
+ tc.to_uint.should.equal uint
307
317
  end
308
318
  end
data/timecode.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{timecode}
5
+ s.version = "0.1.5"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Julik"]
9
+ s.date = %q{2009-01-18}
10
+ s.description = %q{Value class for SMPTE timecode information}
11
+ s.email = ["me@julik.nl"]
12
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt", "SPECS.txt"]
13
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "SPECS.txt", "Rakefile", "lib/timecode.rb", "test/test_timecode.rb", "timecode.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://wiretap.rubyforge.org/timecode}
16
+ s.rdoc_options = ["--main", "README.txt"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{guerilla-di}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{Value class for SMPTE timecode information}
21
+ s.test_files = ["test/test_timecode.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<test-spec>, [">= 0"])
29
+ s.add_development_dependency(%q<hoe>, [">= 1.8.2"])
30
+ else
31
+ s.add_dependency(%q<test-spec>, [">= 0"])
32
+ s.add_dependency(%q<hoe>, [">= 1.8.2"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<test-spec>, [">= 0"])
36
+ s.add_dependency(%q<hoe>, [">= 1.8.2"])
37
+ end
38
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik
@@ -9,9 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-01 00:00:00 +01:00
12
+ date: 2009-01-18 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: test-spec
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
15
25
  - !ruby/object:Gem::Dependency
16
26
  name: hoe
17
27
  type: :development
@@ -33,13 +43,16 @@ extra_rdoc_files:
33
43
  - History.txt
34
44
  - Manifest.txt
35
45
  - README.txt
46
+ - SPECS.txt
36
47
  files:
37
48
  - History.txt
38
49
  - Manifest.txt
39
50
  - README.txt
51
+ - SPECS.txt
40
52
  - Rakefile
41
53
  - lib/timecode.rb
42
54
  - test/test_timecode.rb
55
+ - timecode.gemspec
43
56
  has_rdoc: true
44
57
  homepage: http://wiretap.rubyforge.org/timecode
45
58
  post_install_message:
@@ -62,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
75
  version:
63
76
  requirements: []
64
77
 
65
- rubyforge_project: wiretap
78
+ rubyforge_project: guerilla-di
66
79
  rubygems_version: 1.3.1
67
80
  signing_key:
68
81
  specification_version: 2