robodog 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ require_relative '../lib/robo_dog/pose/orientation'
2
+
3
+ RSpec.describe RoboDog::Pose::Orientation do
4
+ describe 'the public interface' do
5
+ it { expect(described_class).to respond_to :constantize, :stringify }
6
+ end
7
+
8
+ describe '.constantize' do
9
+ shared_examples 'a constantizer' do |string, expect_constant|
10
+ subject { described_class.constantize(string) }
11
+
12
+ it 'returns the correct constant' do
13
+ is_expected.to eql expect_constant
14
+ end
15
+ end
16
+
17
+ it_behaves_like 'a constantizer', 'N', described_class::NORTH
18
+ it_behaves_like 'a constantizer', 'E', described_class::EAST
19
+ it_behaves_like 'a constantizer', 'S', described_class::SOUTH
20
+ it_behaves_like 'a constantizer', 'W', described_class::WEST
21
+ end
22
+
23
+ describe '.stringify' do
24
+ shared_examples 'a stringifier' do |constant, expected_string|
25
+ subject { described_class.stringify(constant) }
26
+
27
+ it 'returns the correct constant' do
28
+ is_expected.to eql expected_string
29
+ end
30
+ end
31
+
32
+ it_behaves_like 'a stringifier', described_class::NORTH, 'N'
33
+ it_behaves_like 'a stringifier', described_class::EAST, 'E'
34
+ it_behaves_like 'a stringifier', described_class::SOUTH, 'S'
35
+ it_behaves_like 'a stringifier', described_class::WEST, 'W'
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ require_relative '../lib/robo_dog/paddock'
2
+
3
+ RSpec.describe RoboDog::Paddock do
4
+ describe 'the public interface' do
5
+ it { expect(described_class).to respond_to :build }
6
+
7
+ subject{ described_class.new }
8
+ it { is_expected.to respond_to :valid_coordinates? }
9
+ end
10
+
11
+ describe '.build' do
12
+ context 'with invalid data' do
13
+ let(:data) { '1' }
14
+ it 'screams at you' do
15
+ expect { described_class.build(data) }.to raise_exception described_class::DataError
16
+ end
17
+ end
18
+ context 'with valid data' do
19
+ let(:data) { '4 3'}
20
+ it 'returns a paddock instance' do
21
+ expect(described_class.build(data)).to be_instance_of described_class
22
+ end
23
+ it 'builds with correct x size' do
24
+ expect(described_class.build(data).instance_variable_get(:@x_size)).to eql 4
25
+ end
26
+ it 'builds with correct y size' do
27
+ expect(described_class.build(data).instance_variable_get(:@y_size)).to eql 3
28
+ end
29
+ end
30
+ end
31
+
32
+ let(:paddock) { described_class.new(5, 5) }
33
+
34
+ describe '#valid_coordinates?' do
35
+ context 'with coordinates within paddock' do
36
+ it 'returns truthy' do
37
+ [[0,0],[5,5],[2,4]].each do |coordinates|
38
+ expect(paddock.valid_coordinates?(x: coordinates[0], y: coordinates[1])).to be_truthy
39
+ end
40
+ end
41
+ end
42
+ context 'with coordinates outside of paddock' do
43
+ it 'returns falsy' do
44
+ [[-1,0],[6,5],[7,7]].each do |coordinates|
45
+ expect(paddock.valid_coordinates?(x: coordinates[0], y: coordinates[1])).to be_falsy
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,74 @@
1
+ require_relative '../lib/robo_dog/parser'
2
+ require 'stringio'
3
+
4
+ RSpec.describe RoboDog::Parser do
5
+ describe 'the public interface' do
6
+ it { expect(described_class).to respond_to :parse }
7
+ end
8
+
9
+ describe '.parse' do
10
+ shared_examples 'a complaining parser' do |input_string|
11
+ it 'screams at you' do
12
+ str_io = StringIO.new(input_string)
13
+ expect { described_class.parse(str_io) }.to raise_exception described_class::DataError
14
+ end
15
+ end
16
+
17
+ context 'with invalid input data' do
18
+ context 'with no data' do
19
+ it_behaves_like 'a complaining parser',
20
+ ''
21
+ end
22
+
23
+ context 'with missing robot data' do
24
+ it_behaves_like 'a complaining parser',
25
+ <<-END.gsub(/^\s+\|/, '')
26
+ |1 1
27
+ |only robot_1 coordinates
28
+ END
29
+ end
30
+ end
31
+
32
+ context 'with valid input data' do
33
+ let(:valid_input) do
34
+ s = <<-END.gsub(/^\s+\|/, '')
35
+ |a paddock description
36
+ |robot_1 coordinate
37
+ |robot_1 commands
38
+ |robot_2 coordinates
39
+ |robot_2 commands
40
+ END
41
+ StringIO.new(s)
42
+ end
43
+
44
+ subject do
45
+ described_class.instance_variable_set(:@paddock_factory, paddock_factory)
46
+ described_class.instance_variable_set(:@robot_factory, robot_factory)
47
+ described_class.parse(valid_input)
48
+ end
49
+
50
+ let(:paddock_factory) do
51
+ dbl = double
52
+ allow(dbl).to receive(:build).and_return('a paddock')
53
+ dbl
54
+ end
55
+ let(:robot_factory) do
56
+ dbl = double
57
+ allow(dbl).to receive(:build).and_return('robot_1','robot_2')
58
+ dbl
59
+ end
60
+
61
+ it 'returns a hash' do
62
+ is_expected.to be_instance_of Hash
63
+ end
64
+
65
+ it 'includes paddock data' do
66
+ is_expected.to include paddock: 'a paddock'
67
+ end
68
+
69
+ it 'includes robots data' do
70
+ is_expected.to include robots: ['robot_1', 'robot_2']
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,164 @@
1
+ require_relative '../lib/robo_dog/pose'
2
+
3
+ RSpec.describe RoboDog::Pose do
4
+ describe 'the public interface' do
5
+ it { expect(described_class).to respond_to :build }
6
+ subject{ described_class.new }
7
+ it { is_expected.to respond_to :report, :coordinates, :adjacent, :rotate! }
8
+ end
9
+
10
+ describe '.build' do
11
+ context 'with invalid data' do
12
+ let(:data) { '1' }
13
+ it 'screams at you' do
14
+ expect { described_class.build(data) }.to raise_exception described_class::DataError
15
+ end
16
+ end
17
+ context 'with valid data' do
18
+ let(:data) { '4 3 N'}
19
+ it 'returns a instance of self' do
20
+ expect(described_class.build(data)).to be_instance_of described_class
21
+ end
22
+ it 'builds with correct x coordinate' do
23
+ expect(described_class.build(data).instance_variable_get(:@x)).to eql 4
24
+ end
25
+ it 'builds with correct y coordinate' do
26
+ expect(described_class.build(data).instance_variable_get(:@y)).to eql 3
27
+ end
28
+ it 'builds with correct orientation' do
29
+ expect(described_class.build(data).instance_variable_get(:@orientation)).to eql described_class::Orientation::NORTH
30
+ end
31
+ end
32
+ end
33
+
34
+ let(:pose) do
35
+ described_class.new(x: 0, y: 0, orientation: :north)
36
+ end
37
+
38
+ describe '#report' do
39
+ subject { pose.report }
40
+ it 'returns a hash' do
41
+ is_expected.to be_instance_of Hash
42
+ end
43
+
44
+ it 'includes x, y and orientation' do
45
+ is_expected.to include x: 0, y: 0, orientation: 'N'
46
+ end
47
+ end
48
+
49
+ describe '#coordinates' do
50
+ subject { pose.coordinates }
51
+
52
+ it 'returns a hash' do
53
+ is_expected.to be_instance_of Hash
54
+ end
55
+
56
+ it 'includes x and y coordinates' do
57
+ is_expected.to include x: 0, y: 0
58
+ end
59
+ end
60
+
61
+ describe '#adjacent' do
62
+ let(:pose) { described_class.new(x: 2, y: 2, orientation: orientation) }
63
+
64
+ subject do
65
+ adj = pose.adjacent
66
+ {
67
+ x: adj.instance_variable_get(:@x),
68
+ y: adj.instance_variable_get(:@y),
69
+ orientation: adj.instance_variable_get(:@orientation)
70
+ }
71
+ end
72
+
73
+ context 'when facing north' do
74
+ let(:orientation) { described_class::Orientation::NORTH }
75
+ it 'returns the correct coordinates' do
76
+ is_expected.to include x: 2, y: 3, orientation: orientation
77
+ end
78
+ end
79
+
80
+ context 'when facing east' do
81
+ let(:orientation) { described_class::Orientation::EAST }
82
+ it 'returns the correct coordinates' do
83
+ is_expected.to include x: 3, y: 2, orientation: orientation
84
+ end
85
+ end
86
+
87
+ context 'when facing south' do
88
+ let(:orientation) { described_class::Orientation::SOUTH }
89
+ it 'returns the correct coordinates' do
90
+ is_expected.to include x: 2, y: 1, orientation: orientation
91
+ end
92
+ end
93
+
94
+ context 'when facing west' do
95
+ let(:orientation) { described_class::Orientation::WEST }
96
+ it 'returns the correct coordinates' do
97
+ is_expected.to include x: 1, y: 2, orientation: orientation
98
+ end
99
+ end
100
+ end
101
+
102
+ describe '#rotate!' do
103
+ let(:pose) { described_class.new(x: 2, y: 2, orientation: init_orientation) }
104
+
105
+ shared_examples 'a rotatable pose' do |direction, correct_orientation|
106
+ subject do
107
+ rotated = pose.rotate!(direction)
108
+ {
109
+ x: rotated.instance_variable_get(:@x),
110
+ y: rotated.instance_variable_get(:@y),
111
+ orientation: rotated.instance_variable_get(:@orientation)
112
+ }
113
+ end
114
+
115
+ it 'returns the correct orientation' do
116
+ is_expected.to include x: 2, y: 2, orientation: correct_orientation
117
+ end
118
+ end
119
+
120
+ context 'when facing north' do
121
+ let(:init_orientation) { described_class::Orientation::NORTH }
122
+ it_behaves_like 'a rotatable pose',
123
+ :clockwise,
124
+ described_class::Orientation::EAST
125
+
126
+ it_behaves_like 'a rotatable pose',
127
+ :counter,
128
+ described_class::Orientation::WEST
129
+ end
130
+
131
+ context 'when facing east' do
132
+ let(:init_orientation) { described_class::Orientation::EAST }
133
+ it_behaves_like 'a rotatable pose',
134
+ :clockwise,
135
+ described_class::Orientation::SOUTH
136
+
137
+ it_behaves_like 'a rotatable pose',
138
+ :counter,
139
+ described_class::Orientation::NORTH
140
+ end
141
+
142
+ context 'when facing south' do
143
+ let(:init_orientation) { described_class::Orientation::SOUTH }
144
+ it_behaves_like 'a rotatable pose',
145
+ :clockwise,
146
+ described_class::Orientation::WEST
147
+
148
+ it_behaves_like 'a rotatable pose',
149
+ :counter,
150
+ described_class::Orientation::EAST
151
+ end
152
+
153
+ context 'when facing west' do
154
+ let(:init_orientation) { described_class::Orientation::WEST }
155
+ it_behaves_like 'a rotatable pose',
156
+ :clockwise,
157
+ described_class::Orientation::NORTH
158
+
159
+ it_behaves_like 'a rotatable pose',
160
+ :counter,
161
+ described_class::Orientation::SOUTH
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,166 @@
1
+ require_relative '../lib/robo_dog/robot'
2
+
3
+ RSpec.describe RoboDog::Robot do
4
+ describe 'the public interface' do
5
+ it { expect(described_class).to respond_to :build }
6
+ subject{ described_class.new }
7
+ it { is_expected.to respond_to :execute, :report, :coordinates, :move, :right, :left, :add_coordinator }
8
+ end
9
+
10
+ describe '.build' do
11
+ context 'with invalid data' do
12
+ let(:data) { {commands: 'wrong'} }
13
+ it 'screams at you' do
14
+ expect { described_class.build(data) }.to raise_exception described_class::DataError
15
+ end
16
+ end
17
+ context 'with valid data' do
18
+ let(:data) do
19
+ {
20
+ commands: 'LRMR',
21
+ pose: '0 0 N'
22
+ }
23
+ end
24
+ it 'returns a instance of self' do
25
+ expect(described_class.build(data)).to be_instance_of described_class
26
+ end
27
+ it 'builds with correct commands' do
28
+ expect(described_class.build(data).instance_variable_get(:@commands)).to be == [:left, :right, :move, :right]
29
+ end
30
+ it 'delegates pose building' do
31
+ expect(RoboDog::Pose).to receive(:build).with(data[:pose])
32
+ described_class.build(data)
33
+ end
34
+ end
35
+ end
36
+
37
+ let(:robot) do
38
+ described_class.new(
39
+ pose: pose,
40
+ commands: commands,
41
+ coordinators: [coordinator]
42
+ )
43
+ end
44
+
45
+ let(:pose) { double }
46
+ let(:commands) { [] }
47
+ let(:coordinator) { double }
48
+
49
+ describe '#add_coordinator' do
50
+ it 'adds a coordinator to the coordinators array' do
51
+ robot.add_coordinator('a added coordinator')
52
+ expect(robot.instance_variable_get(:@coordinators)).to include 'a added coordinator'
53
+ end
54
+ end
55
+
56
+ describe '#execute' do
57
+ let(:commands) { [:left, :move, :left, :right] }
58
+
59
+ context 'with :all' do
60
+ it 'executes all commands' do
61
+ expect(robot).to receive(:left).ordered
62
+ expect(robot).to receive(:move).ordered
63
+ expect(robot).to receive(:left).ordered
64
+ expect(robot).to receive(:right).ordered
65
+
66
+ robot.execute(:all)
67
+ end
68
+
69
+ it 'empties the commands' do
70
+ allow(robot).to receive_messages(left: nil, move: nil, right: nil)
71
+ robot.execute(:all)
72
+ expect(robot.instance_variable_get(:@commands)).to be_empty
73
+ end
74
+ end
75
+
76
+ context 'with :next' do
77
+ it 'executes the next command' do
78
+ expect(robot).to receive(:left)
79
+
80
+ robot.execute(:next)
81
+ end
82
+
83
+ it 'removes the first command' do
84
+ allow(robot).to receive(:left)
85
+ robot.execute(:next)
86
+ expect(robot.instance_variable_get(:@commands)).to match_array [:move, :left, :right]
87
+ end
88
+ end
89
+ end
90
+
91
+ describe '#report' do
92
+ let(:pose) do
93
+ dbl = double
94
+ allow(dbl).to receive(:report).and_return('some report')
95
+ dbl
96
+ end
97
+
98
+ it "returns pose's report" do
99
+ expect(robot.report).to match('some report')
100
+ end
101
+ end
102
+
103
+ describe '#coordinates' do
104
+ let(:pose) do
105
+ dbl = double
106
+ allow(dbl).to receive(:coordinates).and_return('some coordinates')
107
+ dbl
108
+ end
109
+
110
+ it "returns pose's coordinates" do
111
+ expect(robot.coordinates).to match('some coordinates')
112
+ end
113
+ end
114
+
115
+ describe '#move' do
116
+ let(:pose) do
117
+ dbl = double
118
+ adj_pose = 'adjacent pose'
119
+ allow(adj_pose).to receive(:coordinates).and_return('some coordinates')
120
+ allow(dbl).to receive(:adjacent).and_return(adj_pose)
121
+ dbl
122
+ end
123
+
124
+ let(:coordinator) do
125
+ dbl = double
126
+ allow(dbl).to receive(:valid_coordinates?).and_return(coordinator_return)
127
+ dbl
128
+ end
129
+
130
+ let(:coordinator_return) { [true, false].sample }
131
+
132
+ it 'returns nil' do
133
+ expect(robot.move).to be_nil
134
+ end
135
+
136
+ context 'with valid adjacent pose' do
137
+ let(:coordinator_return) { true }
138
+ it 'updates its pose' do
139
+ robot.move
140
+ expect(robot.instance_variable_get(:@pose)).to eql('adjacent pose')
141
+ end
142
+ end
143
+
144
+ context 'with invalid adjacent pose' do
145
+ let(:coordinator_return) { false }
146
+ it 'does not update its pose' do
147
+ robot.move
148
+ expect(robot.instance_variable_get(:@pose)).to eql(pose)
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '#right' do
154
+ it 'rotates its pose' do
155
+ expect(pose).to receive(:rotate!).with(:clockwise)
156
+ robot.right
157
+ end
158
+ end
159
+
160
+ describe '#left' do
161
+ it 'rotates its pose' do
162
+ expect(pose).to receive(:rotate!).with(:counter)
163
+ robot.left
164
+ end
165
+ end
166
+ end