timecode 0.1.4 → 0.1.5

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.
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