zombie-chaser 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/interface.rb ADDED
@@ -0,0 +1,77 @@
1
+ class Interface
2
+ attr_writer :human, :current_zombie
3
+
4
+ def initialize
5
+ @representations = []
6
+ @current_zombie = nil
7
+ end
8
+
9
+ def current_representation
10
+ if @current_zombie.nil?
11
+ "." * human_successful_step_count + @human.current_symbol
12
+ elsif human_successful_step_count > @current_zombie.successful_step_count
13
+ "." * @current_zombie.successful_step_count + @current_zombie.current_symbol + "." * (@human.successful_step_count - @current_zombie.successful_step_count - 1) + @human.current_symbol
14
+ else
15
+ "." * @current_zombie.successful_step_count + @current_zombie.current_symbol
16
+ end
17
+ end
18
+
19
+ def human_successful_step_count
20
+ @human.successful_step_count
21
+ end
22
+
23
+ def something_happened
24
+ @representations << current_representation
25
+ display_representation(@representations.last)
26
+ end
27
+
28
+ end
29
+
30
+ class NoInterface < Interface
31
+ attr_reader :representations
32
+
33
+ def display_representation(representation)
34
+ #Do nothing
35
+ end
36
+
37
+ def finish
38
+ #Do nothing
39
+ end
40
+ end
41
+
42
+ class ConsoleInterface < Interface
43
+
44
+ def display_representation(representation)
45
+ print "\r", representation
46
+ STDOUT.flush
47
+ sleep 0.2
48
+ end
49
+
50
+ def finish
51
+ puts
52
+ end
53
+ end
54
+
55
+ class GuiInterface < Interface
56
+
57
+ def initialize
58
+ @window = Window.new
59
+ #Actor.window = @window #Fixme why can't this do the trick?
60
+ Human.window = @window
61
+ Zombie.window = @window
62
+ @window_showing_thread = Thread.new {@window.show}
63
+ end
64
+
65
+ #Doesn't need to be used, as window updates 60 times a second anyway
66
+ def something_happened
67
+ end
68
+
69
+ def human=(human)
70
+ @window.human = human
71
+ end
72
+
73
+ def current_zombie=(current_zombie)
74
+ @window.current_zombie = current_zombie
75
+ end
76
+
77
+ end
@@ -0,0 +1,42 @@
1
+ require "test/unit/collector/objectspace"
2
+ require "test/unit/ui/testrunnermediator"
3
+
4
+ class TestUnitHandler
5
+ attr_reader :results
6
+
7
+ def initialize(test_pattern, actor)
8
+ @actor = actor
9
+ raise "Error: can't detect any files in test pattern \"#{test_pattern} (Don't forget to use forward slashes even in Windows)" if Dir.glob(test_pattern).empty?
10
+ Dir.glob(test_pattern).each {|test| require test} #In heckle, this is separated out
11
+ @finished = false
12
+ @results = []
13
+ @step_count = 0
14
+ obj_sp = Test::Unit::Collector::ObjectSpace.new
15
+ test_suite = Test::Unit::TestSuite.new("Mutation slayer test suite")
16
+ test_suite << obj_sp.collect
17
+ @test_runner_mediator = Test::Unit::UI::TestRunnerMediator.new(test_suite)
18
+ @test_runner_mediator.add_listener(Test::Unit::TestResult::FAULT) {test_failed}
19
+ @test_runner_mediator.add_listener(Test::Unit::TestCase::FINISHED) {test_finished}
20
+
21
+ end
22
+
23
+ def run
24
+ catch(:stop_test_runner) do
25
+ @test_runner_mediator.run_suite
26
+ end
27
+ end
28
+
29
+ def test_failed
30
+ @results << :failure
31
+ @actor.notify_failing_step
32
+ sleep 0.5
33
+ throw :stop_test_runner
34
+ end
35
+
36
+ def test_finished
37
+ sleep 0.1 #Hack to avoid it being too quick
38
+ @results << :pass
39
+ @actor.notify_passing_step
40
+ end
41
+
42
+ end
data/lib/world.rb ADDED
@@ -0,0 +1,93 @@
1
+ require "human"
2
+ require "interface"
3
+
4
+ class World
5
+ attr_reader :representations, :interface
6
+
7
+ def self.new_using_results(human_results, zombies_results)
8
+ world = new(:no_interface)
9
+ human = MockHuman.new_using_results(human_results, world)
10
+ zombie_list = MockZombieList.new_using_results(zombies_results, world)
11
+ world.set_human(human)
12
+ world.set_zombie_list(zombie_list)
13
+ world
14
+ end
15
+
16
+ def self.new_using_test_unit_handler(test_pattern)
17
+ world = new(:gui_interface) #FIXME make this a choice between console and gui
18
+ human = Human.new_using_test_unit_handler(test_pattern, world)
19
+ zombie_list = MockZombieList.new_using_results([], world) #Fixme
20
+ world.set_human(human)
21
+ world.set_zombie_list(zombie_list)
22
+ world.set_test_pattern(test_pattern)
23
+ world
24
+ end
25
+
26
+ def initialize(interface_type)
27
+ @human = nil
28
+ @current_zombie = nil
29
+ @zombie_list = nil
30
+ @test_pattern = nil
31
+ @interface = case interface_type
32
+ when :console_interface then ConsoleInterface.new
33
+ when :no_interface then NoInterface.new
34
+ when :gui_interface then GuiInterface.new
35
+ end
36
+ end
37
+
38
+ def set_human(human)
39
+ raise "Already set" unless @human.nil?
40
+ @human = human
41
+ interface.human = human
42
+ end
43
+
44
+ def set_zombie_list(zombie_list)
45
+ raise "Already set" unless @zombie_list.nil?
46
+ @zombie_list = zombie_list
47
+ end
48
+
49
+ def set_test_pattern(test_pattern)
50
+ raise "Already set" unless @test_pattern.nil?
51
+ @test_pattern = test_pattern
52
+ end
53
+
54
+ #FIXME currently only used by the unit tests. It is equivalent to ZombieTestChaser.validate
55
+ def run
56
+ run_human
57
+ until (@human.dead? or @zombie_list.all_slain?)
58
+ run_zombie(@zombie_list.supply_next_zombie)
59
+ end
60
+ @interface.finish
61
+ end
62
+
63
+ def run_human
64
+ @human.run
65
+ @human.finish_dying if @human.dying?
66
+ ! @human.dead?
67
+ end
68
+
69
+ def create_zombie_using_test_unit_handler
70
+ raise "@test_pattern not defined?" if @test_pattern.nil?
71
+ zombie = Zombie.new_using_test_unit_handler(@test_pattern, self)
72
+ end
73
+
74
+ def run_zombie(zombie)
75
+ @current_zombie = zombie
76
+ @interface.current_zombie = zombie
77
+ sleep 0.2
78
+ @current_zombie.run
79
+ if @current_zombie.dying?
80
+ @current_zombie.finish_dying
81
+ else
82
+ @current_zombie.eat(@human)
83
+ sleep 1
84
+ end
85
+ ! @current_zombie.dead?
86
+ end
87
+
88
+ def something_happened
89
+ @interface.something_happened
90
+ end
91
+
92
+ end
93
+
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "test/unit/collector/objectspace"
4
+ require "test/unit/ui/testrunnermediator"
5
+ require 'chaser'
6
+
7
+ $: << 'lib' << 'test'
8
+
9
+ # Make sure test/unit doesn't swallow our timeout
10
+ begin
11
+ Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS << Chaser::Timeout
12
+ rescue NameError
13
+ # ignore
14
+ end
15
+
16
+ class ZombieTestChaser < Chaser
17
+
18
+ VERSION = '0.0.1'
19
+
20
+ @@test_pattern = 'test/test_*.rb'
21
+ @@tests_loaded = false
22
+ @@world = nil
23
+
24
+ def self.test_pattern=(value)
25
+ @@test_pattern = value
26
+ end
27
+
28
+ def self.create_world
29
+ @@tests_loaded = true
30
+ @@world = World.new_using_test_unit_handler(@@test_pattern)
31
+ end
32
+
33
+ def self.world
34
+ @@world
35
+ end
36
+
37
+ def self.current_class_names(exclude_list)
38
+ result = []
39
+ ObjectSpace.each_object(Class) do |klass|
40
+ next if klass.to_s.include?("Class:0x")
41
+ next unless klass.ancestors.all? {|ancestor| (ancestor.to_s.split(/::/) & exclude_list).empty?}
42
+ result << klass.to_s
43
+ end
44
+ result
45
+ end
46
+
47
+ def self.validate(klass_name = nil, method_name = nil, force = false)
48
+ pre_existing_class_names = self.current_class_names([]) unless klass_name
49
+ create_world
50
+
51
+ if klass_name
52
+ klass = klass_name.to_class
53
+ # Does the method exist?
54
+ klass_methods = klass.singleton_methods(false).collect {|meth| "self.#{meth}"}
55
+ if method_name
56
+ if method_name =~ /self\./
57
+ abort "Unknown method: #{klass_name}.#{method_name.gsub('self.', '')}" unless klass_methods.include? method_name
58
+ else
59
+ abort "Unknown method: #{klass_name}##{method_name}" unless klass.instance_methods(false).map{|sym| sym.to_s}.include? method_name
60
+ end
61
+ end
62
+ end
63
+
64
+ initial_time = Time.now
65
+
66
+ chaser = self.new(klass_name)
67
+
68
+ passed = chaser.human_survives?
69
+
70
+ unless force or passed then
71
+ abort "Initial run of tests failed... fix and run chaser again"
72
+ end
73
+
74
+ if self.guess_timeout? then
75
+ running_time = Time.now - initial_time
76
+ adjusted_timeout = (running_time * 2 < 5) ? 5 : (running_time * 2).ceil
77
+ self.timeout = adjusted_timeout
78
+ end
79
+
80
+ puts "Timeout set to #{adjusted_timeout} seconds."
81
+
82
+ if passed then
83
+ puts "Initial tests pass. Let's rumble."
84
+ else
85
+ puts "Initial tests failed but you forced things. Let's rumble."
86
+ end
87
+ puts
88
+
89
+ counts = Hash.new(0)
90
+
91
+ klass_names = klass_name ? Array(klass_name) : self.current_class_names(["Test"]) - pre_existing_class_names
92
+ klass_names.each do |block_klass_name|
93
+ block_klass = block_klass_name.to_class
94
+
95
+ methods = method_name ? Array(method_name) : block_klass.instance_methods(false) + block_klass.singleton_methods(false).collect {|meth| "self.#{meth}"}
96
+
97
+ methods.sort.each do |block_method_name|
98
+ result = self.new(block_klass_name, block_method_name).validate
99
+ counts[result] += 1
100
+ end
101
+ end
102
+ all_good = counts[false] == 0
103
+
104
+ puts "Chaser Results:"
105
+ puts
106
+ puts "Passed : %3d" % counts[true]
107
+ puts "Failed : %3d" % counts[false]
108
+ puts
109
+
110
+ if all_good then
111
+ puts "All chasing was thwarted! YAY!!!"
112
+ else
113
+ puts "Improve the tests and try again."
114
+ end
115
+
116
+ all_good
117
+ end
118
+
119
+ def human_survives?
120
+ self.class.world.run_human
121
+ end
122
+
123
+ def zombie_survives?
124
+ zombie = self.class.world.create_zombie_using_test_unit_handler
125
+ self.class.world.run_zombie(zombie)
126
+ end
127
+
128
+ def initialize(klass_name=nil, method_name=nil)
129
+ super
130
+ self.class.create_world unless @@tests_loaded
131
+ end
132
+
133
+ end
@@ -0,0 +1,56 @@
1
+ class Chased
2
+ def add(a,b)
3
+ a + b
4
+ end
5
+
6
+ def say_hello
7
+ "G'day!"
8
+ end
9
+
10
+ def self.static_method
11
+ "Zap!"
12
+ end
13
+
14
+ def block_using_instance_method
15
+ result = []
16
+ block_yielding_instance_method do |i|
17
+ result << i * 2
18
+ end
19
+ result
20
+ end
21
+
22
+ def block_yielding_instance_method
23
+ yield 1
24
+ yield 2
25
+ yield 3
26
+ end
27
+
28
+ def self.block_using_class_method
29
+ result = []
30
+ block_yielding_class_method do |i|
31
+ result << i * 2
32
+ end
33
+ result
34
+ end
35
+
36
+ def self.block_yielding_class_method
37
+ yield 1
38
+ yield 2
39
+ end
40
+
41
+ def [](x)
42
+ x * 2
43
+ end
44
+
45
+ def self.[](x)
46
+ x * 2
47
+ end
48
+
49
+ def question?
50
+ "exclamation!"
51
+ end
52
+
53
+ def foo=(x)
54
+ nil
55
+ end
56
+ end
@@ -0,0 +1,144 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/fixtures')
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'test/unit/testcase'
5
+ require 'test/unit' if $0 == __FILE__
6
+ require 'zombie_test_chaser'
7
+ require 'chased'
8
+
9
+ class TestChaser < Chaser
10
+ def rand(*args)
11
+ 5
12
+ end
13
+
14
+ def rand_string
15
+ "l33t h4x0r"
16
+ end
17
+
18
+ def rand_number(*args)
19
+ 5
20
+ end
21
+
22
+ def rand_symbol
23
+ :"l33t h4x0r"
24
+ end
25
+ end
26
+
27
+ class ChaserTestCase < Test::Unit::TestCase
28
+ unless defined? Mini then
29
+ undef_method :default_test
30
+ alias :refute_equal :assert_not_equal
31
+ end
32
+
33
+ def setup
34
+ end
35
+
36
+ def teardown
37
+ @chaser.unmodify_method if defined?(@chaser) && @chaser
38
+ end
39
+
40
+ def test_unmodified_behaves_as_expected
41
+ chased = Chased.new
42
+ assert_equal 5, chased.add(2,3), "Unmodified version should equal 5"
43
+ end
44
+
45
+ def test_modify_and_unmodify_instance_method
46
+ @chaser = TestChaser.new("Chased", "add")
47
+ chased = Chased.new
48
+ assert_equal 5, chased.add(2,3), "method has been modified before it should have been"
49
+ @chaser.modify_method
50
+ assert_equal 10, chased.add(2,3), "method hasn't been modified"
51
+ @chaser.unmodify_method
52
+ assert_equal 5, chased.add(2,3), "method should be back to normal, but it isn't"
53
+ end
54
+
55
+ def test_modify_and_unmodify_string
56
+ @chaser = TestChaser.new("Chased", "say_hello")
57
+ chased = Chased.new
58
+ assert_equal "G'day!", chased.say_hello, "method has been modified before it should have been"
59
+ @chaser.modify_method
60
+ assert_equal "l33t h4x0r", chased.say_hello, "method hasn't been modified"
61
+ @chaser.unmodify_method
62
+ assert_equal "G'day!", chased.say_hello, "method should be back to normal, but it isn't"
63
+ end
64
+
65
+ def test_modify_and_unmodify_class_method
66
+ @chaser = TestChaser.new("Chased", "self.static_method")
67
+ assert_equal "Zap!", Chased.static_method, "class method has been modified before it should have been"
68
+ @chaser.modify_method
69
+ assert_equal "l33t h4x0r", Chased.static_method, "class method hasn't been modified"
70
+ @chaser.unmodify_method
71
+ assert_equal "Zap!", Chased.static_method, "class method should be back to normal, but it isn't"
72
+ end
73
+
74
+ def test_pass_blocks_on_in_instance_methods
75
+ @chaser = TestChaser.new("Chased", "block_yielding_instance_method")
76
+ chased = Chased.new
77
+ assert_equal [2,4,6], chased.block_using_instance_method, "block yielding instance method has been modified before it should have been"
78
+ @chaser.modify_method
79
+ assert_equal [12, 14, 16], chased.block_using_instance_method, "yielded values haven't been modified"
80
+ @chaser.unmodify_method
81
+ assert_equal [2,4,6], chased.block_using_instance_method, "block yielding instance method has been modified before it should have been"
82
+ end
83
+
84
+ def test_pass_blocks_on_in_class_methods
85
+ @chaser = TestChaser.new("Chased", "self.block_yielding_class_method")
86
+ assert_equal [2,4], Chased.block_using_class_method, "block yielding class method has been modified before it should have been"
87
+ @chaser.modify_method
88
+ assert_equal [12, 14], Chased.block_using_class_method, "yielded values haven't been modified"
89
+ @chaser.unmodify_method
90
+ assert_equal [2,4], Chased.block_using_class_method, "block yielding class method has been modified before it should have been"
91
+ end
92
+
93
+ def test_handle_funny_characters_in_instance_method_names
94
+ @chaser = TestChaser.new("Chased", "[]")
95
+ chased = Chased.new
96
+ assert_equal 2, chased[1], "Original doesn't work"
97
+ @chaser.modify_method
98
+ assert_equal 7, chased[1], "Modified doesn't work"
99
+ @chaser.unmodify_method
100
+ assert_equal 2, chased[1], "Modified then unmodified doesn't work"
101
+ end
102
+
103
+ def test_handle_funny_characters_in_class_method_names
104
+ @chaser = TestChaser.new("Chased", "self.[]")
105
+ assert_equal 2, Chased[1], "Original doesn't work"
106
+ @chaser.modify_method
107
+ assert_equal 7, Chased[1], "Modified doesn't work"
108
+ @chaser.unmodify_method
109
+ assert_equal 2, Chased[1], "Modified then unmodified doesn't work"
110
+ end
111
+
112
+ def test_more_funny_characters
113
+ assert_nothing_raised("Can't handle certain characters") do
114
+ @chaser = TestChaser.new("Chased", "question?")
115
+ chased = Chased.new
116
+ chased.question?
117
+ @chaser.modify_method
118
+ chased.question?
119
+ @chaser.unmodify_method
120
+ chased.question?
121
+ end
122
+
123
+ assert_nothing_raised("Can't handle equal signs") do
124
+ @chaser = TestChaser.new("Chased", "foo=")
125
+ chased = Chased.new
126
+ chased.foo= 1
127
+ @chaser.modify_method
128
+ chased.foo = 2
129
+ @chaser.unmodify_method
130
+ chased.foo = 3
131
+ end
132
+ end
133
+ end
134
+
135
+
136
+ class ZombieTestChaserCase < Test::Unit::TestCase
137
+ def test_detects_invalid_glob
138
+ incorrect_glob = 'test\test_chaser.rb'
139
+ ZombieTestChaser.test_pattern = incorrect_glob
140
+ assert_raise(RuntimeError, "Can't detect an incorrect glob") do
141
+ ZombieTestChaser.create_world
142
+ end
143
+ end
144
+ end
data/test/test_unit.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "test/test_chaser.rb"
2
+ require "test/test_zombie.rb"
@@ -0,0 +1,95 @@
1
+ $: << "lib"
2
+
3
+ require "test/unit"
4
+ require "world"
5
+
6
+ module TestHumanHelper
7
+ def assert_that_representations_include(expected_representation, human_results, failure_message)
8
+ world = create_world(human_results)
9
+ interface = world.interface
10
+ assert interface.representations.include?(expected_representation), failure_message + "Expected #{expected_representation}, got #{world.representations.inspect}"
11
+ end
12
+
13
+ def assert_that_representations_include_these_representations(expected_representations, human_results, zombies_results, failure_message)
14
+ world = create_world(human_results, zombies_results)
15
+ interface = world.interface
16
+ expected_representations.each do |expected_representation|
17
+ assert interface.representations.include?(expected_representation), failure_message + ": Expected #{expected_representation}, got #{world.representations.inspect}"
18
+ end
19
+ end
20
+
21
+ def create_world(human_results, zombies_results = [])
22
+ world = World.new_using_results(human_results, zombies_results)
23
+ world.run
24
+ world
25
+ end
26
+ end
27
+
28
+ class TestHuman < Test::Unit::TestCase
29
+ include TestHumanHelper
30
+
31
+ def test_human_single_success
32
+ human_results = [:pass]
33
+ failure_message = "Can't handle single success"
34
+ assert_that_representations_include(".@", human_results, failure_message)
35
+ end
36
+
37
+ def test_human_single_failure
38
+ human_results = [:failure]
39
+ failure_message = "Can't handle single failure"
40
+ assert_that_representations_include("+", human_results, failure_message)
41
+ end
42
+
43
+ def test_human_two_successes
44
+ human_results = [:pass, :pass]
45
+ expected_representations = ["@", ".@", "..@"]
46
+ failure_message = "Needs to be able to do multiple steps"
47
+ #This'll run the simulation three times. Optimize if neccessary (it isn't yet)
48
+ expected_representations.each do |expected_representation|
49
+ assert_that_representations_include(expected_representation, human_results, failure_message)
50
+ end
51
+ end
52
+
53
+ def test_human_success_failure
54
+ human_results = [:pass, :failure]
55
+ expected_representation = ".+"
56
+ failure_message = "Can't handle success and failure"
57
+ assert_that_representations_include(expected_representation, human_results, failure_message)
58
+ end
59
+
60
+ def test_human_success_failure_success
61
+ human_results = [:pass, :failure, :pass]
62
+ expected_representation = ".+"
63
+ failure_message = "Can't handle success and failure"
64
+ assert_that_representations_include(expected_representation, human_results, failure_message)
65
+ end
66
+
67
+ def test_human_exploding
68
+ human_results = [:pass, :failure]
69
+ expected_representation = ".*"
70
+ failure_message = "Doesn't represent the human exploding"
71
+ assert_that_representations_include(expected_representation, human_results, failure_message)
72
+ end
73
+
74
+ end
75
+
76
+ class TestZombie < Test::Unit::TestCase
77
+ include TestHumanHelper
78
+
79
+ def test_human_surviving_zombie_slaying
80
+ human_results = [:pass, :pass]
81
+ zombies_results = [[:failure]]
82
+ expected_representations = ["..@", "*.@"]
83
+ failure_message = "Can't represent a zombie slaying."
84
+ assert_that_representations_include_these_representations(expected_representations, human_results, zombies_results, failure_message)
85
+ end
86
+
87
+ def test_human_surviving_zombie_slaying2
88
+ human_results = [:pass, :pass, :pass]
89
+ zombies_results = [[:pass, :failure]]
90
+ expected_representations = ["...@", "Z..@", ".*.@"]
91
+ failure_message = "Can't represent a zombie slaying."
92
+ assert_that_representations_include_these_representations(expected_representations, human_results, zombies_results, failure_message)
93
+ end
94
+
95
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file