melomel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Ben Johnson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ melomel.rb -- A Ruby interface to Melomel
2
+ =========================================
3
+
4
+ ## DESCRIPTION
5
+
6
+ Melomel.rb is a library that allows Ruby to communicate with an application
7
+ running within the Flash virtual machine. For more information on Melomel,
8
+ visit the Melomel repository:
9
+
10
+ http://github.com/benbjohnson/melomel
11
+
12
+ Melomel.rb follows the rules of [Semantic Versioning](http://semver.org/) and
13
+ uses [TomDoc](http://tomdoc.org/) for inline documentation.
14
+
15
+
16
+ ## INSTALLATION
17
+
18
+ To install Melomel.rb simply use RubyGems:
19
+
20
+ $ [sudo] gem install melomel
21
+
22
+ Melomel needs to be embedded and running in the application you're trying to
23
+ connect to. Please see the Melomel repository for instructions on installation.
24
+
25
+
26
+ ## GETTING STARTED
27
+
28
+ The first step to using Melomel is to install the Flash SWC in your application.
29
+ Once the SWC is in your application, setup in your Ruby project is simple.
30
+
31
+ In your Ruby file, simply call the `connect()` method on `Melomel`:
32
+
33
+ require 'melomel'
34
+ Melomel.connect()
35
+
36
+ The `connect()` method is a blocking method so it won't proceed until it's done.
37
+ After it's connected, there are several actions you can perform:
38
+
39
+ 1. Create Object
40
+ 1. Get Class
41
+ 1. Get Property
42
+ 1. Set Property
43
+ 1. Invoke Method
44
+
45
+ ## API
46
+
47
+ ### Overview
48
+
49
+ Melomel communicates to the Flash virtual machine over a socket connection
50
+ using XML. The protocol is simple and is meant to proxy all data access calls
51
+ to the Flash virtual machine. This means that only primitives (strings, numbers
52
+ and booleans) are copied but all objects are accessed by reference. By proxying
53
+ objects, all data stays in the Flash virtual machine and there are no syncing
54
+ issues.
55
+
56
+ ### Create Object
57
+
58
+ To create an object, use the `create_object()` method on `Melomel`:
59
+
60
+ point = Melomel.create_object('flash.geom.Point')
61
+
62
+ The object returned is a proxy object so any actions performed on it in Ruby
63
+ will be performed on the ActionScript object in the Flash virtual machine.
64
+
65
+ ### Get Class
66
+
67
+ You can retrieve a class to call static methods and properties. Since classes
68
+ are objects in ActionScript, they work identically in Melomel.
69
+
70
+ app = Melomel.get_class('mx.core.FlexGlobals')
71
+ app.topLevelApplication.name = 'Melomel App!'
72
+
73
+ This Flex 4 example updates the name of the application to "Melomel App!".
74
+
75
+ ### Get Property & Set Property
76
+
77
+ Getting and setting properties is handled transparently when using the object
78
+ proxies.
79
+
80
+ point = Melomel.create_object('flash.geom.Point')
81
+ point.x = 30
82
+ point.set_property('y') = 40
83
+ p "pos: #{point.x}, #{point.y}"
84
+ p "length: #{point.get_property('length')}"
85
+
86
+ Property accessors on an object proxy are automatically used to retrieve the
87
+ property value of the Flash object. You can also use the `get_property()`
88
+ method when accessing properties and the `set_property()` method when mutating
89
+ properties.
90
+
91
+ ### Invoke Method
92
+
93
+ Invoking methods is also handled transparently when using object proxies.
94
+
95
+ _IMPORTANT: There a catch! Methods without any parameters must have a bang
96
+ (`!`) character appended to their method name when calling directly on an
97
+ object proxy._
98
+
99
+ clipboard = Melomel.get_class('flash.desktop.Clipboard').generalClipboard
100
+ data = clipboard.getData('air:text')
101
+ data = clipboard.invoke_method('getData', 'air:text')
102
+ clipboard.clear!()
103
+ clipboard.invoke_method('clear')
104
+
105
+ In this example, the `getData` method could be called on the object directly
106
+ because it had more than one argument. The `clear` method, however, required
107
+ that a bang character be appended to the name or that the `invoke_method` be
108
+ used.
109
+
110
+
111
+ ## HACKING
112
+
113
+ To get started with working with the source, start by running Bundler to get all
114
+ your dependencies:
115
+
116
+ [sudo] bundle install
117
+
118
+ If you are adding new features, please add test coverage to all code that you
119
+ write. Run the test suite with `rake`:
120
+
121
+ rake test
122
+
123
+
124
+ ## CONTRIBUTE
125
+
126
+ If you'd like to contribute to Melomel.rb, please fork the repo on GitHub:
127
+
128
+ http://github.com/benbjohnson/melomel.rb
129
+
130
+ To get all of the dependencies, install the gem first. The best way to get
131
+ your changes merged back into core is as follows:
132
+
133
+ 1. Clone your fork locally
134
+ 1. Create a named topic branch to contain your change
135
+ 1. Code
136
+ 1. All code must have RSpec test coverage. Run `rake` to ensure everything
137
+ passes.
138
+ 1. If you are adding new functionality, document it in the README
139
+ 1. If necessary, rebase your commits into logical chunks, without errors
140
+ 1. Push the branch up to GitHub
141
+ 1. Send me a pull request for your branch
@@ -0,0 +1,112 @@
1
+ require 'nokogiri'
2
+
3
+ # These add-on methods enable the message encoding and decoding.
4
+ module Melomel
5
+ class Bridge
6
+ # Creates an object in the Flash virtual machine and returns the reference
7
+ # to it.
8
+ def create_object(class_name)
9
+ send("<create class=\"#{class_name}\"/>")
10
+ parse_message_value(Nokogiri::XML(receive()).root)
11
+ end
12
+
13
+ # Retrieves a reference to a class in the Flash virtual machine.
14
+ def get_class(class_name)
15
+ send("<get-class name=\"#{class_name}\"/>")
16
+ parse_message_value(Nokogiri::XML(receive()).root)
17
+ end
18
+
19
+ # Retrieves a property of an object in the Flash virtual machine
20
+ def get_property(proxy_id, property)
21
+ send("<get object=\"#{proxy_id}\" property=\"#{property}\"/>")
22
+ parse_message_value(Nokogiri::XML(receive()).root)
23
+ end
24
+
25
+ # Sets a property on an object in the Flash virtual machine
26
+ def set_property(proxy_id, property, value)
27
+ # Create message and format value to set
28
+ xml = Nokogiri::XML("<set object=\"#{proxy_id}\" property=\"#{property}\"><arg/></set>")
29
+ format_message_value(xml.at_xpath('/set/arg'), value)
30
+
31
+ # Send & Receive
32
+ send(xml.root.to_xml(:indent => 0))
33
+ parse_message_value(Nokogiri::XML(receive()).root)
34
+ end
35
+
36
+ # Invokes a method on an object in the Flash virtual machine
37
+ def invoke_method(proxy_id, method_name, *args)
38
+ xml = Nokogiri::XML("<invoke object=\"#{proxy_id}\" method=\"#{method_name}\"><args/></invoke>")
39
+
40
+ # Loop over and add arguments to call
41
+ args_node = xml.at_xpath('invoke/args')
42
+ args.each do |arg|
43
+ arg_node = Nokogiri::XML::Node.new('arg', xml)
44
+ format_message_value(arg_node, arg)
45
+ args_node.add_child(arg_node)
46
+ end
47
+
48
+ # Send and receive
49
+ send(xml.root.to_xml(:indent => 0))
50
+ parse_message_value(Nokogiri::XML(receive()).root)
51
+ end
52
+
53
+ # Creates an object proxy from a hash
54
+ def create_hash(hash)
55
+ proxy = create_object('Object')
56
+ hash.each_pair do |k,v|
57
+ v = create_hash(v) if !v.nil? && v.is_a?(Hash)
58
+ proxy.set_property(k, v)
59
+ end
60
+ return proxy
61
+ end
62
+
63
+ # Formats a Ruby value into an XML message
64
+ def format_message_value(xml, value)
65
+ # Automatically convert simple hashes to objects
66
+ if(!value.nil? && value.is_a?(Hash))
67
+ value = create_hash(value)
68
+ end
69
+
70
+ if value.nil?
71
+ xml['dataType'] = 'null'
72
+ elsif value.class == Fixnum || value.class == Bignum
73
+ xml['value'] = value.to_s
74
+ xml['dataType'] = 'int'
75
+ elsif value.class == Float
76
+ xml['value'] = value.to_s
77
+ xml['dataType'] = 'float'
78
+ elsif value == true || value == false
79
+ xml['value'] = value.to_s
80
+ xml['dataType'] = 'boolean'
81
+ elsif value.class == Melomel::ObjectProxy
82
+ xml['value'] = value.proxy_id.to_s
83
+ xml['dataType'] = 'object'
84
+ elsif value.class == String
85
+ xml['value'] = value
86
+ xml['dataType'] = 'string'
87
+ end
88
+ end
89
+
90
+ # Parses a return message and converts it into an appropriate type
91
+ def parse_message_value(xml)
92
+ value = xml['value']
93
+ data_type = xml['dataType']
94
+
95
+ if data_type == 'null'
96
+ return nil
97
+ elsif data_type == 'int'
98
+ return value.to_i
99
+ elsif data_type == 'float'
100
+ return value.to_f
101
+ elsif data_type == 'boolean'
102
+ return value == 'true'
103
+ elsif data_type == 'object'
104
+ return Melomel::ObjectProxy.new(self, value.to_i)
105
+ elsif data_type == 'string' || data_type.nil?
106
+ return value
107
+ else
108
+ raise UnrecognizedTypeError, "Unknown type: #{data_type}"
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,55 @@
1
+ # These add-on methods provide ease of use utility methods.
2
+ module Melomel
3
+ class Bridge
4
+ # Finds a list of display objects matching a class and hash of properties.
5
+ def find_all(class_name, root={}, properties={})
6
+ # Merge hashes if no root is specified
7
+ if root.is_a?(Hash)
8
+ properties.merge!(root)
9
+ root = nil
10
+ end
11
+
12
+ # Retrieve object
13
+ get_class('melomel.core.UI').findAll(class_name, root, properties)
14
+ end
15
+
16
+ # Finds a display object by class and properties.
17
+ def find(class_name, root={}, properties={})
18
+ # Merge hashes if no root is specified
19
+ if root.is_a?(Hash)
20
+ properties.merge!(root)
21
+ root = nil
22
+ end
23
+
24
+ # Retrieve object
25
+ get_class('melomel.core.UI').find(class_name, root, properties)
26
+ end
27
+
28
+
29
+ # Imitates a click on a component
30
+ def click(component, properties={})
31
+ get_class('melomel.core.UI').click(component, properties)
32
+ end
33
+
34
+ # Imitates a double click on a component
35
+ def double_click(component, properties={})
36
+ get_class('melomel.core.UI').doubleClick(component, properties)
37
+ end
38
+
39
+
40
+ # Imitates a key down on a component
41
+ def key_down(component, char, properties={})
42
+ get_class('melomel.core.UI').keyDown(component, char, properties)
43
+ end
44
+
45
+ # Imitates a key up on a component
46
+ def key_up(component, char, properties={})
47
+ get_class('melomel.core.UI').keyUp(component, char, properties)
48
+ end
49
+
50
+ # Imitates a key press on a component
51
+ def key_press(component, char, properties={})
52
+ get_class('melomel.core.UI').keyPress(component, char, properties)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,76 @@
1
+ require 'socket'
2
+ require 'melomel/bridge/messaging'
3
+ require 'melomel/bridge/ui'
4
+
5
+ # The bridge manages the connection to the Flash virtual machine. All messages
6
+ # sent to the virtual machine are passed through this object.
7
+ module Melomel
8
+ class Bridge
9
+ attr_accessor :host, :port
10
+
11
+ def initialize(host='localhost', port=10101)
12
+ self.host = host
13
+ self.port = port
14
+ server, @socket = nil, nil
15
+ end
16
+
17
+ # Opens a socket connection to listen for incoming bridge connections. This
18
+ # method automatically handles requests for the policy file.
19
+ def connect()
20
+ disconnect()
21
+
22
+ # Listen for connections
23
+ server = TCPServer.open(host, port)
24
+
25
+ # Retrieve socket and check for initial handshake
26
+ while(@socket.nil?) do
27
+ socket = server.accept()
28
+ data = socket.gets("\x00").chomp("\x00")
29
+
30
+ # Send policy file if requested.
31
+ if(data == '<policy-file-request/>')
32
+ send_policy_file(socket)
33
+ # Otherwise open connection and continue
34
+ elsif(data == '<connect/>')
35
+ @socket = socket
36
+ end
37
+ end
38
+
39
+ server.close();
40
+ end
41
+
42
+ # Closes any open connection to a Flash virtual machine.
43
+ def disconnect()
44
+ begin
45
+ @socket.close()
46
+ rescue
47
+ end
48
+
49
+ @socket = nil
50
+ end
51
+
52
+ # Sends a message over to the Flash bridge.
53
+ def send(message)
54
+ @socket.puts("#{message}\x00")
55
+ end
56
+
57
+ # Receives a message from the Flash bridge. This is a blocking call.
58
+ def receive()
59
+ @socket.gets("\x00").chomp("\x00")
60
+ end
61
+
62
+
63
+ private
64
+ def send_policy_file(socket)
65
+ policy = ''
66
+ policy << '<?xml version="1.0"?>'
67
+ policy << '<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">'
68
+ policy << '<cross-domain-policy>'
69
+ policy << "<allow-access-from domain=\"#{host}\" to-ports=\"#{port}\"/>"
70
+ policy << '</cross-domain-policy>'
71
+ socket.send(policy)
72
+ socket.flush()
73
+ socket.close()
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,58 @@
1
+ # This class as a proxy to an object in the Flash virtual machine. Invoking
2
+ # methods, accessing properties or changing properties on this object will
3
+ # result in a command being sent to Flash to change or access the state of the
4
+ # object within the virtual machine. The Ruby object proxy holds no state.
5
+ #
6
+ # The object proxy works exactly as if the Flash object was a local Ruby object.
7
+ # To do this, the following aliases are made:
8
+ # * Method calls with method names ending in "=" are aliased to #set_property
9
+ # * Method calls without arguments are aliased to #get_property.
10
+ # * Method calls with arguments are aliased to #invoke_method
11
+ module Melomel
12
+ class ObjectProxy
13
+ attr_reader :bridge, :proxy_id
14
+
15
+ def initialize(bridge, proxy_id)
16
+ @bridge = bridge
17
+ @proxy_id = proxy_id
18
+ end
19
+
20
+ # Retrieves the value of a property for the proxied object.
21
+ def get_property(name)
22
+ @bridge.get_property(@proxy_id, name)
23
+ end
24
+
25
+ # Sets the value of a property for the proxied object.
26
+ def set_property(name, value)
27
+ @bridge.set_property(@proxy_id, name, value)
28
+ end
29
+
30
+ # Invokes a method on the proxied object. Arguments passed into the method
31
+ # are passed through to the invoked method
32
+ def invoke_method(method_name, *args)
33
+ @bridge.invoke_method(@proxy_id, method_name, *args)
34
+ end
35
+
36
+ alias :invoke :invoke_method
37
+
38
+ # Proxies all methods to the appropriate Flash objects.
39
+ def method_missing(symbol, *args)
40
+ method_name = symbol.to_s
41
+ last_char = method_name.to_s[-1,1]
42
+
43
+ # Methods ending in "=" are aliased to set_property
44
+ if last_char == '='
45
+ return set_property(method_name.chop, *args)
46
+ # Methods with arguments are methods
47
+ elsif args.length > 0
48
+ return invoke_method(method_name, *args)
49
+ # Methods ending in '!' are methods
50
+ elsif last_char == '!'
51
+ return invoke_method(method_name.chop, *args)
52
+ # Methods with no arguments are aliased to get_property
53
+ else
54
+ return get_property(method_name)
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/melomel/ui.rb ADDED
@@ -0,0 +1,44 @@
1
+ # This class provides ease of use utility methods for finding display objects
2
+ # and interacting with them.
3
+ module Melomel
4
+ class UI
5
+ class << self
6
+ # Finds all display objects matching a class and hash of properties.
7
+ def find_all(class_name, root={}, properties={})
8
+ Melomel.bridge.find_all(class_name, root, properties)
9
+ end
10
+
11
+ # Finds a display object by class and properties.
12
+ def find(class_name, root={}, properties={})
13
+ Melomel.bridge.find(class_name, root, properties)
14
+ end
15
+
16
+
17
+ # Imitates a click on a component
18
+ def click(component, properties={})
19
+ Melomel.bridge.click(component, properties)
20
+ end
21
+
22
+ # Imitates a double click on a component
23
+ def double_click(component, properties={})
24
+ Melomel.bridge.double_click(component, properties)
25
+ end
26
+
27
+
28
+ # Imitates a key down on a component
29
+ def key_down(component, char, properties={})
30
+ Melomel.bridge.key_down(component, char, properties)
31
+ end
32
+
33
+ # Imitates a key up on a component
34
+ def key_up(component, char, properties={})
35
+ Melomel.bridge.key_up(component, char, properties)
36
+ end
37
+
38
+ # Imitates a key press on a component
39
+ def key_press(component, char, properties={})
40
+ Melomel.bridge.key_press(component, char, properties)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Melomel
2
+ VERSION = "0.1.0"
3
+ end
data/lib/melomel.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'melomel/bridge'
2
+ require 'melomel/object_proxy'
3
+ require 'melomel/ui'
4
+ require 'melomel/version'
5
+
6
+ # This class acts as a singleton instance of the bridge. This is typically the
7
+ # only class you'll need to use. If multiple instances of the bridge are needed
8
+ # (to connect to multiple SWF files), you will need to instantiate and manage
9
+ # the bridge objects manually.
10
+ module Melomel
11
+ class MelomelError < StandardError; end
12
+ class UnrecognizedTypeError < MelomelError; end
13
+
14
+ class << self
15
+ attr_reader :bridge
16
+
17
+ # Opens a bridge connection to the SWF file. This is a blocking call and
18
+ # will wait until a SWF connects before continuing.
19
+ def connect(host='localhost', port=10101)
20
+ @bridge = Melomel::Bridge.new(host, port)
21
+ @bridge.connect();
22
+ end
23
+
24
+ # Retrieves a reference to a class
25
+ def get_class(class_name)
26
+ @bridge.get_class(class_name)
27
+ end
28
+
29
+ # Creates an object in the virtual machine.
30
+ def create_object(class_name)
31
+ @bridge.create_object(class_name)
32
+ end
33
+ end
34
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require "bundler"
3
+ Bundler.setup
4
+ require 'minitest/autorun'
5
+ require 'mocha'
6
+
7
+ dir = File.dirname(File.expand_path(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
9
+ $LOAD_PATH.unshift(dir)
10
+
11
+ require 'melomel'
12
+
13
+ class RunnerTestCase < MiniTest::Unit::TestCase
14
+ def start_runner
15
+ # Make sure FLEX_HOME is defined
16
+ raise 'FLEX_HOME environment variable must be set' if ENV['FLEX_HOME'].nil?
17
+
18
+ # Open up the sandbox
19
+ @pid = fork do
20
+ exec("#{ENV['FLEX_HOME']}/bin/adl target/MelomelRunner-app.xml")
21
+ end
22
+ Process.detach(@pid)
23
+ end
24
+
25
+ def stop_runner
26
+ Process.kill('KILL', @pid)
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), *%w[helper])
2
+
3
+ class BridgeTestCase < MiniTest::Unit::TestCase
4
+ def setup
5
+ @bridge = Melomel::Bridge.new('localhost', 10101)
6
+ end
7
+
8
+ def connect
9
+ # Mock server
10
+ @server = mock('server')
11
+ TCPServer.stubs(:open).returns(@server)
12
+ TCPServer.any_instance.stubs(:close)
13
+
14
+ # Mock sockets
15
+ @socket = mock('socket')
16
+ @socket.expects(:gets).returns("<connect/>\x00")
17
+ @server.expects(:accept).returns(@socket)
18
+ @server.expects(:close)
19
+
20
+ # Attempt connection
21
+ @bridge.connect()
22
+ end
23
+
24
+ def test_should_send_messages_over_socket_connection
25
+ connect()
26
+ @socket.expects(:puts).with("<message/>\x00")
27
+ @bridge.send('<message/>')
28
+ end
29
+
30
+ def test_should_receive_messages_from_socket_connection
31
+ connect()
32
+ @socket.expects(:gets).returns("<message/>\x00")
33
+ assert_equal '<message/>', @bridge.receive()
34
+ end
35
+
36
+ def test_should_send_policy_file_and_connect
37
+ policy = '<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy><allow-access-from domain="localhost" to-ports="10101"/></cross-domain-policy>';
38
+
39
+ # Mock server
40
+ server = mock('server')
41
+ server.expects(:close)
42
+ TCPServer.expects(:open).returns(server)
43
+
44
+ # Mock policy file
45
+ policy_socket = mock('policy_socket')
46
+ policy_socket.expects(:gets).returns("<policy-file-request/>\x00")
47
+ policy_socket.expects(:send).with(policy)
48
+ policy_socket.expects(:flush)
49
+ policy_socket.expects(:close)
50
+
51
+ # Mock regular socket
52
+ socket = mock('socket')
53
+ socket.expects(:gets).returns("<connect/>\x00")
54
+
55
+ # Server should return policy socket first and then regular socket
56
+ server.stubs(:accept).returns(policy_socket, socket, nil)
57
+
58
+ # Attempt connection
59
+ @bridge.connect()
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), *%w[helper])
2
+
3
+ class IntegrationTestCase < RunnerTestCase
4
+ def setup
5
+ start_runner
6
+ Melomel.connect()
7
+ end
8
+
9
+ def teardown
10
+ stop_runner
11
+ end
12
+
13
+ def test_should_get_application_name
14
+ app = Melomel.get_class('mx.core.FlexGlobals')
15
+ assert_equal 'Melomel Runner', app.topLevelApplication.name
16
+ end
17
+
18
+ def test_should_get_property
19
+ runner = Melomel.get_class('MelomelRunner')
20
+ assert_equal 'bar', runner.foo
21
+ end
22
+
23
+ def test_should_set_property
24
+ runner = Melomel.get_class('MelomelRunner')
25
+ runner.name = 'Susy'
26
+ assert_equal 'Susy', runner.name
27
+ runner.name = 'John' # TODO: Do not make other tests dependent on this
28
+ end
29
+
30
+ def test_should_invoke_method
31
+ runner = Melomel.get_class('MelomelRunner')
32
+ assert_equal 'Hello, John', runner.hello('John')
33
+ end
34
+
35
+ def test_should_create_object
36
+ point = Melomel.create_object('flash.geom.Point')
37
+ point.x = 30
38
+ point.y = 40
39
+ assert_equal 50, point.length
40
+ end
41
+ end
@@ -0,0 +1,109 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), *%w[helper])
2
+
3
+ class MessagingTestCase < MiniTest::Unit::TestCase
4
+ def setup
5
+ @bridge = Melomel::Bridge.new('localhost', 10101)
6
+ end
7
+
8
+
9
+ #############################################################################
10
+ #
11
+ # Message Parsing
12
+ #
13
+ #############################################################################
14
+
15
+ def test_should_parse_null
16
+ message = '<return dataType="null"/>'
17
+ assert_nil @bridge.parse_message_value(Nokogiri::XML(message).root)
18
+ end
19
+
20
+ def test_should_parse_integer
21
+ message = '<return value="12" dataType="int"/>'
22
+ assert_equal 12, @bridge.parse_message_value(Nokogiri::XML(message).root)
23
+ end
24
+
25
+ def test_should_parse_float
26
+ message = '<return value="100.12" dataType="float"/>'
27
+ assert_equal 100.12, @bridge.parse_message_value(Nokogiri::XML(message).root)
28
+ end
29
+
30
+ def test_should_parse_boolean_true
31
+ message = '<return value="true" dataType="boolean"/>'
32
+ assert_equal true, @bridge.parse_message_value(Nokogiri::XML(message).root)
33
+ end
34
+
35
+ def test_should_parse_boolean_false
36
+ message = '<return value="false" dataType="boolean"/>'
37
+ assert_equal false, @bridge.parse_message_value(Nokogiri::XML(message).root)
38
+ end
39
+
40
+ def test_should_parse_string
41
+ message = '<return value="foo" dataType="string"/>'
42
+ assert_equal 'foo', @bridge.parse_message_value(Nokogiri::XML(message).root)
43
+ end
44
+
45
+ def test_should_parse_object_proxy
46
+ message = '<return value="123" dataType="object"/>'
47
+ proxy = @bridge.parse_message_value(Nokogiri::XML(message).root)
48
+ assert_instance_of Melomel::ObjectProxy, proxy
49
+ assert_equal 123, proxy.proxy_id
50
+ assert_equal @bridge, proxy.bridge
51
+ end
52
+
53
+ def test_should_throw_error_parsing_unknown_type
54
+ message = '<return value="foo" dataType="unknown_type"/>'
55
+ assert_raises Melomel::UnrecognizedTypeError do
56
+ @bridge.parse_message_value(Nokogiri::XML(message).root)
57
+ end
58
+ end
59
+
60
+
61
+ #############################################################################
62
+ #
63
+ # Message Formatting
64
+ #
65
+ #############################################################################
66
+
67
+ def test_should_format_nil
68
+ xml = Nokogiri::XML('<root/>').root
69
+ @bridge.format_message_value(xml, nil)
70
+ assert_equal '<root dataType="null"/>', xml.to_s
71
+ end
72
+
73
+ def test_should_format_integer
74
+ xml = Nokogiri::XML('<root/>').root
75
+ @bridge.format_message_value(xml, 12)
76
+ assert_equal '<root value="12" dataType="int"/>', xml.to_s
77
+ end
78
+
79
+ def should_format_float
80
+ xml = Nokogiri::XML('<root/>').root
81
+ @bridge.format_message_value(xml, 100.12)
82
+ assert_equal '<root value="100.12" dataType="float"/>', xml.to_s
83
+ end
84
+
85
+ def test_should_format_boolean_true
86
+ xml = Nokogiri::XML('<root/>').root
87
+ @bridge.format_message_value(xml, true)
88
+ assert_equal '<root value="true" dataType="boolean"/>', xml.to_s
89
+ end
90
+
91
+ def test_should_format_boolean_false
92
+ xml = Nokogiri::XML('<root/>').root
93
+ @bridge.format_message_value(xml, false)
94
+ assert_equal '<root value="false" dataType="boolean"/>', xml.to_s
95
+ end
96
+
97
+ def test_should_format_string
98
+ xml = Nokogiri::XML('<root/>').root
99
+ @bridge.format_message_value(xml, 'foo')
100
+ assert_equal '<root value="foo" dataType="string"/>', xml.to_s
101
+ end
102
+
103
+ def test_should_format_object
104
+ proxy = Melomel::ObjectProxy.new(@bridge, 123)
105
+ xml = Nokogiri::XML('<root/>').root
106
+ @bridge.format_message_value(xml, proxy)
107
+ assert_equal '<root value="123" dataType="object"/>', xml.to_s
108
+ end
109
+ end
@@ -0,0 +1,48 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), *%w[helper])
2
+
3
+ class ObjectProxyTestCase < MiniTest::Unit::TestCase
4
+ def setup
5
+ @bridge = mock()
6
+ @proxy = Melomel::ObjectProxy.new(@bridge, 123)
7
+ end
8
+
9
+ def test_should_delegate_to_bridge_to_retrieve_property
10
+ @bridge.expects(:get_property).with(123, 'foo')
11
+ @proxy.get_property('foo')
12
+ end
13
+
14
+ def test_should_delegate_to_bridge_to_set_property
15
+ @bridge.expects(:set_property).with(123, 'foo', 'bar')
16
+ @proxy.set_property('foo', 'bar')
17
+ end
18
+
19
+ def test_should_delegate_to_bridge_to_invoke_method
20
+ @bridge.expects(:invoke_method).with(123, 'foo', 'bar', 'baz')
21
+ @proxy.invoke_method('foo', 'bar', 'baz')
22
+ end
23
+
24
+ def test_should_alias_invoke_to_invoke_method
25
+ @bridge.expects(:invoke_method).with(123, 'foo')
26
+ @proxy.invoke('foo')
27
+ end
28
+
29
+ def test_should_alias_accessors_to_get_property_method
30
+ @proxy.expects(:get_property).with('foo')
31
+ @proxy.foo
32
+ end
33
+
34
+ def test_should_alias_mutators_to_set_property_method
35
+ @proxy.expects(:set_property).with('foo', 'bar')
36
+ @proxy.foo = 'bar'
37
+ end
38
+
39
+ def test_should_alias_methods_with_arguments_to_invoke_method
40
+ @proxy.expects(:invoke_method).with('foo', 'bar', 'baz')
41
+ @proxy.foo('bar', 'baz')
42
+ end
43
+
44
+ def test_should_alias_methods_ending_in_bang_to_invoke_method
45
+ @proxy.expects(:invoke_method).with('foo')
46
+ @proxy.foo!()
47
+ end
48
+ end
data/test/test_ui.rb ADDED
@@ -0,0 +1,59 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), *%w[helper])
2
+
3
+ class IntegrationTestCase < RunnerTestCase
4
+ def setup
5
+ start_runner
6
+ Melomel.connect()
7
+ end
8
+
9
+ def teardown
10
+ stop_runner
11
+ end
12
+
13
+ def test_should_find_list_of_labels_named_foo
14
+ labels = Melomel::UI.find_all('spark.components.Label', :name => 'foo')
15
+ assert_equal 3, labels.length
16
+ end
17
+
18
+ def should_find_text_input
19
+ text_input = Melomel::UI.find('spark.components.TextInput', :id => 'nameTextInput')
20
+ assert_equal 'nameTextField', text_input.name
21
+ end
22
+
23
+
24
+ def test_should_click_button
25
+ button = Melomel::UI.find('spark.components.Button', :id => 'clickButton')
26
+ label = Melomel::UI.find('spark.components.Label', :id => 'clickLabel')
27
+ Melomel::UI.click(button)
28
+ assert_equal 'Hello!', label.text
29
+ end
30
+
31
+ def test_should_double_click_the_button
32
+ button = Melomel::UI.find('spark.components.Button', :id => 'doubleClickButton')
33
+ label = Melomel::UI.find('spark.components.Label', :id => 'doubleClickLabel')
34
+ Melomel::UI.double_click(button)
35
+ assert_equal 'Hello Hello!', label.text
36
+ end
37
+
38
+
39
+ def test_should_press_key_down
40
+ text_input = Melomel::UI.find('spark.components.TextInput', :id => 'keyDownTextInput')
41
+ label = Melomel::UI.find('spark.components.Label', :id => 'keyDownLabel')
42
+ Melomel::UI.key_down(text_input, 'a')
43
+ assert_equal 'a', label.text
44
+ end
45
+
46
+ def test_should_release_key_up
47
+ text_input = Melomel::UI.find('spark.components.TextInput', :id => 'keyUpTextInput')
48
+ label = Melomel::UI.find('spark.components.Label', :id => 'keyUpLabel')
49
+ Melomel::UI.key_up(text_input, 'b')
50
+ assert_equal 'b', label.text
51
+ end
52
+
53
+ def test_should_press_key
54
+ text_input = Melomel::UI.find('spark.components.TextInput', :id => 'keyPressTextInput')
55
+ label = Melomel::UI.find('spark.components.Label', :id => 'keyPressLabel')
56
+ Melomel::UI.key_press(text_input, 'a')
57
+ assert_equal 'du', label.text
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: melomel
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ben Johnson
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-26 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: nokogiri
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 3
31
+ version: 1.4.3
32
+ type: :runtime
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: minitest
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 1
44
+ - 7
45
+ - 0
46
+ version: 1.7.0
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: mocha
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ - 9
60
+ - 8
61
+ version: 0.9.8
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: *id003
65
+ description: Melomel allows Rubyist to communicate with Flash and Flex applications easily
66
+ email:
67
+ - benbjohnson@yahoo.com
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ extra_rdoc_files: []
73
+
74
+ files:
75
+ - lib/melomel/bridge/messaging.rb
76
+ - lib/melomel/bridge/ui.rb
77
+ - lib/melomel/bridge.rb
78
+ - lib/melomel/object_proxy.rb
79
+ - lib/melomel/ui.rb
80
+ - lib/melomel/version.rb
81
+ - lib/melomel.rb
82
+ - LICENSE
83
+ - README.md
84
+ - CHANGELOG.md
85
+ - test/helper.rb
86
+ - test/test_bridge.rb
87
+ - test/test_integration.rb
88
+ - test/test_messaging.rb
89
+ - test/test_object_proxy.rb
90
+ - test/test_ui.rb
91
+ has_rdoc: true
92
+ homepage: http://github.com/benbjohnson/melomel.rb
93
+ licenses: []
94
+
95
+ post_install_message:
96
+ rdoc_options: []
97
+
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ requirements: []
117
+
118
+ rubyforge_project:
119
+ rubygems_version: 1.3.7
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: A Ruby interface to Melomel
123
+ test_files:
124
+ - test/helper.rb
125
+ - test/test_bridge.rb
126
+ - test/test_integration.rb
127
+ - test/test_messaging.rb
128
+ - test/test_object_proxy.rb
129
+ - test/test_ui.rb