rb-scpt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +497 -0
  3. data/doc/aem-manual/01_introduction.html +60 -0
  4. data/doc/aem-manual/02_apioverview.html +107 -0
  5. data/doc/aem-manual/03_packingandunpackingdata.html +135 -0
  6. data/doc/aem-manual/04_references.html +409 -0
  7. data/doc/aem-manual/05_targetingapplications.html +164 -0
  8. data/doc/aem-manual/06_buildingandsendingevents.html +229 -0
  9. data/doc/aem-manual/07_findapp.html +63 -0
  10. data/doc/aem-manual/08_examples.html +94 -0
  11. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  12. data/doc/aem-manual/index.html +56 -0
  13. data/doc/appscript-manual/01_introduction.html +94 -0
  14. data/doc/appscript-manual/02_aboutappscripting.html +247 -0
  15. data/doc/appscript-manual/03_quicktutorial.html +167 -0
  16. data/doc/appscript-manual/04_gettinghelp.html +188 -0
  17. data/doc/appscript-manual/05_keywordconversion.html +106 -0
  18. data/doc/appscript-manual/06_classesandenums.html +192 -0
  19. data/doc/appscript-manual/07_applicationobjects.html +211 -0
  20. data/doc/appscript-manual/08_realvsgenericreferences.html +96 -0
  21. data/doc/appscript-manual/09_referenceforms.html +241 -0
  22. data/doc/appscript-manual/10_referenceexamples.html +154 -0
  23. data/doc/appscript-manual/11_applicationcommands.html +245 -0
  24. data/doc/appscript-manual/12_commandexamples.html +138 -0
  25. data/doc/appscript-manual/13_performanceissues.html +142 -0
  26. data/doc/appscript-manual/14_notes.html +80 -0
  27. data/doc/appscript-manual/application_architecture.gif +0 -0
  28. data/doc/appscript-manual/application_architecture2.gif +0 -0
  29. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  30. data/doc/appscript-manual/index.html +62 -0
  31. data/doc/appscript-manual/relationships_example.gif +0 -0
  32. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  33. data/doc/full.css +106 -0
  34. data/doc/index.html +45 -0
  35. data/doc/mactypes-manual/01_introduction.html +54 -0
  36. data/doc/mactypes-manual/02_aliasclass.html +124 -0
  37. data/doc/mactypes-manual/03_fileurlclass.html +126 -0
  38. data/doc/mactypes-manual/04_unitsclass.html +100 -0
  39. data/doc/mactypes-manual/index.html +53 -0
  40. data/doc/osax-manual/01_introduction.html +67 -0
  41. data/doc/osax-manual/02_interface.html +147 -0
  42. data/doc/osax-manual/03_examples.html +73 -0
  43. data/doc/osax-manual/04_notes.html +61 -0
  44. data/doc/osax-manual/index.html +53 -0
  45. data/doc/rb-appscript-logo.png +0 -0
  46. data/extconf.rb +65 -0
  47. data/rb-scpt.gemspec +14 -0
  48. data/sample/AB_export_vcard.rb +31 -0
  49. data/sample/AB_list_people_with_emails.rb +13 -0
  50. data/sample/Add_iCal_event.rb +21 -0
  51. data/sample/Create_daily_iCal_todos.rb +75 -0
  52. data/sample/Export_Address_Book_phone_numbers.rb +59 -0
  53. data/sample/Hello_world.rb +21 -0
  54. data/sample/List_iTunes_playlist_names.rb +11 -0
  55. data/sample/Make_Mail_message.rb +33 -0
  56. data/sample/Open_file_in_TextEdit.rb +13 -0
  57. data/sample/Organize_Mail_messages.rb +61 -0
  58. data/sample/Print_folder_tree.rb +16 -0
  59. data/sample/Select_all_HTML_files.rb +14 -0
  60. data/sample/Set_iChat_status.rb +24 -0
  61. data/sample/Simple_Finder_GUI_Scripting.rb +18 -0
  62. data/sample/Stagger_Finder_windows.rb +25 -0
  63. data/sample/TextEdit_demo.rb +130 -0
  64. data/sample/iTunes_top40_to_html.rb +71 -0
  65. data/src/SendThreadSafe.c +380 -0
  66. data/src/SendThreadSafe.h +139 -0
  67. data/src/lib/_aem/aemreference.rb +1022 -0
  68. data/src/lib/_aem/codecs.rb +662 -0
  69. data/src/lib/_aem/connect.rb +205 -0
  70. data/src/lib/_aem/encodingsupport.rb +77 -0
  71. data/src/lib/_aem/findapp.rb +85 -0
  72. data/src/lib/_aem/mactypes.rb +251 -0
  73. data/src/lib/_aem/send.rb +279 -0
  74. data/src/lib/_aem/typewrappers.rb +59 -0
  75. data/src/lib/_appscript/defaultterminology.rb +277 -0
  76. data/src/lib/_appscript/referencerenderer.rb +245 -0
  77. data/src/lib/_appscript/reservedkeywords.rb +116 -0
  78. data/src/lib/_appscript/safeobject.rb +249 -0
  79. data/src/lib/_appscript/terminology.rb +471 -0
  80. data/src/lib/aem.rb +253 -0
  81. data/src/lib/appscript.rb +1075 -0
  82. data/src/lib/kae.rb +1489 -0
  83. data/src/lib/osax.rb +659 -0
  84. data/src/rbae.c +979 -0
  85. data/test/README +3 -0
  86. data/test/test_aemreference.rb +118 -0
  87. data/test/test_appscriptcommands.rb +152 -0
  88. data/test/test_appscriptreference.rb +106 -0
  89. data/test/test_codecs.rb +186 -0
  90. data/test/test_findapp.rb +26 -0
  91. data/test/test_mactypes.rb +79 -0
  92. data/test/test_osax.rb +54 -0
  93. data/test/testall.sh +10 -0
  94. metadata +145 -0
@@ -0,0 +1,3 @@
1
+ Some of the code in these tests may not yet be 100% portable (hardcoded paths, etc) so may currently fail for some users, although they have passed for Ruby 1.8.x on OS X 10.3.9, OS X 10.4.x (PPC & i386) and OS X 10.5.1 (i386).
2
+
3
+ Note that some mactypes unit tests may fail as Alias and FileURL comparisons are rather dumb and don't normalise path strings before comparing/hashing. Future releases may try to remedy this.
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ begin; require 'rubygems'; rescue LoadError; end
4
+
5
+ require 'test/unit'
6
+ require "_aem/aemreference"
7
+ require "_aem/typewrappers"
8
+ require "_aem/codecs"
9
+
10
+
11
+ class TC_AEMReferences < Test::Unit::TestCase
12
+
13
+ def test_reference_forms
14
+ [
15
+ [AEMReference::App.property('ctxt'), 'AEM.app.property("ctxt")', nil],
16
+
17
+ [AEMReference::App.elements('docu'), 'AEM.app.elements("docu")', nil],
18
+
19
+ [AEMReference::App.elements('docu').by_index(1),
20
+ 'AEM.app.elements("docu").by_index(1)', nil],
21
+ [AEMReference::App.elements('docu').by_name('foo'),
22
+ 'AEM.app.elements("docu").by_name("foo")', nil],
23
+ [AEMReference::App.elements('docu').by_id(300),
24
+ 'AEM.app.elements("docu").by_id(300)', nil],
25
+
26
+ [AEMReference::App.elements('docu').next('docu'),
27
+ 'AEM.app.elements("docu").next("docu")', nil],
28
+ [AEMReference::App.elements('docu').previous('docu'),
29
+ 'AEM.app.elements("docu").previous("docu")', nil],
30
+
31
+ [AEMReference::App.elements('docu').first, 'AEM.app.elements("docu").first', nil],
32
+ [AEMReference::App.elements('docu').middle, 'AEM.app.elements("docu").middle', nil],
33
+ [AEMReference::App.elements('docu').last, 'AEM.app.elements("docu").last', nil],
34
+ [AEMReference::App.elements('docu').any, 'AEM.app.elements("docu").any', nil],
35
+
36
+ [AEMReference::Con.elements("docu").by_index(3), 'AEM.con.elements("docu").by_index(3)', nil],
37
+
38
+ [AEMReference::App.elements('docu').by_range(
39
+ AEMReference::Con.elements('docu').by_index(3),
40
+ AEMReference::Con.elements('docu').by_name('foo')),
41
+ 'AEM.app.elements("docu").by_range(' +
42
+ 'AEM.con.elements("docu").by_index(3), ' +
43
+ 'AEM.con.elements("docu").by_name("foo"))', nil],
44
+
45
+ [AEMReference::App.elements('docu').by_range(1, 'foo'),
46
+ 'AEM.app.elements("docu").by_range(1, "foo")',
47
+ AEMReference::App.elements("docu").by_range(
48
+ AEMReference::Con.elements("docu").by_index(1),
49
+ AEMReference::Con.elements("docu").by_name("foo"))],
50
+
51
+ [AEMReference::Its.property('name').eq('foo').and(AEMReference::Its.elements('cwor').eq([])),
52
+ 'AEM.its.property("name").eq("foo").and(AEM.its.elements("cwor").eq([]))', nil],
53
+
54
+ [AEMReference::Its.elements('cwor').ne([]),
55
+ 'AEM.its.elements("cwor").ne([])',
56
+ AEMReference::Its.elements("cwor").eq([]).not], # i.e. there isn't a KAENotEqual operator, so not-equal tests are actually packed as an equal test followed by not test
57
+
58
+ [AEMReference::Its.elements('cwor').eq(nil), 'AEM.its.elements("cwor").eq(nil)', nil],
59
+ [AEMReference::Its.elements('cwor').property('leng').gt(0),
60
+ 'AEM.its.elements("cwor").property("leng").gt(0)', nil],
61
+ [AEMReference::Its.elements('cwor').le(''), 'AEM.its.elements("cwor").le("")', nil],
62
+ [AEMReference::Its.elements('cwor').begins_with('foo').not,
63
+ 'AEM.its.elements("cwor").begins_with("foo").not', nil],
64
+
65
+
66
+ [AEMReference::Its.elements('cwor').contains('foo'), 'AEM.its.elements("cwor").contains("foo")', nil],
67
+ [AEMReference::Its.elements('cwor').is_in('foo'), 'AEM.its.elements("cwor").is_in("foo")', nil],
68
+
69
+ [AEMReference::App.elements('docu').by_filter(AEMReference::Its.property('size').ge(42)),
70
+ 'AEM.app.elements("docu").by_filter(AEM.its.property("size").ge(42))', nil],
71
+
72
+ [AEMReference::App.elements('docu').by_index(1).property('ctxt') \
73
+ .elements('cpar').elements('cha ').by_range(
74
+ AEMReference::Con.elements('cha ').by_index(3),
75
+ AEMReference::Con.elements('cha ').by_index(55)
76
+ ).next('cha ').after,
77
+ 'AEM.app.elements("docu").by_index(1).property("ctxt").elements("cpar").elements("cha ")' +
78
+ '.by_range(AEM.con.elements("cha ").by_index(3), AEM.con.elements("cha ").by_index(55))' +
79
+ '.next("cha ").after', nil],
80
+
81
+ [AEMReference::Its.property('pnam').ne('foo').and(AEMReference::Its.elements('cfol').eq([])).not,
82
+ 'AEM.its.property("pnam").ne("foo").and(AEM.its.elements("cfol").eq([])).not',
83
+ AEMReference::Its.property('pnam').eq('foo').not.and(AEMReference::Its.elements('cfol').eq([])).not],
84
+
85
+ [AEMReference::App.elements('docu').beginning, 'AEM.app.elements("docu").beginning', nil],
86
+ [AEMReference::App.elements('docu').end, 'AEM.app.elements("docu").end', nil],
87
+ [AEMReference::App.elements('docu').by_index(3).before, 'AEM.app.elements("docu").by_index(3).before', nil],
88
+ [AEMReference::App.elements('docu').by_name('foo').after, 'AEM.app.elements("docu").by_name("foo").after', nil],
89
+
90
+ ].each do |val, res, unpacked_version|
91
+ begin
92
+ assert_equal(res, val.to_s)
93
+ d = DefaultCodecs.pack(val)
94
+ val = unpacked_version ? unpacked_version : val
95
+ val2 = DefaultCodecs.unpack(d)
96
+ assert_equal(val, val2)
97
+ val2 = DefaultCodecs.unpack(d)
98
+ assert_block { val.eql?(val2) }
99
+ val2 = DefaultCodecs.unpack(d)
100
+ assert_equal(val2, val)
101
+ val2 = DefaultCodecs.unpack(d)
102
+ assert_block { val2.eql?(val) }
103
+ rescue
104
+ puts 'EXPECTED: ' + res
105
+ raise
106
+ end
107
+ end
108
+ assert_not_equal(AEMReference::App.property('ctxt').property('ctxt'), AEMReference::Con.property('ctxt').property('ctxt'))
109
+ assert_not_equal(AEMReference::App.property('foob').property('ctxt'), AEMReference::App.property('ctxt').property('ctxt'))
110
+ assert_not_equal(AEMReference::App.elements('ctxt').property('ctxt'), AEMReference::App.property('ctxt').property('ctxt'))
111
+ assert_not_equal(AEMReference::App.elements('ctxt').property('ctxt'), 333)
112
+ assert_not_equal(333, AEMReference::App.property('ctxt').property('ctxt'))
113
+ # # by-filter references do basic type checking to ensure an its-based reference is given
114
+ assert_raises(TypeError) { AEMReference::App.elements('docu').by_filter(1) }
115
+
116
+ end
117
+ end
118
+
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ begin; require 'rubygems'; rescue LoadError; end
4
+
5
+ require 'test/unit'
6
+ require 'appscript'
7
+
8
+ # rb-appscript 0.5.0+ should no longer require the following kludge:
9
+ #class AS_SafeObject
10
+ # def self.hide(name)
11
+ # end
12
+ #end
13
+
14
+ class TC_AppscriptNewApp < Test::Unit::TestCase
15
+
16
+ def test_by_name
17
+ [
18
+ '/Applications/TextEdit.app',
19
+ 'Finder.app',
20
+ 'System Events'
21
+ ].each do |name|
22
+ a = Appscript.app(name)
23
+ assert_not_nil(a)
24
+ assert_instance_of(Appscript::Application, a)
25
+ assert_instance_of(Appscript::Reference, a.name)
26
+ end
27
+ assert_equal('app("/Applications/TextEdit.app")', Appscript.app('TextEdit').to_s)
28
+ assert_equal('app("/Applications/TextEdit.app")', Appscript.app.by_name('TextEdit').to_s)
29
+
30
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app('/non-existent/app') }
31
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app('non-existent.app') }
32
+ end
33
+
34
+ def test_by_id
35
+ [
36
+ 'com.apple.textedit',
37
+ 'com.apple.finder',
38
+ ].each do |name|
39
+ a = Appscript.app.by_id(name)
40
+ assert_not_nil(a)
41
+ assert_instance_of(Appscript::Application, a)
42
+ assert_instance_of(Appscript::Reference, a.name)
43
+ end
44
+ assert_equal('app("/Applications/TextEdit.app")', Appscript.app.by_id('com.apple.textedit').to_s)
45
+
46
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app.by_id('non.existent.app') }
47
+ end
48
+
49
+ def test_by_creator
50
+ a = Appscript.app.by_creator('ttxt')
51
+ assert_instance_of(Appscript::Reference, a.name)
52
+ assert_equal('app("/Applications/TextEdit.app")', a.to_s)
53
+ assert_raises(Appscript::ApplicationNotFoundError) { Appscript.app.by_id('!@$o') }
54
+ end
55
+
56
+ def test_by_pid
57
+ pid = `top -l1 | grep Finder | awk '{ print $1 }'`.to_i # note: this line will return a bad result if more than one user is logged in
58
+ a = Appscript.app.by_pid(pid)
59
+ assert_instance_of(Appscript::Reference, a.name)
60
+ assert_equal("app.by_pid(#{pid})", a.to_s)
61
+ assert_equal('Finder', a.name.get)
62
+ end
63
+
64
+ def test_by_aem_app
65
+ a = Appscript.app.by_aem_app(AEM::Application.by_path('/Applications/TextEdit.app'))
66
+ assert_instance_of(Appscript::Reference, a.name)
67
+ assert_equal('app.by_aem_app(AEM::Application.by_path("/Applications/TextEdit.app"))', a.to_s)
68
+ end
69
+ end
70
+
71
+
72
+ class TC_AppscriptCommands < Test::Unit::TestCase
73
+
74
+ def setup
75
+ @te = Appscript.app('TextEdit')
76
+ @f = Appscript.app('Finder')
77
+ end
78
+
79
+ def test_commands_1
80
+ assert_equal('TextEdit', @te.name.get)
81
+ d = @te.make(:new=>:document, :with_properties=>{:text=>'test test_commands'})
82
+ assert_instance_of(Appscript::Reference, d)
83
+ d.text.end.make(:new=>:word, :with_data=>' test2')
84
+ assert_equal('test test_commands test2', d.text.get)
85
+ assert_instance_of(String,
86
+ d.text.get(:ignore=>[:diacriticals, :punctuation, :whitespace, :expansion], :timeout=>10))
87
+ assert_nil(d.get(:wait_reply=>false))
88
+
89
+
90
+ # test Ruby 1.9+ String Encoding support
91
+ version, sub_version = RUBY_VERSION.split('.').collect {|n| n.to_i} [0, 2]
92
+ if version >= 1 and sub_version >= 9
93
+
94
+ print "(check Encoding support)"
95
+ s = "\302\251 M. Lef\303\250vre"
96
+ s.force_encoding('utf-8')
97
+ d.text.set(s)
98
+ assert_equal(s, d.text.get)
99
+
100
+ @te.AS_app_data.use_ascii_8bit
101
+ end
102
+
103
+ d.text.set("\302\251 M. Lef\303\250vre")
104
+ assert_equal("\302\251 M. Lef\303\250vre", d.text.get)
105
+
106
+ d.close(:saving=>:no)
107
+ end
108
+
109
+ def test_commands_2
110
+ d = @te.make(:new=>:document, :at=>@te.documents.end)
111
+
112
+ @te.set(d.text, :to=> 'test1')
113
+ assert_equal('test1', d.text.get)
114
+
115
+ @te.set(d.text, :to=> 'test2')
116
+ @te.make(:new=>:word, :at=>Appscript.app.documents[1].paragraphs.end, :with_data=>' test3')
117
+ assert_equal('test2 test3', d.text.get)
118
+
119
+ d.close(:saving=>:no)
120
+
121
+ assert_raises(Appscript::CommandError) { @te.documents[10000].get }
122
+
123
+ assert_instance_of(Fixnum, @te.documents.count)
124
+ assert_equal(@te.documents.count, @te.count(:each=>:document))
125
+ end
126
+
127
+ def test_commands_3
128
+ assert_equal('Finder', @f.name.get)
129
+ val = @f.home.folders['Desktop'].get(:result_type=>:alias)
130
+ assert_instance_of(MacTypes::Alias, val)
131
+ assert_equal(val, @f.desktop.get(:result_type=>:alias))
132
+ assert_instance_of(Array, @f.disks.get)
133
+
134
+ r = @f.home.get
135
+ f = r.get(:result_type=>:file_ref)
136
+ assert_equal(r, @f.items[f].get)
137
+
138
+ assert_equal(@f.home.items.get, @f.home.items.get)
139
+ assert_not_equal(@f.disks['non-existent'], @f.disks[1].get)
140
+ end
141
+
142
+ def test_command_error
143
+ begin
144
+ @f.items[10000].get
145
+ rescue Appscript::CommandError => e
146
+ assert_equal(-1728, e.to_i)
147
+ assert_equal("CommandError\n\t\tOSERROR: -1728\n\t\tMESSAGE: Can't get reference.\n\t\tOFFENDING OBJECT: app(\"/System/Library/CoreServices/Finder.app\").items[10000]\n\t\tCOMMAND: app(\"/System/Library/CoreServices/Finder.app\").items[10000].get()\n", e.to_s)
148
+ assert_instance_of(AEM::EventError, e.real_error)
149
+ end
150
+ end
151
+ end
152
+
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ begin; require 'rubygems'; rescue LoadError; end
4
+
5
+ require 'test/unit'
6
+ require "appscript"
7
+
8
+ class TC_AppscriptReferences < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @te = Appscript.app('TextEdit')
12
+ @s = @te.to_s
13
+ end
14
+
15
+ def test_reference_forms
16
+ [
17
+
18
+ [@te.documents[
19
+ Appscript.con.documents[3],
20
+ Appscript.con.documents['foo']],
21
+ @s+'.documents[' +
22
+ 'con.documents[3], ' +
23
+ 'con.documents["foo"]]', nil],
24
+
25
+
26
+
27
+ [@te.text, @s+'.text', nil],
28
+
29
+ [@te.documents, @s+'.documents', nil],
30
+
31
+ [@te.documents[1],
32
+ @s+'.documents[1]', nil],
33
+ [@te.documents['foo'],
34
+ @s+'.documents["foo"]', nil],
35
+ [@te.documents.ID(300),
36
+ @s+'.documents.ID(300)', nil],
37
+
38
+ [@te.documents.next(:document),
39
+ @s+'.documents.next(:document)', nil],
40
+ [@te.documents.previous(:document),
41
+ @s+'.documents.previous(:document)', nil],
42
+
43
+ [@te.documents.first, @s+'.documents.first', nil],
44
+ [@te.documents.middle, @s+'.documents.middle', nil],
45
+ [@te.documents.last, @s+'.documents.last', nil],
46
+ [@te.documents.any, @s+'.documents.any', nil],
47
+
48
+ [Appscript.con.documents[3], 'con.documents[3]', nil],
49
+
50
+
51
+ [Appscript.its.name.eq('foo').and(Appscript.its.words.eq([])),
52
+ 'its.name.eq("foo").and(its.words.eq([]))', nil],
53
+
54
+ [Appscript.its.words.ne([]),
55
+ 'its.words.ne([])',
56
+ Appscript.its.words.eq([]).not], # i.e. there isn't a KAENotEqual operator, so not-equal tests are actually packed as an equal test followed by not test
57
+
58
+ [Appscript.its.words.eq(nil), 'its.words.eq(nil)', nil],
59
+ [Appscript.its.words.size.gt(0),
60
+ 'its.words.size.gt(0)', nil],
61
+ [Appscript.its.words.le(''), 'its.words.le("")', nil],
62
+ [Appscript.its.words.begins_with('foo').not,
63
+ 'its.words.begins_with("foo").not', nil],
64
+
65
+
66
+ [Appscript.its.words.contains('foo'), 'its.words.contains("foo")', nil],
67
+ [Appscript.its.words.is_in('foo'), 'its.words.is_in("foo")', nil],
68
+
69
+ [@te.documents[Appscript.its.size.ge(42)],
70
+ @s+'.documents[its.size.ge(42)]', nil],
71
+
72
+ [@te.documents[1, 'foo'],
73
+ @s+'.documents[1, "foo"]',
74
+ @te.documents[Appscript.con.documents[1], Appscript.con.documents["foo"]]],
75
+
76
+ [@te.documents[1].text \
77
+ .paragraphs.characters[
78
+ Appscript.con.characters[3],
79
+ Appscript.con.characters[55]
80
+ ].next(:character).after,
81
+ @s+'.documents[1].text.paragraphs.characters' +
82
+ '[con.characters[3], con.characters[55]]' +
83
+ '.next(:character).after', nil],
84
+
85
+ [Appscript.its.name.ne('foo').and(Appscript.its.words.eq([])).not,
86
+ 'its.name.ne("foo").and(its.words.eq([])).not',
87
+ Appscript.its.name.eq('foo').not.and(Appscript.its.words.eq([])).not],
88
+
89
+ [@te.documents.beginning, @s+'.documents.beginning', nil],
90
+ [@te.documents.end, @s+'.documents.end', nil],
91
+ [@te.documents[3].before, @s+'.documents[3].before', nil],
92
+ [@te.documents['foo'].after, @s+'.documents["foo"].after', nil],
93
+
94
+ ].each do |val, res, unpacked_version|
95
+ assert_equal(res, val.to_s)
96
+ d = @te.AS_app_data.pack(val)
97
+ val = unpacked_version ? unpacked_version : val
98
+ val2 = @te.AS_app_data.unpack(d)
99
+ if val.class == @te.AS_app_data.unpack(d).class # note: Appscript::Reference and Appscript::GenericReference currently aren't comparable with each other, so the next test would always fail for those
100
+ assert_equal(val, val2)
101
+ assert_block { val.eql?(val2) }
102
+ end
103
+ end
104
+ end
105
+ end
106
+
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ begin; require 'rubygems'; rescue LoadError; end
4
+
5
+ require 'test/unit'
6
+ require 'aem'
7
+ require 'kae'
8
+ require 'ae'
9
+
10
+ def num(s)
11
+ if [1].pack('s') == "\001\000" # host system is i386
12
+ s.reverse
13
+ else
14
+ s
15
+ end
16
+ end
17
+
18
+ def ut16(s)
19
+ if [1].pack('s') == "\001\000" # host system is i386
20
+ i = 0
21
+ s2 = ''
22
+ (s.length / 2).times do
23
+ s2 += s[i, 2].reverse
24
+ i+=2
25
+ end
26
+ s2
27
+ else
28
+ s
29
+ end
30
+ end
31
+
32
+
33
+
34
+
35
+ class TC_Codecs < Test::Unit::TestCase
36
+
37
+ def setup
38
+ @c = AEM::Codecs.new
39
+ end
40
+
41
+ def test_nil
42
+ d = @c.pack(nil)
43
+ assert_equal(KAE::TypeNull, d.type)
44
+ assert_equal('', d.data)
45
+ assert_nil(@c.unpack(d))
46
+ end
47
+
48
+ def test_bool
49
+ [
50
+ [true, KAE::TypeTrue],
51
+ [false, KAE::TypeFalse]
52
+ ].each do |val, type|
53
+ d = @c.pack(val)
54
+ assert_equal(type, d.type)
55
+ assert_equal('', d.data)
56
+ assert_equal(val, @c.unpack(d))
57
+ end
58
+ assert_equal(true, @c.unpack(AE::AEDesc.new(KAE::TypeBoolean, "\xfe")))
59
+ assert_equal(true, @c.unpack(AE::AEDesc.new(KAE::TypeTrue, '')))
60
+ assert_equal(false, @c.unpack(AE::AEDesc.new(KAE::TypeFalse, '')))
61
+ end
62
+
63
+ def test_num
64
+ [ # (mostly testing at threshold points where Codecs switches types when packing integers)
65
+ [0, "\x00\x00\x00\x00", KAE::TypeInteger],
66
+ [2, "\x00\x00\x00\x02", KAE::TypeInteger],
67
+ [-9, "\xff\xff\xff\xf7", KAE::TypeInteger],
68
+ [2**31-1, "\x7f\xff\xff\xff", KAE::TypeInteger],
69
+ [-2**31, "\x80\x00\x00\x00", KAE::TypeInteger],
70
+ [2**31, "\x00\x00\x00\x00\x80\x00\x00\x00", KAE::TypeSInt64],
71
+ [2**32-1, "\x00\x00\x00\x00\xff\xff\xff\xff", KAE::TypeSInt64],
72
+ [2**32, "\x00\x00\x00\x01\x00\x00\x00\x00", KAE::TypeSInt64],
73
+ [-2**32, "\xff\xff\xff\xff\x00\x00\x00\x00", KAE::TypeSInt64],
74
+ [2**63-1, "\x7f\xff\xff\xff\xff\xff\xff\xff", KAE::TypeSInt64],
75
+ [-2**63, "\x80\x00\x00\x00\x00\x00\x00\x00", KAE::TypeSInt64],
76
+ [-2**63+1, "\x80\x00\x00\x00\x00\x00\x00\x01", KAE::TypeSInt64],
77
+ [2**63, "C\xe0\x00\x00\x00\x00\x00\x00", KAE::TypeFloat],
78
+ [-2**63-1, "\xc3\xe0\x00\x00\x00\x00\x00\x00", KAE::TypeFloat],
79
+ [0.1, "?\xb9\x99\x99\x99\x99\x99\x9a", KAE::TypeFloat],
80
+ [-0.9e-9, "\xbe\x0e\xec{\xd5\x12\xb5r", KAE::TypeFloat],
81
+ [2**300, "R\xb0\x00\x00\x00\x00\x00\x00", KAE::TypeFloat],
82
+ ].each do |val, data, type|
83
+ data = num(data)
84
+ d = @c.pack(val)
85
+ assert_equal(type, d.type)
86
+ assert_equal(data, d.data)
87
+ assert_equal(val, @c.unpack(d))
88
+ end
89
+ end
90
+
91
+ def test_str
92
+ s = "\xc6\x92\xe2\x88\x82\xc2\xae\xd4\xb7\xd5\x96\xd4\xb9\xe0\xa8\x89\xe3\x82\xa2\xe3\x84\xbb"
93
+ # test Ruby 1.9+ String Encoding support
94
+ version, sub_version = RUBY_VERSION.split('.').collect {|n| n.to_i} [0, 2]
95
+ if version >= 1 and sub_version >= 9
96
+ s.force_encoding('utf-8')
97
+ end
98
+
99
+ [
100
+ # note: aem has to pack UTF8 data as typeUnicodeText (UTF16) as stupid apps expect that type and will error on typeUTF8Text instead of just asking AEM to coerce it to the desired type in advance.
101
+ # note: UTF16 BOM must be omitted when packing UTF16 data into typeUnicodeText AEDescs, as a BOM will upset stupid apps like iTunes 7 that don't recognise it as a BOM and treat it as character data instead
102
+ ['', ''],
103
+ ['hello', "\000h\000e\000l\000l\000o"],
104
+ [s, "\x01\x92\"\x02\x00\xae\x057\x05V\x059\n\t0\xa21;"],
105
+ ].each do |val, data|
106
+ data = ut16(data)
107
+ d = @c.pack(val)
108
+ assert_equal(KAE::TypeUnicodeText, d.type)
109
+ assert_equal(data, d.data)
110
+ assert_equal(val, @c.unpack(d))
111
+ end
112
+ assert_raises(TypeError) { @c.pack("\x88") } # non-valid UTF8 strings should raise error when coercing from typeUTF8Text to typeUnicodeText
113
+ end
114
+
115
+ def test_date
116
+ # note: not testing on ST-DST boundaries; this is known to have out-by-an-hour problems due to LongDateTime type being crap
117
+ [
118
+ [Time.local(2005, 12, 11, 15, 40, 43), "\x00\x00\x00\x00\xbf\xc1\xf8\xfb"],
119
+ [Time.local(2005, 5, 1, 6, 51, 7), "\x00\x00\x00\x00\xbe\x9a\x2c\xdb"],
120
+ ].each do |t, data|
121
+ data = num(data)
122
+ d = @c.pack(t)
123
+ assert_equal(KAE::TypeLongDateTime, d.type)
124
+ assert_equal(data, d.data)
125
+ assert_equal(t, @c.unpack(AE::AEDesc.new(KAE::TypeLongDateTime, data)))
126
+ end
127
+ end
128
+
129
+ def test_file
130
+ path = '/Applications/TextEdit.app'
131
+ d = @c.pack(MacTypes::Alias.path(path))
132
+ assert_equal(path, @c.unpack(d).to_s)
133
+
134
+ path = '/Applications/TextEdit.app'
135
+ d = @c.pack(MacTypes::FileURL.path(path))
136
+ assert_equal(path, @c.unpack(d).to_s)
137
+ end
138
+
139
+ def test_typewrappers
140
+ [
141
+ AEM::AEType.new("docu"),
142
+ AEM::AEEnum.new('yes '),
143
+ AEM::AEProp.new('pnam'),
144
+ AEM::AEKey.new('ABCD'),
145
+ ].each do |val|
146
+ d = @c.pack(val)
147
+ val2 = @c.unpack(d)
148
+ assert_equal(val, val2)
149
+ assert_block { val.eql?(val2) }
150
+ val2 = @c.unpack(d)
151
+ assert_equal(val2, val)
152
+ assert_block { val2.eql?(val) }
153
+ end
154
+ assert_raises(ArgumentError) { AEM::AEType.new(3) }
155
+ assert_raises(ArgumentError) { AEM::AEType.new("docum") }
156
+ end
157
+
158
+ def test_list
159
+ end
160
+
161
+ def test_hash
162
+ val = {'foo' => 1, AEM::AEType.new('foob') => 2, AEM::AEProp.new('barr') => 3}
163
+ expected_val = {'foo' => 1, AEM::AEType.new('foob') => 2, AEM::AEType.new('barr') => 3} # note that four-char-code keys are always unpacked as AEType
164
+ d = @c.pack(val)
165
+ assert_equal(expected_val, @c.unpack(d))
166
+ end
167
+
168
+ def test_units
169
+ val = MacTypes::Units.new(3.3, :inches)
170
+ assert_equal(:inches, val.type)
171
+ assert_equal(3.3, val.value)
172
+ d = @c.pack(val)
173
+ assert_equal('inch', d.type)
174
+ assert_equal(3.3, @c.unpack(d.coerce(KAE::TypeFloat)))
175
+ val2 = @c.unpack(d)
176
+ assert_equal(val, val2)
177
+ assert_equal(:inches, val2.type)
178
+ assert_equal(3.3, val2.value)
179
+ end
180
+
181
+ def test_app
182
+ val = AEM::Application.by_path(FindApp.by_name('TextEdit'))
183
+ d = @c.pack(val)
184
+ assert_equal(KAE::TypeProcessSerialNumber, d.type)
185
+ end
186
+ end