fairtilizer-vpim 0.695

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/CHANGES +510 -0
  2. data/COPYING +58 -0
  3. data/README +182 -0
  4. data/lib/vpim.rb +13 -0
  5. data/lib/vpim/address.rb +219 -0
  6. data/lib/vpim/agent/atomize.rb +104 -0
  7. data/lib/vpim/agent/base.rb +73 -0
  8. data/lib/vpim/agent/calendars.rb +173 -0
  9. data/lib/vpim/agent/handler.rb +26 -0
  10. data/lib/vpim/agent/ics.rb +161 -0
  11. data/lib/vpim/attachment.rb +102 -0
  12. data/lib/vpim/date.rb +222 -0
  13. data/lib/vpim/dirinfo.rb +277 -0
  14. data/lib/vpim/duration.rb +119 -0
  15. data/lib/vpim/enumerator.rb +32 -0
  16. data/lib/vpim/field.rb +614 -0
  17. data/lib/vpim/icalendar.rb +384 -0
  18. data/lib/vpim/maker/vcard.rb +16 -0
  19. data/lib/vpim/property/base.rb +193 -0
  20. data/lib/vpim/property/common.rb +315 -0
  21. data/lib/vpim/property/location.rb +38 -0
  22. data/lib/vpim/property/priority.rb +43 -0
  23. data/lib/vpim/property/recurrence.rb +69 -0
  24. data/lib/vpim/property/resources.rb +24 -0
  25. data/lib/vpim/repo.rb +261 -0
  26. data/lib/vpim/rfc2425.rb +367 -0
  27. data/lib/vpim/rrule.rb +591 -0
  28. data/lib/vpim/time.rb +40 -0
  29. data/lib/vpim/vcard.rb +1426 -0
  30. data/lib/vpim/version.rb +18 -0
  31. data/lib/vpim/vevent.rb +187 -0
  32. data/lib/vpim/view.rb +90 -0
  33. data/lib/vpim/vjournal.rb +58 -0
  34. data/lib/vpim/vpim.rb +65 -0
  35. data/lib/vpim/vtodo.rb +103 -0
  36. data/samples/README.mutt +93 -0
  37. data/samples/ab-query.rb +57 -0
  38. data/samples/agent.ru +10 -0
  39. data/samples/cmd-itip.rb +156 -0
  40. data/samples/ex_cpvcard.rb +55 -0
  41. data/samples/ex_get_vcard_photo.rb +22 -0
  42. data/samples/ex_mkv21vcard.rb +34 -0
  43. data/samples/ex_mkvcard.rb +64 -0
  44. data/samples/ex_mkyourown.rb +29 -0
  45. data/samples/ics-dump.rb +210 -0
  46. data/samples/ics-to-rss.rb +84 -0
  47. data/samples/mutt-aliases-to-vcf.rb +45 -0
  48. data/samples/osx-wrappers.rb +86 -0
  49. data/samples/reminder.rb +209 -0
  50. data/samples/rrule.rb +71 -0
  51. data/samples/tabbed-file-to-vcf.rb +390 -0
  52. data/samples/vcf-dump.rb +86 -0
  53. data/samples/vcf-lines.rb +61 -0
  54. data/samples/vcf-to-ics.rb +22 -0
  55. data/samples/vcf-to-mutt.rb +121 -0
  56. data/test/test_agent_atomize.rb +84 -0
  57. data/test/test_agent_calendars.rb +128 -0
  58. data/test/test_agent_ics.rb +96 -0
  59. data/test/test_all.rb +17 -0
  60. data/test/test_date.rb +120 -0
  61. data/test/test_dur.rb +41 -0
  62. data/test/test_field.rb +156 -0
  63. data/test/test_ical.rb +437 -0
  64. data/test/test_misc.rb +13 -0
  65. data/test/test_repo.rb +129 -0
  66. data/test/test_rrule.rb +1030 -0
  67. data/test/test_vcard.rb +973 -0
  68. data/test/test_view.rb +79 -0
  69. metadata +140 -0
@@ -0,0 +1,96 @@
1
+ require 'test/common'
2
+ require 'sinatra/test'
3
+ require 'vpim/agent/ics'
4
+
5
+ class TestIcsAgent < Test::Unit::TestCase
6
+ include Sinatra::Test
7
+
8
+ def to_str
9
+ @caldata
10
+ end
11
+
12
+ def setup
13
+ @thrd = data_on_port(self, 9876)
14
+ @app = Vpim::Agent::Ics
15
+ end
16
+ def teardown
17
+ @thrd.kill
18
+ end
19
+
20
+ def _test_ics_atom_query(scheme)
21
+ @caldata = open("test/weekly.ics").read
22
+
23
+ get "/atom?#{scheme}://127.0.0.1:9876"
24
+
25
+ assert_match(/<\?xml/, @response.body)
26
+ assert_equal(Vpim::Agent::Atomize::MIME, @response["Content-Type"])
27
+ assert_equal(200, @response.status)
28
+ assert_match(Regexp.new( Regexp.quote(
29
+ "<id>http://example.org/atom?#{scheme}://127.0.0.1:9876</id>"
30
+ )), @response.body)
31
+ end
32
+
33
+ def test_ics_atom_query_http
34
+ _test_ics_atom_query "http"
35
+ end
36
+
37
+ def test_ics_atom_query_webcal
38
+ _test_ics_atom_query "webcal"
39
+ end
40
+
41
+ def test_ics
42
+ get ""
43
+
44
+ assert_match(/<html/, @response.body)
45
+ assert_equal("text/html", @response["Content-Type"])
46
+ assert_equal(200, @response.status)
47
+ assert_match(Regexp.new(Regexp.quote("<title>Subscribe")), @response.body)
48
+ end
49
+
50
+ def test_ics_query
51
+ @caldata = open("test/calendars/weather.calendar/Events/1205042405-0-0.ics").read
52
+
53
+ get "?http://127.0.0.1:9876"
54
+
55
+ assert_match(/<html/, @response.body)
56
+ assert_equal("text/html", @response["Content-Type"])
57
+ assert_equal(200, @response.status)
58
+
59
+ assert_match(Regexp.new(Regexp.quote("Subscribe to")), @response.body)
60
+ end
61
+
62
+ def test_ics_post
63
+ post "/", :url => "http://example.com"
64
+ assert_equal(302, @response.status)
65
+ assert_equal("http://example.org?http://example.com", @response.headers["Location"])
66
+ end
67
+
68
+ def test_ics_atom
69
+ get "/atom"
70
+ assert_equal(302, @response.status)
71
+ assert_equal("http://example.org", @response.headers["Location"])
72
+ end
73
+
74
+ def test_ics_style
75
+ get "/style.css"
76
+ assert_match(/body \{/, @response.body)
77
+ assert_equal("text/css", @response["Content-Type"])
78
+ assert_equal(200, @response.status)
79
+ end
80
+
81
+ def test_ics_query_bad
82
+ get "/?url://example.com"
83
+ assert_equal(200, @response.status)
84
+ assert_match(/Sorry/, @response.body)
85
+ end
86
+
87
+ def test_ics_atom_query_bad
88
+ #Vpim::Agent::Ics.enable :raise_errors
89
+
90
+ get "/atom?url://example.com"
91
+ assert_equal(302, @response.status)
92
+ assert_equal("http://example.org?url://example.com", @response.headers["Location"])
93
+ end
94
+
95
+ end
96
+
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+
5
+ $-w = true
6
+
7
+ $:.unshift File.dirname(__FILE__) + "/../lib"
8
+
9
+
10
+ #pp [__LINE__, $:, $"]
11
+
12
+ require 'test/unit'
13
+
14
+ Dir[File.dirname(__FILE__) + "/test_*.rb"].each do |test|
15
+ require test unless test =~ /test_all/
16
+ end
17
+
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vpim/date'
4
+ require 'vpim/time'
5
+ require 'test/unit'
6
+
7
+ class TestVpimDate < Test::Unit::TestCase
8
+
9
+ def test_to_time
10
+ # Need to test with DateTime, but I don't have that with ruby 1.6.
11
+ assert_equal(Time.at(0), Date.new(1970, 1, 1).vpim_to_time)
12
+ assert_equal(Time.at(24 * 60 * 60), Date.new(1970, 1, 2).vpim_to_time)
13
+ end
14
+
15
+ def test_date_weekstart
16
+ assert_equal(Date.weekstart(2004, 01, 12, 'tu').to_s, Date.new(2004, 01, 6).to_s)
17
+ assert_equal(Date.weekstart(2004, 01, 12, 'we').to_s, Date.new(2004, 01, 7).to_s)
18
+ assert_equal(Date.weekstart(2004, 01, 12, 'th').to_s, Date.new(2004, 01, 8).to_s)
19
+ assert_equal(Date.weekstart(2004, 01, 12, 'fr').to_s, Date.new(2004, 01, 9).to_s)
20
+ assert_equal(Date.weekstart(2004, 01, 12, 'sa').to_s, Date.new(2004, 01, 10).to_s)
21
+ assert_equal(Date.weekstart(2004, 01, 12, 'su').to_s, Date.new(2004, 01, 11).to_s)
22
+ assert_equal(Date.weekstart(2004, 01, 12, 'mo').to_s, Date.new(2004, 01, 12).to_s)
23
+ end
24
+
25
+ def do_bywday(args, expect)
26
+ # Rewrite 'string' weekday specifications.
27
+ args = [ args[0], args[1], Date.str2wday(args[2]), args[3] ]
28
+ date = Date.bywday(*args)
29
+ dates = DateGen.bywday(*args)
30
+ need = Date.new(*expect)
31
+
32
+ assert_equal(date, need)
33
+
34
+ # Date.bywday always produces a single date, so should the generator, in
35
+ # this case.
36
+ assert_equal(dates[0], need)
37
+ end
38
+
39
+ def test_bywday
40
+
41
+ # 2004
42
+ #
43
+ # January February March
44
+ # S M Tu W Th F S S M Tu W Th F S S M Tu W Th F S
45
+ # 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6
46
+ # 4 5 6 7 8 9 10 8 9 10 11 12 13 14 7 8 9 10 11 12 13
47
+ # 11 12 13 14 15 16 17 15 16 17 18 19 20 21 14 15 16 17 18 19 20
48
+ # 18 19 20 21 22 23 24 22 23 24 25 26 27 28 21 22 23 24 25 26 27
49
+ # 25 26 27 28 29 30 31 29 28 29 30 31
50
+ #
51
+ do_bywday([2004, 1, 4, 1], [2004, 1, 1])
52
+ do_bywday([2004, 1, 4, 2], [2004, 1, 8])
53
+ do_bywday([2004, 1, 4, -1], [2004, 1, 29])
54
+ do_bywday([2004, 1, 4, -2], [2004, 1, 22])
55
+ do_bywday([2004, 1, 4, -5], [2004, 1, 1])
56
+ do_bywday([2004,nil, 4, 1], [2004, 1, 1])
57
+ do_bywday([2004,nil, 4, 2], [2004, 1, 8])
58
+ do_bywday([2004,-12, 4, 1], [2004, 1, 1])
59
+ do_bywday([2004,-12, 4, 2], [2004, 1, 8])
60
+ do_bywday([2004,-12, 4, -1], [2004, 1, 29])
61
+ do_bywday([2004,-12, 4, -2], [2004, 1, 22])
62
+ do_bywday([2004,-12, 4, -5], [2004, 1, 1])
63
+
64
+ do_bywday([2004, 1, "th", 1], [2004, 1, 1])
65
+ do_bywday([2004, 1, "th", 2], [2004, 1, 8])
66
+ do_bywday([2004, 1, "th", -1], [2004, 1, 29])
67
+ do_bywday([2004, 1, "th", -2], [2004, 1, 22])
68
+ do_bywday([2004, 1, "th", -5], [2004, 1, 1])
69
+ do_bywday([2004,nil, "th", 1], [2004, 1, 1])
70
+ do_bywday([2004,nil, "th", 2], [2004, 1, 8])
71
+ do_bywday([2004,-12, "th", 1], [2004, 1, 1])
72
+ do_bywday([2004,-12, "th", 2], [2004, 1, 8])
73
+ do_bywday([2004,-12, "th", -1], [2004, 1, 29])
74
+ do_bywday([2004,-12, "th", -2], [2004, 1, 22])
75
+ do_bywday([2004,-12, "th", -5], [2004, 1, 1])
76
+
77
+ # October November December
78
+ # S M Tu W Th F S S M Tu W Th F S S M Tu W Th F S
79
+ # 1 2 1 2 3 4 5 6 1 2 3 4
80
+ # 3 4 5 6 7 8 9 7 8 9 10 11 12 13 5 6 7 8 9 10 11
81
+ # 10 11 12 13 14 15 16 14 15 16 17 18 19 20 12 13 14 15 16 17 18
82
+ # 17 18 19 20 21 22 23 21 22 23 24 25 26 27 19 20 21 22 23 24 25
83
+ # 24 25 26 27 28 29 30 28 29 30 26 27 28 29 30 31
84
+ # 31
85
+
86
+ do_bywday([2004, -1, 4, 1], [2004, 12, 2])
87
+ do_bywday([2004, -1, 4, -1], [2004, 12, 30])
88
+ do_bywday([2004, -1, 4, -2], [2004, 12, 23])
89
+ do_bywday([2004, -1, 4, -5], [2004, 12, 2])
90
+ do_bywday([2004,nil, 4, -1], [2004, 12, 30])
91
+ do_bywday([2004,nil, 4, -2], [2004, 12, 23])
92
+ do_bywday([2004,nil, 4, -5], [2004, 12, 2])
93
+ do_bywday([2004,nil, 4, -7], [2004, 11, 18])
94
+
95
+ end
96
+
97
+ def do_gen(args)
98
+ assert_nothing_thrown do
99
+ dates = DateGen.bywday(*args)
100
+ dates.each do |d|
101
+ assert_equal(args[0], d.year)
102
+ if(args[1])
103
+ mon = args[1]
104
+ if mon < 0
105
+ mon = 13 + args[1]
106
+ end
107
+ assert_equal(mon, d.mon)
108
+ end
109
+ assert_equal(args[2], d.wday)
110
+ end
111
+ end
112
+ end
113
+
114
+ def test_gen
115
+ do_gen([2004, 12, 1])
116
+ do_gen([2004, -1, 1])
117
+ do_gen([2004, nil, 1])
118
+ end
119
+ end
120
+
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vpim/duration'
4
+ require 'test/unit'
5
+
6
+ include Vpim
7
+
8
+ class TestVpimDate < Test::Unit::TestCase
9
+
10
+ def duration(d0, h0, m0, s0)
11
+ # 3 hours, 2 mins, 39 secs
12
+ d = Duration.secs(d0 * 24 * 60 * 60 + h0 * 60 * 60 + m0 * 60 + s0)
13
+
14
+ assert_equal(d.secs, d0 * 24 * 60 * 60 + h0 * 60 * 60 + m0 * 60 + s0)
15
+ assert_equal(d.mins, d0 * 24 * 60 + h0 * 60 + m0)
16
+ assert_equal(d.hours, d0 * 24 + h0)
17
+ assert_equal(d.days, d0)
18
+ assert_equal(d.by_hours, [d0*24 + h0, m0, s0])
19
+ assert_equal(d.by_days, [d0, h0, m0, s0])
20
+
21
+ h, m, s = d.by_hours
22
+
23
+ assert_equal(h, h0 + d0*24)
24
+ assert_equal(m, m0)
25
+ assert_equal(s, s0)
26
+
27
+ d, h, m, s = d.by_days
28
+
29
+ assert_equal(d, d0)
30
+ assert_equal(h, h0)
31
+ assert_equal(m, m0)
32
+ assert_equal(s, s0)
33
+ end
34
+
35
+ def test_1
36
+ duration(0, 3, 2, 39)
37
+ duration(5, 23, 39, 1)
38
+ end
39
+
40
+ end
41
+
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'pp'
5
+ require 'vpim/field'
6
+
7
+ Field=Vpim::DirectoryInfo::Field
8
+
9
+ class TestField < Test::Unit::TestCase
10
+
11
+ def test_encode_decode_text()
12
+ enc_in = "+\\\\+\\n+\\N+\\,+\\;+\\a+\\b+\\c+"
13
+ dec = Vpim.decode_text(enc_in)
14
+ #puts("<#{enc_in}> => <#{dec}>")
15
+ assert_equal("+\\+\n+\n+,+;+a+b+c+", dec)
16
+ enc_out = Vpim.encode_text(dec)
17
+ should_be = "+\\\\+\\n+\\n+\\,+\\;+a+b+c+"
18
+ # Note a, b, and c are allowed to be escaped, but shouldn't be and
19
+ # aren't in output
20
+ #puts("<#{dec}> => <#{enc_out}>")
21
+ assert_equal(should_be, enc_out)
22
+
23
+ end
24
+
25
+ def test_field4
26
+ line = 't;e=a,b: 4 '
27
+ part = Field.decode0(line)
28
+ assert_equal("4", part[ 3 ])
29
+ end
30
+
31
+ def test_field3
32
+ line = 't;e=a,b:4'
33
+ part = Field.decode0(line)
34
+ assert_equal("4", part[ 3 ])
35
+ assert_equal( {'E' => [ 'a','b' ] }, part[ 2 ])
36
+ end
37
+
38
+ def test_field2
39
+ line = 'tel;type=work,voice,msg:+1 313 747-4454'
40
+ part = Field.decode0(line)
41
+ assert_equal("+1 313 747-4454", part[ 3 ])
42
+ assert_equal( {'TYPE' => [ 'work','voice','msg' ] }, part[ 2 ])
43
+ end
44
+
45
+ def test_field1
46
+ line = 'ORGANIZER;CN="xxxx, xxxx [SC100:370:EXCH]":MAILTO:xxxx@americasm01.nt.com'
47
+ parts = Field.decode0(line)
48
+
49
+ assert_equal(nil, parts[0])
50
+ assert_equal('ORGANIZER', parts[1])
51
+ assert_equal({ 'CN' => [ "xxxx, xxxx [SC100:370:EXCH]" ] }, parts[2])
52
+ assert_equal('MAILTO:xxxx@americasm01.nt.com', parts[3])
53
+ end
54
+
55
+ =begin this can not be done :-(
56
+ def test_case_equiv
57
+ line = 'ORGANIZER;CN="xxxx, xxxx [SC100:370:EXCH]":MAILTO:xxxx@americasm01.nt.com'
58
+ field = Field.decode(line)
59
+ assert_equal(true, field.name?('organIZER'))
60
+ assert_equal(true, field === 'organIZER')
61
+
62
+ b = nil
63
+ case field
64
+ when 'organIZER'
65
+ b = true
66
+ end
67
+
68
+ assert_equal(true, b)
69
+ end
70
+ =end
71
+
72
+ def test_field0
73
+ assert_equal('name:', line = Field.encode0(nil, 'name'))
74
+ assert_equal([ nil, 'NAME', {}, ''], Field.decode0(line))
75
+
76
+ assert_equal('name:value', line = Field.encode0(nil, 'name', {}, 'value'))
77
+ assert_equal([ nil, 'NAME', {}, 'value'], Field.decode0(line))
78
+
79
+ assert_equal('name;encoding=B:dmFsdWU=', line = Field.encode0(nil, 'name', { 'encoding'=>:b64 }, 'value'))
80
+ assert_equal([ nil, 'NAME', { 'ENCODING'=>['B']}, ['value'].pack('m').chomp ], Field.decode0(line))
81
+
82
+ assert_equal('group.name:value', line = Field.encode0('group', 'name', {}, 'value'))
83
+ assert_equal([ 'GROUP', 'NAME', {}, 'value'], Field.decode0(line))
84
+ end
85
+
86
+ def tEst_invalid_fields
87
+ [
88
+ 'g.:',
89
+ ':v',
90
+ ].each do |line|
91
+ assert_raises(Vpim::InvalidEncodingError) { Field.decode0(line) }
92
+ end
93
+ end
94
+
95
+ def test_date_encode
96
+ assert_equal("DTSTART:20040101\n", Field.create('DTSTART', Date.new(2004, 1, 1) ).to_s)
97
+ assert_equal("DTSTART:20040101\n", Field.create('DTSTART', [Date.new(2004, 1, 1)]).to_s)
98
+ end
99
+
100
+ def test_field_modify
101
+ f = Field.create('name')
102
+
103
+ assert_equal('', f.value)
104
+ f.value = ''
105
+ assert_equal('', f.value)
106
+ f.value = 'z'
107
+ assert_equal('z', f.value)
108
+
109
+ f.group = 'z.b'
110
+ assert_equal('Z.B', f.group)
111
+ assert_equal("z.b.NAME:z\n", f.encode)
112
+
113
+ assert_raises(TypeError) { f.value = :group }
114
+
115
+ assert_equal('Z.B', f.group)
116
+
117
+ assert_equal("z.b.NAME:z\n", f.encode)
118
+
119
+ assert_raises(TypeError) { f.group = :group }
120
+
121
+ assert_equal("z.b.NAME:z\n", f.encode)
122
+ assert_equal('Z.B', f.group)
123
+
124
+ f['p0'] = "hi julie"
125
+
126
+ assert_equal("Z.B.NAME;P0=hi julie:z\n", f.encode)
127
+ assert_equal(['hi julie'], f.param('p0'))
128
+ assert_equal(['hi julie'], f['p0'])
129
+ assert_equal('NAME', f.name)
130
+ assert_equal('Z.B', f.group)
131
+
132
+ # FAIL assert_raises(ArgumentError) { f.group = 'z.b:' }
133
+
134
+ assert_equal('Z.B', f.group)
135
+
136
+ f.value = 'some text'
137
+
138
+ assert_equal('some text', f.value)
139
+ assert_equal('some text', f.value_raw)
140
+
141
+ f['encoding'] = :b64
142
+
143
+ assert_equal('some text', f.value)
144
+ assert_equal([ 'some text' ].pack('m*').chomp, f.value_raw)
145
+ end
146
+
147
+ def test_field_wrapping
148
+ assert_equal("0:x\n", Vpim::DirectoryInfo::Field.create('0', 'x' * 1).encode(4))
149
+ assert_equal("0:xx\n", Vpim::DirectoryInfo::Field.create('0', 'x' * 2).encode(4))
150
+ assert_equal("0:xx\n x\n", Vpim::DirectoryInfo::Field.create('0', 'x' * 3).encode(4))
151
+ assert_equal("0:xx\n xx\n", Vpim::DirectoryInfo::Field.create('0', 'x' * 4).encode(4))
152
+ assert_equal("0:xx\n xxxx\n", Vpim::DirectoryInfo::Field.create('0', 'x' * 6).encode(4))
153
+ assert_equal("0:xx\n xxxx\n x\n", Vpim::DirectoryInfo::Field.create('0', 'x' * 7).encode(4))
154
+ end
155
+ end
156
+
@@ -0,0 +1,437 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vpim/icalendar'
4
+ require 'test/unit'
5
+
6
+
7
+ # Sorry for the donkey patching...
8
+ module Enumerable
9
+ def first
10
+ find{true}
11
+ end
12
+ def last
13
+ inject{|memo, o| o}
14
+ end
15
+ end
16
+
17
+ include Vpim
18
+
19
+ Req_1 =<<___
20
+ BEGIN:VCALENDAR
21
+ METHOD:REQUEST
22
+ PRODID:-//Lotus Development Corporation//NONSGML Notes 6.0//EN
23
+ VERSION:2.0
24
+ X-LOTUS-CHARSET:UTF-8
25
+ BEGIN:VTIMEZONE
26
+ TZID:Pacific
27
+ BEGIN:STANDARD
28
+ DTSTART:19501029T020000
29
+ TZOFFSETFROM:-0700
30
+ TZOFFSETTO:-0800
31
+ RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=10
32
+ END:STANDARD
33
+ BEGIN:DAYLIGHT
34
+ DTSTART:19500402T020000
35
+ TZOFFSETFROM:-0800
36
+ TZOFFSETTO:-0700
37
+ RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=1SU;BYMONTH=4
38
+ END:DAYLIGHT
39
+ END:VTIMEZONE
40
+ BEGIN:VEVENT
41
+ ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED;CN="Gary Pope/Certicom"
42
+ ;RSVP=FALSE:mailto:gpope@certicom.com
43
+ ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
44
+ ;CN="Mike Harvey/Certicom";RSVP=TRUE:mailto:MHarvey@certicom.com
45
+ ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE
46
+ :mailto:rgallant@emilpost.certicom.com
47
+ ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
48
+ ;CN="Sam Roberts/Certicom";RSVP=TRUE:mailto:SRoberts@certicom.com
49
+ ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
50
+ ;CN="Tony Walters/Certicom";RSVP=TRUE:mailto:TWalters@certicom.com
51
+ CLASS:PUBLIC
52
+ DTEND;TZID="Pacific":20040415T130000
53
+ DTSTAMP:20040319T205045Z
54
+ DTSTART;TZID="Pacific":20040415T120000
55
+ ORGANIZER;CN="Gary Pope/Certicom":mailto:gpope@certicom.com
56
+ SEQUENCE:0
57
+ SUMMARY:hjold intyel
58
+ TRANSP:OPAQUE
59
+ UID:3E19204063C93D2388256E5C006BF8D9-Lotus_Notes_Generated
60
+ X-LOTUS-BROADCAST:FALSE
61
+ X-LOTUS-CHILD_UID:3E19204063C93D2388256E5C006BF8D9
62
+ X-LOTUS-NOTESVERSION:2
63
+ X-LOTUS-NOTICETYPE:I
64
+ X-LOTUS-UPDATE-SEQ:1
65
+ X-LOTUS-UPDATE-WISL:$S:1;$L:1;$B:1;$R:1;$E:1
66
+ END:VEVENT
67
+ END:VCALENDAR
68
+ ___
69
+
70
+ Rep_1 =<<___
71
+ BEGIN:VCALENDAR
72
+ CALSCALE:GREGORIAN
73
+ PRODID:-//Apple Computer\, Inc//iCal 1.5//EN
74
+ VERSION:2.0
75
+ METHOD:REPLY
76
+ BEGIN:VEVENT
77
+ ATTENDEE;CN="Sam Roberts/Certicom";PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
78
+ T;RSVP=TRUE:mailto:SRoberts@certicom.com
79
+ CLASS:PUBLIC
80
+ DTEND;TZID=Pacific:20040415T130000
81
+ DTSTAMP:20040319T205045Z
82
+ DTSTART;TZID=Pacific:20040415T120000
83
+ ORGANIZER;CN="Gary Pope/Certicom":mailto:gpope@certicom.com
84
+ SEQUENCE:0
85
+ SUMMARY:hjold intyel
86
+ TRANSP:OPAQUE
87
+ UID:3E19204063C93D2388256E5C006BF8D9-Lotus_Notes_Generated
88
+ X-LOTUS-BROADCAST:FALSE
89
+ X-LOTUS-CHILDUID:3E19204063C93D2388256E5C006BF8D9
90
+ X-LOTUS-NOTESVERSION:2
91
+ X-LOTUS-NOTICETYPE:I
92
+ X-LOTUS-UPDATE-SEQ:1
93
+ X-LOTUS-UPDATE-WISL:$S:1\;$L:1\;$B:1\;$R:1\;$E:1
94
+ END:VEVENT
95
+ END:VCALENDAR
96
+ ___
97
+
98
+ class TestIcal < Test::Unit::TestCase
99
+
100
+ def assert_time(expected, actual, msg = nil)
101
+ assert_in_delta(expected, actual, 1, msg)
102
+ end
103
+
104
+ # Reported by Kyle Maxwell
105
+ def test_serialize_todo
106
+ icstodo =<<___
107
+ BEGIN:VCALENDAR
108
+ VERSION:2.0
109
+ BEGIN:VTODO
110
+ END:VTODO
111
+ END:VCALENDAR
112
+ ___
113
+
114
+ cal = Icalendar.decode(icstodo)
115
+ assert_equal(icstodo, cal.to_s)
116
+ end
117
+
118
+ # Tracker #18920
119
+ def test_recurring_todos
120
+ icstodo =<<___
121
+ BEGIN:VCALENDAR
122
+ VERSION:2.0
123
+ BEGIN:VTODO
124
+ SUMMARY:todo
125
+ DTSTART:20040415T120000
126
+ RRULE:FREQ=WEEKLY;COUNT=2
127
+ END:VTODO
128
+ END:VCALENDAR
129
+ ___
130
+
131
+ cal = Icalendar.decode(icstodo).first
132
+ todo = cal.todos.first
133
+ assert(todo)
134
+ assert_equal(todo.occurrences.to_a.size, 2)
135
+ end
136
+
137
+ def test_1
138
+ req = Icalendar.decode(Req_1).first
139
+
140
+ req.components { }
141
+ req.components(Icalendar::Vevent) { }
142
+ req.components(Icalendar::Vjournal) { }
143
+ assert_equal(1, req.components.size)
144
+ assert_equal(1, req.components(Icalendar::Vevent).size)
145
+ assert_equal(0, req.components(Icalendar::Vjournal).size)
146
+
147
+ assert_equal(req.protocol, 'REQUEST')
148
+
149
+ event = req.events.first
150
+
151
+ assert(event)
152
+
153
+ assert( event.attendee?('mailto:sroberts@certicom.com'))
154
+ assert(!event.attendee?('mailto:sroberts@uniserve.com'))
155
+
156
+ me = event.attendees('mailto:sroberts@certicom.com').first
157
+
158
+ assert(me)
159
+ assert(me == 'mailto:sroberts@certicom.com')
160
+
161
+ reply = Icalendar.create_reply
162
+
163
+ reply.push(event.accept(me))
164
+
165
+ # puts "Reply=>"
166
+ # puts reply.to_s
167
+ end
168
+
169
+ def test_hal1
170
+ # Hal was encoding raw strings, here's how to do it with the API.
171
+
172
+ cal = Icalendar.create
173
+
174
+ start = Time.now
175
+
176
+ event = Icalendar::Vevent.create(start,
177
+ 'DTEND' => start + 60 * 60,
178
+ 'SUMMARY' => "this is an event",
179
+ 'RRULE' => 'freq=monthly;byday=2fr,4fr;count=5'
180
+ )
181
+
182
+ cal.push event
183
+
184
+ #puts cal.encode
185
+ end
186
+
187
+ # FIXME - test bad durations, like 'PT1D'
188
+
189
+ def test_event_duration
190
+ now = Time.now
191
+ event = Icalendar::Vevent.create(now)
192
+ assert_time(now, event.dtstart)
193
+ assert_nil(event.duration)
194
+ assert_nil(event.dtend)
195
+
196
+ event = Icalendar::Vevent.create(Date.new(2000, 1, 21),
197
+ 'DURATION' => 'PT1H')
198
+ assert_equal(Time.gm(2000, 1, 21, 1), event.dtend)
199
+
200
+ event = Icalendar::Vevent.create(Date.new(2000, 1, 21),
201
+ 'DTEND' => Date.new(2000, 1, 22))
202
+ assert_equal(24*60*60, event.duration)
203
+ end
204
+
205
+ def test_todo_duration
206
+ todo = Icalendar::Vtodo.create()
207
+ assert_nil(todo.dtstart)
208
+ assert_nil(todo.duration)
209
+ assert_nil(todo.due)
210
+
211
+ todo = Icalendar::Vtodo.create('DTSTART' => Date.new(2000, 1, 21),
212
+ 'DURATION' => 'PT1H')
213
+ assert_equal(Time.gm(2000, 1, 21, 1), todo.due)
214
+
215
+ todo = Icalendar::Vtodo.create('DTSTART' => Date.new(2000, 1, 21),
216
+ 'DUE' => Date.new(2000, 1, 22))
217
+ assert_equal(24*60*60, todo.duration)
218
+ end
219
+
220
+ def test_journal_create
221
+ vj = Icalendar::Vjournal.create('DESCRIPTION' => "description")
222
+ assert_equal("description", vj.description)
223
+ end
224
+
225
+ def TODO_test_occurrence_with_date_start
226
+ d = Date.new(2000, 1, 21)
227
+ event = Icalendar::Vevent.create(d)
228
+ d1 = event.occurences.to_a.first
229
+ assert_equal(d.class, d1.class)
230
+ end
231
+
232
+ def test_decode_duration_four_weeks
233
+ assert_equal 4*7*86400, Icalendar.decode_duration('P4W')
234
+ end
235
+
236
+ def test_decode_duration_negative_two_weeks
237
+ assert_equal(-2*7*86400, Icalendar.decode_duration('-P2W'))
238
+ end
239
+
240
+ def test_decode_duration_five_days
241
+ assert_equal 5*86400, Icalendar.decode_duration('P5D')
242
+ end
243
+
244
+ def test_decode_duration_one_hour
245
+ assert_equal 3600, Icalendar.decode_duration('PT1H')
246
+ end
247
+
248
+ def test_decode_duration_five_minutes
249
+ assert_equal 5*60, Icalendar.decode_duration('PT5M')
250
+ end
251
+
252
+ def test_decode_duration_ten_seconds
253
+ assert_equal 10, Icalendar.decode_duration('PT10S')
254
+ end
255
+
256
+ def test_decode_duration_with_leading_plus
257
+ assert_equal 10, Icalendar.decode_duration('+PT10S')
258
+ end
259
+
260
+ def test_decode_duration_with_composite_duration
261
+ assert_equal((15*86400+5*3600+20), Icalendar.decode_duration('P15DT5H0M20S'))
262
+ end
263
+
264
+ def test_create_with_prodid
265
+ prodid = "me//here/non-sgml"
266
+ cal = Icalendar.create2(prodid) do |cal|
267
+ assert_respond_to(cal, :push)
268
+ end
269
+ assert_equal(prodid, cal.producer)
270
+ end
271
+
272
+ def test_occurences
273
+ t0 = Time.now
274
+ vc = Icalendar.create2 do |vc|
275
+ vc.add_event do |ve|
276
+ ve.dtstart t0
277
+ end
278
+ end
279
+ ve = vc.components(Icalendar::Vevent).first
280
+ assert_time(t0, ve.occurences.select{true}.first)
281
+ ve.occurences do |t|
282
+ assert_time(t0, t)
283
+ end
284
+
285
+ vc = Icalendar.decode(<<__).first
286
+ BEGIN:VCALENDAR
287
+ BEGIN:VEVENT
288
+ END:VEVENT
289
+ END:VCALENDAR
290
+ __
291
+ ve = vc.components(Icalendar::Vevent).first
292
+ assert_raises(ArgumentError) { ve.occurences }
293
+ end
294
+
295
+ def test_each
296
+ vc = Icalendar.decode(<<__).first
297
+ BEGIN:VCALENDAR
298
+ BEGIN:VEVENT
299
+ END:VEVENT
300
+ BEGIN:VTODO
301
+ END:VTODO
302
+ BEGIN:VJOURNAL
303
+ END:VJOURNAL
304
+ BEGIN:VTIMEZONE
305
+ END:VTIMEZONE
306
+ BEGIN:X-UNKNOWN
307
+ END:X-UNKNOWN
308
+ END:VCALENDAR
309
+ __
310
+ count = Hash.new(0)
311
+
312
+ vc.each do |c| count[c.class] += 1 end
313
+
314
+ assert_equal(3, count.size)
315
+ count.each do |_,v| assert_equal(1, v) end
316
+
317
+ count = Hash.new(0)
318
+ vc.events do |c| count[c.class] += 1 end
319
+ vc.todos do |c| count[c.class] += 1 end
320
+ vc.journals do |c| count[c.class] += 1 end
321
+ assert_equal(3, count.size)
322
+ count.each do |_,v| assert_equal(1, v) end
323
+
324
+ assert_equal(3, vc.each.to_a.size)
325
+ assert_equal(1, vc.each.select{|c| Vpim::Icalendar::Vevent === c}.size)
326
+ assert_equal(1, vc.each.select{|c| Vpim::Icalendar::Vtodo === c}.size)
327
+ assert_equal(1, vc.each.select{|c| Vpim::Icalendar::Vjournal === c}.size)
328
+
329
+ assert_equal(1, vc.events.to_a.size)
330
+ assert_equal(1, vc.todos.to_a.size)
331
+ assert_equal(1, vc.journals.to_a.size)
332
+
333
+ vc.to_s # Shouldn't raise...
334
+ # TODO - encode isn't round-tripping, unknown components are lost, which is
335
+ # not good.
336
+ end
337
+
338
+ def test_calscale
339
+ req = Icalendar.decode(<<__).first
340
+ BEGIN:VCALENDAR
341
+ END:VCALENDAR
342
+ __
343
+ assert_equal("GREGORIAN", req.calscale)
344
+ req = Icalendar.decode(<<__).first
345
+ BEGIN:VCALENDAR
346
+ CALSCALE:GREGORIAN
347
+ END:VCALENDAR
348
+ __
349
+ assert_equal("GREGORIAN", req.calscale)
350
+ end
351
+
352
+ def test_version
353
+ req = Icalendar.decode(<<__).first
354
+ BEGIN:VCALENDAR
355
+ END:VCALENDAR
356
+ __
357
+ assert_raises(InvalidEncodingError) { req.version }
358
+
359
+ req = Icalendar.decode(<<__).first
360
+ BEGIN:VCALENDAR
361
+ VERSION:2.0
362
+ END:VCALENDAR
363
+ __
364
+ assert_equal(20, req.version)
365
+ end
366
+
367
+ def test_protocol
368
+ req = Icalendar.decode(<<__).first
369
+ BEGIN:VCALENDAR
370
+ METHOD:GET
371
+ END:VCALENDAR
372
+ __
373
+ assert(req.protocol?("get"))
374
+ assert(!req.protocol?("set"))
375
+ end
376
+
377
+ def test_transparency
378
+ transparency = Icalendar.decode(<<__).first.to_a.first.transparency
379
+ BEGIN:VCALENDAR
380
+ BEGIN:VEVENT
381
+ END:VEVENT
382
+ END:VCALENDAR
383
+ __
384
+ assert_equal("OPAQUE", transparency, "check default")
385
+
386
+ transparency = Icalendar.decode(<<__).first.to_a.first.transparency
387
+ BEGIN:VCALENDAR
388
+ BEGIN:VEVENT
389
+ TRANSP:Opaque
390
+ END:VEVENT
391
+ END:VCALENDAR
392
+ __
393
+ assert_equal("OPAQUE", transparency, "check opaque")
394
+
395
+ transparency = Icalendar.decode(<<__).first.to_a.first.transparency
396
+ BEGIN:VCALENDAR
397
+ BEGIN:VEVENT
398
+ TRANSP:TrANSPARENT
399
+ END:VEVENT
400
+ END:VCALENDAR
401
+ __
402
+ assert_equal("TRANSPARENT", transparency, "check transparent")
403
+
404
+ end
405
+
406
+ def test_location
407
+ cal = Icalendar.decode(<<__).first
408
+ BEGIN:VCALENDAR
409
+ BEGIN:VEVENT
410
+ LOCATION:bien located
411
+ END:VEVENT
412
+ BEGIN:VTODO
413
+ LOCATION:
414
+ END:VTODO
415
+ BEGIN:VEVENT
416
+ END:VEVENT
417
+ END:VCALENDAR
418
+ __
419
+ assert_equal(cal.events.first.location, "bien located")
420
+ assert_equal(cal.todos.first.location, "")
421
+ assert_equal(cal.events.last.location, nil)
422
+ end
423
+
424
+
425
+ def test_event_maker_w_rrule
426
+ vc = Icalendar.create2 do |vc|
427
+ vc.add_event do |m|
428
+ m.add_rrule("freq=monthly")
429
+ m.set_rrule do |_| _.frequency = "daily" end
430
+ end
431
+ end
432
+ assert_no_match(/RRULE:FREQ=MONTHLY/, vc.to_s)
433
+ assert_match(/RRULE:FREQ=DAILY/, vc.to_s)
434
+ end
435
+
436
+ end
437
+