Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Your name

Please write your full name here to make it easier to find your pull request.
Coral Baker

# User stories

Please list which user stories you've implemented (delete the ones that don't apply).

- [ ] User story 1: "I want to instruct a plane to land at an airport"
- [ ] User story 2: "I want to instruct a plane to take off from an airport and confirm that it is no longer in the airport"
- [ ] User story 3: "I want to prevent landing when the airport is full"
- [ ] User story 4: "I would like a default airport capacity that can be overridden as appropriate"
- [ ] User story 5: "I want to prevent takeoff when weather is stormy"
- [ ] User story 6: "I want to prevent landing when weather is stormy"
- [X] User story 1: "I want to instruct a plane to land at an airport"
- [X] User story 2: "I want to instruct a plane to take off from an airport and confirm that it is no longer in the airport"
- [X] User story 3: "I want to prevent landing when the airport is full"
- [X] User story 4: "I would like a default airport capacity that can be overridden as appropriate"
- [X] User story 5: "I want to prevent takeoff when weather is stormy"
- [X] User story 6: "I want to prevent landing when weather is stormy"

# README checklist

Does your README contains instructions for

- [ ] how to install,
- [ ] how to run,
- [ ] and how to test your code?
- [X] how to install,
- [X] how to run,
- [X] and how to test your code?

[Here is a pill](https://github.com/makersacademy/course/blob/main/pills/readmes.md) that can help you write a great README!
83 changes: 38 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,47 @@ Airport Challenge

```

Instructions
---------
This program will control the flow of planes at an airport:
- Land and store a plane in an airport if the hangar is not full.
- Allow a plane to take off from the airport, as long as there are planes in the hangar.
- Check if a plane has departed.
- Change the default capacity of the hangar if required.
- Prevent takeoff and landing if weather is stormy.

Installation
-----

1. Fork this repository.
2. Clone your new forked repository using `git clone` followed by your fork URL.
3. Move into the repository by using `cd airport_challenge`.
4. Run the command `gem install bundler` (if you don't have bundler already).
5. When the installation completes, run `bundle`.


Program Usage
-----

Use irb to run the program from the main repository:

```zsh
irb -r './lib/airport.rb'
```


* Feel free to use google, your notes, books, etc. but work on your own
* If you refer to the solution of another coach or student, please put a link to that in your README
* If you have a partial solution, **still check in a partial solution**
* You must submit a pull request to this repo with your code by 10am Monday morning
Test Usage
-----

Steps
-------
Use rspec to run the tests from the main repository:

1. Fork this repo, and clone to your local machine
2. Run the command `gem install bundler` (if you don't have bundler already)
3. When the installation completes, run `bundle`
4. Complete the following task:
```zsh
rspec
```

Task

User Stories
-----

We have a request from a client to write the software to control the flow of planes at an airport. The planes can land and take off provided that the weather is sunny. Occasionally it may be stormy, in which case no planes can land or take off. Here are the user stories that we worked out in collaboration with the client:
The client requests are broken down as follows:

```
As an air traffic controller
Expand All @@ -45,45 +66,17 @@ I want to instruct a plane to take off from an airport and confirm that it is no

As an air traffic controller
To ensure safety
I want to prevent landing when the airport is full
I want to prevent landing when the airport is full

As the system designer
So that the software can be used for many different airports
I would like a default airport capacity that can be overridden as appropriate

As an air traffic controller
As an air traffic controller
To ensure safety
I want to prevent takeoff when weather is stormy

As an air traffic controller
To ensure safety
I want to prevent landing when weather is stormy
```

Your task is to test drive the creation of a set of classes/modules to satisfy all the above user stories. You will need to use a random number generator to set the weather (it is normally sunny but on rare occasions it may be stormy). In your tests, you'll need to use a stub to override random weather to ensure consistent test behaviour.

Your code should defend against [edge cases](http://programmers.stackexchange.com/questions/125587/what-are-the-difference-between-an-edge-case-a-corner-case-a-base-case-and-a-b) such as inconsistent states of the system ensuring that planes can only take off from airports they are in; planes that are already flying cannot take off and/or be in an airport; planes that are landed cannot land again and must be in an airport, etc.

For overriding random weather behaviour, please read the documentation to learn how to use test doubles: https://www.relishapp.com/rspec/rspec-mocks/docs . There’s an example of using a test double to test a die that’s relevant to testing random weather in the test.

Please create separate files for every class, module and test suite.

In code review we'll be hoping to see:

* All tests passing
* High [Test coverage](https://github.com/makersacademy/course/blob/main/pills/test_coverage.md) (>95% is good)
* The code is elegant: every class has a clear responsibility, methods are short etc.

Reviewers will potentially be using this [code review rubric](docs/review.md). Referring to this rubric in advance will make the challenge somewhat easier. You should be the judge of how much challenge you want this at this moment.

**BONUS**

* Write an RSpec **feature** test that lands and takes off a number of planes

Note that is a practice 'tech test' of the kinds that employers use to screen developer applicants. More detailed submission requirements/guidelines are in [CONTRIBUTING.md](CONTRIBUTING.md)

Finally, don’t overcomplicate things. This task isn’t as hard as it may seem at first.

* **Submit a pull request early.**

* Finally, please submit a pull request before Monday at 10am with your solution or partial solution. However much or little amount of code you wrote please please please submit a pull request before Monday at 10am.
```
41 changes: 41 additions & 0 deletions lib/airport.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require_relative '../lib/plane.rb'
require_relative '../lib/weather.rb'

class Airport
attr_reader :hangar, :capacity
DEFAULT_CAPACITY = 10

def initialize(capacity = DEFAULT_CAPACITY)
@hangar = []
@capacity = capacity
@weather = Weather.new
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods were kept very short which makes them easier to understand

def land(plane)
land_checks(plane)
@hangar << plane
end

def take_off(plane)
take_off_checks(plane)
@hangar.delete(plane)
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of private methods really helps with isolating parts of the code that you don't want the user to access.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also greatly simplifies the code that the user will be using

private

def land_checks(plane)
fail 'Hangar full.' if @hangar.count >= @capacity
fail 'Plane already grounded.' if @hangar.include? plane
fail 'Weather stormy, landing not available.' if storm? == true
end

def take_off_checks(plane)
fail 'No planes in hangar.' if @hangar.count == 0
fail 'Plane not in hangar.' if [email protected]? plane
fail 'Weather stormy, take off not available.' if storm? == true
end

def storm?
@weather.storm?
end
end
19 changes: 19 additions & 0 deletions lib/plane.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Plane
attr_reader :flying

def initialize
@flying = true
end

def ground
@flying = false
end

def air
@flying = true
end
end

# Now that I've completed the 'stormy' stories and understand rspec a bit better,
# I might be able to use the above to detect whether a plane has landed or not.
# Unfortunately it's quite late and best saved for another time!
5 changes: 5 additions & 0 deletions lib/weather.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Weather
def storm?
rand(1..6) == 6
end
end
97 changes: 97 additions & 0 deletions spec/airport_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require_relative '../lib/airport.rb'

describe Airport do
let(:plane) { double :plane }
let(:plane2) { double :plane2 }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single line syntax for setting doubles with their own methods and return values in each unit test to make code more DRY

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also take that a step further by using a "before do" bock of code to set the doubles and method results for all subsequent tests

let(:airport) { Airport.new(5) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests have been ordered well with landing tests grouped together first before take off tests

describe '#capacity' do
it 'should confirm default capacity is set to 10.' do
expect(subject.capacity).to eq Airport::DEFAULT_CAPACITY
end

it 'should allow user to set the docking station capacity' do
allow(airport).to receive(:storm?).and_return false
5.times { airport.land(be_an_instance_of plane) }
expect{ airport.land(plane) }.to raise_error 'Hangar full.'
end
end

describe '#land' do
it { is_expected.to respond_to(:land).with(1).argument }

it 'should store a plane in hangar when landed' do
allow(subject).to receive(:storm?).and_return false
expect(subject.land(plane)).not_to be_empty
end

it 'raises an error when hangar is full.' do
allow(subject).to receive(:storm?).and_return false
Airport::DEFAULT_CAPACITY.times { subject.land(be_an_instance_of plane) }
expect { subject.land(plane) }.to raise_error 'Hangar full.'
end

it 'should raise error if grounded plane tries landing again.' do
allow(subject).to receive(:storm?).and_return false
subject.land(plane)
expect { subject.land(plane) }.to raise_error 'Plane already grounded.'
end

it 'raises error when plane tries to land during storm' do
allow(subject).to receive(:storm?).and_return true
expect { subject.land(plane) }.to raise_error 'Weather stormy, landing not available.'
end

it 'should not raise an error for landing planes when weather clear' do
allow(subject).to receive(:storm?).and_return false
expect { subject.land(plane) }.to_not raise_error
end

# Didn't get around to looking futher into this edge case
# it 'should raise error if grounded plane tries to land in different airport.' do
# subject.land(plane)
# expect { airport.land(plane) }.to raise_error 'Plane in another airport.'
# end
end

describe '#take_off' do
it { is_expected.to respond_to(:take_off).with(1).argument }

it 'should remove plane from airport' do
allow(subject).to receive(:storm?).and_return false
subject.land(plane)
expect(subject.take_off(plane)).to eq plane
end

it 'should remove the plane requested from airport' do
allow(airport).to receive(:storm?).and_return false
airport.land(plane)
airport.land(plane2)
airport.take_off(plane)
expect(airport.hangar).not_to include(plane)
end

it 'raises an error when there are no planes in the hangar' do
expect { subject.take_off(plane) }.to raise_error 'No planes in hangar.'
end

it 'should raise error if flying plane tries take_off again.' do
allow(subject).to receive(:storm?).and_return false
subject.land(plane)
expect { subject.take_off(plane2) }.to raise_error 'Plane not in hangar.'
end

it 'raises error when plane tries to take off during storm' do
allow(subject).to receive(:storm?).and_return false
subject.land(plane)
allow(subject).to receive(:storm?).and_return true
expect { subject.take_off(plane) }.to raise_error 'Weather stormy, take off not available.'
end

it 'should not raise an error on plane take off when weather clear' do
allow(subject).to receive(:storm?).and_return false
subject.land(plane)
expect { subject.take_off(plane) }.to_not raise_error
end
end
end
65 changes: 65 additions & 0 deletions spec/feature_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require_relative '../lib/airport.rb'
# require_relative '../lib/weather.rb'


# Haven't looked into feature tests yet so just recording irb tests.
# Use the drop-downs and remove hashes from code you wish to try out.
# Please do this one at a time!
# Rogue weather spells may hit during tests...

airport1 = Airport.new
airport2 = Airport.new
airport3 = Airport.new(2)

plane1 = Plane.new
plane2 = Plane.new
plane3 = Plane.new

weather1 = Weather.new

# [X] Plane can land (store plane) & take off (return plane)
# p airport1.land(plane1)
# p airport1.take_off(plane1)

# [X] Remove plane from hangar when taking off
# p airport1.land(plane1)
# p airport1
# p airport1.take_off(plane1)
# p airport1

# [X] Default capacity of 10 planes vs. setting capacity at 2
# p airport1
# p airport3

# [X] Error when hangar is full
# p airport3.land(plane1)
# p airport3.land(plane2)
# p airport3.land(plane3)

# [X] Error when hangar empty
# p airport1.take_off(plane1)

# [X] Error if grounded plane tries landing in same airport again
# p airport1.land(plane1)
# p airport1.land(plane1)

# [ ] Error if grounded plane tries landing in another airport
# p airport1.land(plane1)
# p airport2.land(plane1)

# [X] Planes can only take off from airport they are in
# p airport1.land(plane1)
# p airport2.land(plane2)
# p airport2.take_off(plane1)

# [X] landing/takeoff error if weather is stormy
# p airport1.land(plane1)
# p airport1.take_off(plane1)

# [X] Check if weather / storm method works
# p weather1.storm?
# p weather1.storm?
# p weather1.storm?
# p weather1.storm?
# p weather1.storm?
# p weather1.storm?
Loading