vmsim 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.autotest +27 -0
  2. data/README.txt +1 -0
  3. data/Rakefile +40 -0
  4. data/VERSION +1 -0
  5. data/bin/vmdeploy +23 -0
  6. data/bin/vmsim +37 -0
  7. data/doc/actuators_and_sensors.txt +32 -0
  8. data/doc/handout.odt +0 -0
  9. data/doc/machine.jpg +0 -0
  10. data/doc/todo.txt +25 -0
  11. data/lib/adapter/actuator_collection.rb +61 -0
  12. data/lib/adapter/sensor_collection.rb +57 -0
  13. data/lib/archive_builder.rb +64 -0
  14. data/lib/control/main.rb +21 -0
  15. data/lib/control.rb +3 -0
  16. data/lib/deploy/deployer.rb +96 -0
  17. data/lib/deploy.rb +1 -0
  18. data/lib/devices/devices.rb +73 -0
  19. data/lib/devices.rb +1 -0
  20. data/lib/gui/SimulatorGui.gtk +289 -0
  21. data/lib/gui/simulator_gtk.rb +57 -0
  22. data/lib/gui/simulator_gui.rb +64 -0
  23. data/lib/gui.rb +2 -0
  24. data/lib/hardware/bin.rb +28 -0
  25. data/lib/hardware/button.rb +18 -0
  26. data/lib/hardware/can.rb +9 -0
  27. data/lib/hardware/cash_register.rb +79 -0
  28. data/lib/hardware/component.rb +56 -0
  29. data/lib/hardware/display.rb +29 -0
  30. data/lib/hardware/drawer.rb +45 -0
  31. data/lib/hardware/enum.rb +20 -0
  32. data/lib/hardware/simulator.rb +98 -0
  33. data/lib/hardware.rb +10 -0
  34. data/lib/hardware_adapter.rb +11 -0
  35. data/lib/vmlog.rb +5 -0
  36. data/test/adapter/actuator_collection_test.rb +106 -0
  37. data/test/adapter/sensor_collection_test.rb +51 -0
  38. data/test/deploy/deployer_test.rb +114 -0
  39. data/test/deploy/was_run.sh +5 -0
  40. data/test/devices/devices_test.rb +78 -0
  41. data/test/hardware/bin_test.rb +57 -0
  42. data/test/hardware/button_test.rb +23 -0
  43. data/test/hardware/can_test.rb +11 -0
  44. data/test/hardware/cash_register_test.rb +157 -0
  45. data/test/hardware/component_test.rb +37 -0
  46. data/test/hardware/display_test.rb +94 -0
  47. data/test/hardware/drawer_test.rb +100 -0
  48. data/test/hardware/enum_test.rb +33 -0
  49. data/test/hardware/simulator_test.rb +189 -0
  50. data/test/test_helper.rb +5 -0
  51. data/vmsim.gemspec +89 -0
  52. metadata +131 -0
@@ -0,0 +1,20 @@
1
+ module Hardware
2
+ class Enum
3
+ def initialize(type)
4
+ @type = type
5
+ end
6
+ def inspect
7
+ return "#{self.class.name}.#{@type}"
8
+ end
9
+ def self.method_missing(method)
10
+ begin
11
+ self.send('class_variable_get',"@@#{method}")
12
+ rescue
13
+ raise "Enum constant '#{method}' not defined in #{self}"
14
+ end
15
+ end
16
+ def self.values(*values)
17
+ values.each{|value| send('class_variable_set',"@@#{value}", new(value.to_s))}
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,98 @@
1
+ require 'hardware_adapter'
2
+ module Hardware
3
+ class Simulator
4
+ attr_reader :bin, :cash_register, :display
5
+
6
+ def initialize(actuators = Adapter.actuators, sensors = Adapter.sensors)
7
+ @components = {}
8
+ @actuators = actuators
9
+ @sensors = sensors
10
+ end
11
+
12
+ def component(component_name)
13
+ @components[component_name]
14
+ end
15
+
16
+ def assemble_hardware_component(component_name, component)
17
+ @components[component_name] = component
18
+ component.configure
19
+ end
20
+
21
+ def assemble_hardware_components(basename, components)
22
+ components.each_with_index do |component, sequence_number|
23
+ assemble_hardware_component(component_name(basename, sequence_number), component)
24
+ end
25
+ end
26
+
27
+ def monitor_changes(component_name, &block)
28
+ component(component_name).monitor_changes(&block)
29
+ end
30
+
31
+ def assemble_hardware
32
+ assemble_hardware_component :bin, create_bin
33
+ assemble_hardware_component :display, create_display
34
+ assemble_hardware_component :cash_register, create_cash_register
35
+ assemble_hardware_components :drawer, create_drawers
36
+ assemble_hardware_components :button, create_buttons
37
+ end
38
+
39
+ def drawer(no)
40
+ @drawers[no]
41
+ end
42
+
43
+ def button(no)
44
+ @buttons[no]
45
+ end
46
+
47
+ def reset
48
+ @components.each { |name, component| component.reset }
49
+ sensors.remove_fire_listeners
50
+ boot
51
+ end
52
+
53
+ def boot
54
+ call_boot_block
55
+ end
56
+
57
+ def on_boot(&block)
58
+ @boot_block = block
59
+ end
60
+
61
+
62
+ private
63
+ attr_reader :actuators, :sensors
64
+
65
+ def call_boot_block
66
+ @boot_block.call if @boot_block
67
+ end
68
+ def component_name(basename, sequence_number)
69
+ "#{basename}_#{sequence_number}".to_sym
70
+ end
71
+
72
+ def create_display
73
+ @display = Hardware::Display.new(actuators)
74
+ end
75
+
76
+ def create_cash_register
77
+ @cash_register = Hardware::CashRegister.new(bin, sensors, actuators)
78
+ end
79
+
80
+ def create_drawers
81
+ @drawers = [ Hardware::Drawer.new(bin,sensors,actuators, 0),
82
+ Hardware::Drawer.new(bin,sensors,actuators, 1),
83
+ Hardware::Drawer.new(bin,sensors,actuators, 2),
84
+ Hardware::Drawer.new(bin,sensors,actuators, 3)]
85
+ end
86
+
87
+ def create_buttons
88
+ @buttons = [ Hardware::Button.new(sensors, 0),
89
+ Hardware::Button.new(sensors, 1),
90
+ Hardware::Button.new(sensors, 2),
91
+ Hardware::Button.new(sensors, 3)]
92
+ end
93
+
94
+ def create_bin
95
+ @bin = Hardware::Bin.new(sensors)
96
+ end
97
+ end
98
+ end
data/lib/hardware.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'hardware/enum'
2
+ require 'hardware/component'
3
+ require 'hardware/can'
4
+ require 'hardware/button'
5
+ require 'hardware/display'
6
+ require 'hardware/bin'
7
+ require 'hardware/drawer'
8
+ require 'hardware/cash_register'
9
+ require 'hardware/simulator'
10
+
@@ -0,0 +1,11 @@
1
+ require 'adapter/sensor_collection'
2
+ require 'adapter/actuator_collection'
3
+
4
+ module Adapter
5
+ def self.actuators
6
+ @@actuators ||= ActuatorCollection.new
7
+ end
8
+ def self.sensors
9
+ @@sensors ||= SensorCollection.new
10
+ end
11
+ end
data/lib/vmlog.rb ADDED
@@ -0,0 +1,5 @@
1
+ module VMLog
2
+ def log(message)
3
+ $stderr.puts message
4
+ end
5
+ end
@@ -0,0 +1,106 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'hardware_adapter'
3
+
4
+ module Adapter
5
+ class ActuatorCollectionTest < Test::Unit::TestCase
6
+ attr_reader :actuator_collection, :actuator_called, :other_actuator_called
7
+ def setup
8
+ @actuator_collection = ActuatorCollection.new(SynchronousFirer)
9
+ @actuator_called = false
10
+ @other_actuator_called = false
11
+ actuator_collection.create_actuator_for('some_actuator') do
12
+ @actuator_called = true
13
+ end
14
+ actuator_collection.create_actuator_for(:some_other_actuator) do
15
+ @other_actuator_called = true
16
+ end
17
+ end
18
+
19
+ def test_list_lists_actuators
20
+ assert_equal [:some_actuator, :some_other_actuator], actuator_collection.list
21
+ end
22
+
23
+ def test_actuators_not_called_if_not_fired
24
+ assert_equal false, actuator_called, "this actuator should not be called yet"
25
+ assert_equal false, other_actuator_called, "this actuator should not be called yet"
26
+ end
27
+
28
+ def test_calls_block_given_for_any_actuator
29
+ actuator_collection.fire(:some_actuator)
30
+ actuator_collection.fire(:some_other_actuator)
31
+
32
+ assert actuator_called, "actuator should be called on fire"
33
+ assert other_actuator_called, "actuator should be called on fire"
34
+ end
35
+
36
+ def test_actuator_block_can_accept_parameters
37
+ int_val = -1
38
+ string_val = ""
39
+ actuator_collection.create_actuator_for(:parametarized_actuator) do |param1, param2|
40
+ int_val = param1
41
+ string_val = param2
42
+ end
43
+ actuator_collection.fire(:parametarized_actuator, 34, "gijs")
44
+
45
+ assert_equal 34, int_val
46
+ assert_equal "gijs", string_val
47
+ end
48
+
49
+ def test_actuator_block_may_skip_parameters
50
+ int_val = -1
51
+ string_val = ""
52
+ actuator_collection.create_actuator_for(:parametarized_actuator) do |param1, param2|
53
+ int_val = param1
54
+ string_val = param2
55
+ end
56
+ actuator_collection.fire(:parametarized_actuator, 34, "gijs", "truus")
57
+
58
+ assert_equal 34, int_val
59
+ assert_equal "gijs", string_val
60
+ end
61
+
62
+ def test_raises_exception_on_firing_unknown_actuator
63
+ assert_raises NoSuchActuatorException do
64
+ actuator_collection.fire(:unknown_actuator)
65
+ end
66
+ end
67
+
68
+ def test_raises_exception_on_no_block_given
69
+ assert_raises NoActuatorBlockGiven do
70
+ actuator_collection.create_actuator_for(:actuator_no_block)
71
+ end
72
+ end
73
+
74
+ def test_raises_exception_on_duplicate_actuator
75
+ assert_raises DuplicateActuatorException do
76
+ actuator_collection.create_actuator_for(:some_actuator) {}
77
+ end
78
+ end
79
+ end
80
+
81
+ class AsynchronousFirerTest < Test::Unit::TestCase
82
+ def test_fires_on_other_thread
83
+ fired = false
84
+ firer = AsynchronousFirer.new do
85
+ sleep(0.1)
86
+ fired = true
87
+ end
88
+ t = firer.fire
89
+ assert_equal false, fired, "should not be fired"
90
+ t.join
91
+ assert fired, "should be fired"
92
+ end
93
+
94
+ def test_fires_on_other_thread_with_parameters
95
+ fired = ''
96
+ firer = AsynchronousFirer.new do |value|
97
+ sleep(0.1)
98
+ fired = value
99
+ end
100
+ t = firer.fire('passed string value')
101
+ assert_equal '', fired, "should not be fired"
102
+ t.join
103
+ assert_equal 'passed string value', fired, "should be fired"
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'hardware_adapter'
3
+
4
+ module Adapter
5
+
6
+ class SensorCollectionFireTest < Test::Unit::TestCase
7
+ attr_reader :sensors
8
+ def setup
9
+ @sensors = SensorCollection.new
10
+ sensors.create_sensor_for(:bin_entry)
11
+ end
12
+
13
+ def test_list_lists_sensor
14
+ sensors.create_sensor_for(:something_entry)
15
+ assert_equal [:bin_entry, :something_entry], sensors.list
16
+ end
17
+
18
+ def test_calls_block_given_for_a_sensor
19
+ bin_entry_fired = false
20
+ sensors.on(:bin_entry) do
21
+ bin_entry_fired = true
22
+ end
23
+ sensors.fire(:bin_entry)
24
+ assert bin_entry_fired, "bin_entry should be fired"
25
+ end
26
+
27
+ def test_swallows_fire_silently_when_no_handler_defined
28
+ sensors.fire(:bin_entry)
29
+ end
30
+
31
+ end
32
+
33
+ class SensorCollectionFireWithoutReceptorsCreatedTest < Test::Unit::TestCase
34
+ attr_reader :sensors
35
+ def setup
36
+ @sensors = SensorCollection.new
37
+ end
38
+
39
+ def test_swallows_fire_silently_when_no_handler_defined
40
+ sensors.fire(:bin_entry)
41
+ end
42
+
43
+ def test_on_raises_a_no_such_sensor_exception
44
+ assert_raises NoSuchSensorException do
45
+ sensors.on(:bin_entry) {}
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,114 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'deploy'
3
+ require 'tmpdir'
4
+ require 'archive_builder'
5
+
6
+ module Deploy
7
+ module DeployerTestHelper
8
+ attr_reader :deployer
9
+ def create_deployer
10
+ @deployer = Deployer.new(File.expand_path(File.join(File.dirname(__FILE__), 'was_run.sh')))
11
+ end
12
+ def deploy(archive)
13
+ create_deployer
14
+ deployer.deploy archive
15
+ end
16
+ def is_running(pid)
17
+ begin
18
+ Process.kill(0, pid)
19
+ return true
20
+ rescue
21
+ return false
22
+ end
23
+ end
24
+ end
25
+
26
+ class DeployerDeployTest < Test::Unit::TestCase
27
+ include ArchiveBuilder
28
+ include DeployerTestHelper
29
+ include FileUtils
30
+ def teardown
31
+ rm_r './tmp' if File.exists?('./tmp')
32
+ end
33
+
34
+ def test_unzips_the_archive_and_fires_the_executable
35
+ deploy an_archive('controlcode.pkg') {
36
+ a_file("main.rb").with("puts 'was unpacked'")
37
+ }
38
+ assert is_running(deployer.current_pid)
39
+ assert_match /was started.*was unpacked/m, deployer.wait_for.run_log
40
+ end
41
+
42
+ def test_unzips_the_archive_with_relative_path_and_fires_the_executable
43
+ deploy an_archive('controlcode.pkg', 'tmp') {
44
+ a_file("main.rb").with("puts 'was unpacked'")
45
+ }
46
+ assert is_running(deployer.current_pid)
47
+ assert_match /was started.*was unpacked/m, deployer.wait_for.run_log
48
+ end
49
+
50
+ def test_clears_current_pid_on_exit
51
+ deploy an_archive('controlcode.pkg') {
52
+ a_file("main.rb").with("puts 'was unpacked'")
53
+ }
54
+ assert_nil deployer.wait_for.current_pid
55
+ assert !File.exists?(Deployer::PIDFILE), "PIDFILE should be gone"
56
+ end
57
+
58
+ def test_removes_previous_files
59
+ deploy an_archive('controlcode.pkg') {
60
+ a_file("loaded_class.rb").with("class LoadedClass; end")
61
+ a_file("main.rb").with("require 'loaded_class'")
62
+ }
63
+
64
+ deploy an_archive('controlcode.pkg') {
65
+ a_file("main.rb").with("puts 'was unpacked'")
66
+ }
67
+
68
+ assert !File.exists?(File.join(Deployer::DEPLOYED_DIR, 'loaded_class.rb')), "loaded class should be removed"
69
+ end
70
+
71
+ def test_removes_previous_directories
72
+ deploy an_archive('controlcode.pkg') {
73
+ a_file("lib/loaded_class.rb").with("class LoadedClass; end")
74
+ a_file("main.rb").with("require 'lib/loaded_class'")
75
+ }
76
+
77
+ deploy an_archive('controlcode.pkg') {
78
+ a_file("main.rb").with("puts 'was unpacked'")
79
+ }
80
+
81
+ assert !File.exists?(File.join(Deployer::DEPLOYED_DIR, 'loaded_class.rb')), "loaded class should be removed"
82
+ end
83
+
84
+ def test_kills_the_current_running_process_and_starts_new_one
85
+ deploy an_archive('controlcode.pkg') {
86
+ a_file("main.rb").with("sleep 1")
87
+ }
88
+ old_process_id = deployer.current_pid
89
+
90
+ deploy an_archive('controlcode.pkg') {
91
+ a_file("main.rb").with("puts 'was unpacked'")
92
+ }
93
+
94
+ assert !is_running(old_process_id), "process should be stopped before starting new"
95
+ end
96
+
97
+ end
98
+
99
+ class DeployerKillTest < Test::Unit::TestCase
100
+ include ArchiveBuilder
101
+ include DeployerTestHelper
102
+
103
+ def test_kills_the_current_running_process
104
+ deploy an_archive('controlcode.pkg') {
105
+ a_file("main.rb").with("sleep 20")
106
+ }
107
+ process_id = deployer.current_pid
108
+ create_deployer
109
+ deployer.kill
110
+ assert !is_running(process_id), "process should be stopped"
111
+ end
112
+ end
113
+ end
114
+
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path('~/.vendingmachine/control')
3
+ puts "was started"
4
+
5
+ require 'main'
@@ -0,0 +1,78 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'mocha'
3
+ require 'devices'
4
+
5
+ module Devices
6
+ class DevicesTest < Test::Unit::TestCase
7
+ attr_reader :actuators, :sensors, :devices, :handler
8
+ def setup
9
+ @actuators = mock('actuatorcollection')
10
+ @sensors = mock('sensorcollection')
11
+ @devices = Devices.new(actuators, sensors)
12
+ @handler = 'not called'
13
+ end
14
+
15
+ def test_display_show
16
+ actuators.expects(:fire).with(:display_show, 1, "test message")
17
+ devices.display_show(1, "test message")
18
+ end
19
+
20
+ def test_button_on_press
21
+ sensors.expects(:on).with(:button_press_1).yields
22
+ devices.on_button_pressed(1) { handler_called }
23
+ assert_equal 'called', handler
24
+ end
25
+
26
+ def test_bin_entry
27
+ sensors.expects(:on).with(:bin_entry).yields
28
+ devices.on_bin_entry { handler_called }
29
+ assert_equal 'called', handler
30
+ end
31
+
32
+ def test_bin_fetch_all
33
+ sensors.expects(:on).with(:bin_fetch_all).yields
34
+ devices.on_bin_fetch_all { handler_called }
35
+ assert_equal 'called', handler
36
+ end
37
+
38
+ def test_cash_register_fill_money
39
+ sensors.expects(:on).with(:cash_fill_100).yields
40
+ devices.on_cash_filled(100) { handler_called }
41
+ assert_equal 'called', handler
42
+ end
43
+
44
+ def test_cash_register_insert_money
45
+ sensors.expects(:on).with(:cash_insert_100).yields
46
+ devices.on_cash_inserted(100) { handler_called }
47
+ assert_equal 'called', handler
48
+ end
49
+
50
+ def test_cash_drop_drops_coin
51
+ actuators.expects(:fire).with(:cash_drop_100)
52
+ devices.cash_drop_coin(100)
53
+ end
54
+
55
+ def test_drawer_register_can_dropped
56
+ sensors.expects(:on).with(:drawer_drop_can_0).yields
57
+ devices.on_drawer_dropped(0) { handler_called }
58
+ assert_equal 'called', handler
59
+ end
60
+
61
+ def test_drawer_register_can_filled
62
+ sensors.expects(:on).with(:drawer_fill_can_0).yields
63
+ devices.on_drawer_filled(0) { handler_called }
64
+ assert_equal 'called', handler
65
+ end
66
+
67
+ def test_drawer_drop_drops_can
68
+ actuators.expects(:fire).with(:drawer_drop_can_1)
69
+ devices.drawer_drop_can(1)
70
+ end
71
+
72
+ private
73
+
74
+ def handler_called
75
+ @handler = 'called'
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,57 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'mocha'
3
+ require 'hardware'
4
+ require 'hardware_adapter'
5
+
6
+ module Hardware
7
+ class BinTest < Test::Unit::TestCase
8
+ attr_reader :bin, :sensor_collection
9
+ def setup
10
+ @sensor_collection = mock('sensor_collection')
11
+ @bin = Bin.new(sensor_collection)
12
+ end
13
+
14
+ def test_receive_fills_the_bin_with_the_item
15
+ sensor_collection.expects(:fire).with(:bin_entry)
16
+ bin.receive('some_object')
17
+ assert_equal(['some_object'], bin.contents)
18
+ end
19
+
20
+ def test_fetch_the_stuff_empties_the_bin
21
+ sensor_collection.stubs(:fire).with(:bin_entry) # firing :bin_entry tested above
22
+ bin.receive('some_object')
23
+ bin.receive('some_object')
24
+
25
+ sensor_collection.expects(:fire).with(:bin_fetch_all)
26
+ bin.fetch_all
27
+
28
+ assert_equal([], bin.contents)
29
+ end
30
+
31
+ def test_configure_configures_the_sensor_in_the_sensor_collection
32
+ sensor_collection.expects(:create_sensor_for).with(:bin_entry)
33
+ sensor_collection.expects(:create_sensor_for).with(:bin_fetch_all)
34
+ bin.configure
35
+ end
36
+
37
+ def test_updates_ui_when_receiving_something
38
+ bin = Bin.new(Adapter::SensorCollection.null)
39
+ change_called = false
40
+ bin.monitor_changes do
41
+ change_called = true
42
+ end
43
+ bin.receive('some_object')
44
+ assert change_called, 'change was not called'
45
+ end
46
+
47
+ def test_updates_ui_when_fetching_all
48
+ bin = Bin.new(Adapter::SensorCollection.null)
49
+ change_called = false
50
+ bin.monitor_changes do
51
+ change_called = true
52
+ end
53
+ bin.fetch_all
54
+ assert change_called, 'change was not called'
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'mocha'
3
+ require 'hardware'
4
+
5
+ module Hardware
6
+ class ButtonTest < Test::Unit::TestCase
7
+ attr_reader :sensor_collection, :button
8
+ def setup
9
+ @sensor_collection = mock('sensor_collection')
10
+ @button = Button.new(sensor_collection, 1)
11
+ end
12
+
13
+ def test_press_fires_sensor
14
+ sensor_collection.expects(:fire).with(:button_press_1)
15
+ button.press
16
+ end
17
+
18
+ def test_configure_configures_sensor_for_button_press
19
+ sensor_collection.expects(:create_sensor_for).with(:button_press_1)
20
+ button.configure
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ require File.join(File.dirname(__FILE__),'..','test_helper')
2
+ require 'hardware'
3
+
4
+ module Hardware
5
+ class CanTest < Test::Unit::TestCase
6
+ def test_to_s_shows_can_name
7
+ assert_equal 'can of cola', Can.cola.to_s
8
+ assert_equal 'can of fanta', Can.fanta.to_s
9
+ end
10
+ end
11
+ end