robodog 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.
@@ -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