diff --git a/README.md b/README.md index 6dd4fa6bc9..95ed12f124 100644 --- a/README.md +++ b/README.md @@ -12,27 +12,35 @@ Airport Challenge = ===(_________) ``` +# Airport Challenge -Instructions ---------- +This Airport challenge allows users to instruct a plane to land and take off from an airport, but only if the weather is sunny! -* 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 +## Getting started -Steps -------- +- First step - Fork this repo and clone it to your local machine +- Second step - Run the command gem install bundler (if you don't have bundler already) +- Third step - When the installation completes, run bundle -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: -Task ------ +## How to use -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: +Now, I know you're excited to get your pilot's hat on, but please read these instructions first, which will walk you through how to test my program. + +- Open your terminal and navigate to the Airport_challenge directory +- Run irb and require './lib/airport.rb' +- You can create plane instances (plane = Plane.new) and airport instances (airport = Airport.new). +- To instruct a plane to take off from an airport you can use the 'takeoff' method (airport.takeoff(plane)) +- To instruct a plane to land you can use the 'land' method (airport.land(plane)) +## Some things to note +- The airport's default capacity is set to 10 planes, so if you would like to change the airports default capacity, you can pass your desired capacity as an integer to the airport instance eg (airport = Airport.new(20)) +- You will only be able to land and take-off planes if the weather is sunny. Luckily, the weather is sunny 75% of the time, and stormy the other 25%. + +## Running tests + +- If you would like to run my unit tests, run rspec in the Airport_challenge directory. + +## User stories ``` As an air traffic controller @@ -59,31 +67,3 @@ 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. diff --git a/lib/airport.rb b/lib/airport.rb new file mode 100644 index 0000000000..d3d33fdbbe --- /dev/null +++ b/lib/airport.rb @@ -0,0 +1,33 @@ +require_relative '../lib/weather.rb' +require_relative '../lib/plane.rb' + +class Airport + attr_reader :planes, :capacity + DEFAULT_CAPACITY = 10 + + def initialize(capacity = DEFAULT_CAPACITY, weather: Weather.new) + @planes = [] + @capacity = capacity + @weather = weather + end + + def land(plane) + fail "Plane is not in the air" if plane.in_flight? + fail 'Due to stormy weather no landing allowed' if stormy? + fail 'Airport is full no landing allowed' if @planes.count >= DEFAULT_CAPACITY + plane.in_flight = false + @planes << plane + end + + def takeoff(plane) + fail 'Due to stormy weather no landing allowed' if stormy? + fail 'Plane is not at the airport' if plane.in_flight? + plane.in_flight = true + @planes.delete(plane) + 'plane has taken off' + end + + def stormy? + @weather.stormy + end +end diff --git a/lib/plane.rb b/lib/plane.rb new file mode 100644 index 0000000000..edad113829 --- /dev/null +++ b/lib/plane.rb @@ -0,0 +1,11 @@ +class Plane + attr_accessor :in_flight + + def initialize(in_flight = false) + @in_flight = in_flight + end + + def in_flight? + @in_flight + end +end diff --git a/lib/weather.rb b/lib/weather.rb new file mode 100644 index 0000000000..179d590325 --- /dev/null +++ b/lib/weather.rb @@ -0,0 +1,13 @@ +class Weather + def stormy + random_outlook == :stormy + end + + private + + OUTLOOKS = [:stormy, :fine, :fine, :fine] + + def random_outlook + OUTLOOKS.sample + end +end diff --git a/spec/airport_spec.rb b/spec/airport_spec.rb new file mode 100644 index 0000000000..9cd13a9c9a --- /dev/null +++ b/spec/airport_spec.rb @@ -0,0 +1,66 @@ +require_relative '../lib/airport.rb' + +describe Airport do + let(:plane) { Plane.new } + let(:airport) { Airport.new } + let(:weather) { Weather.new } + + + describe '# landing' do + + it 'has a Default capcity' do + expect(airport.capacity).to eq(Airport::DEFAULT_CAPACITY) + end + + it '--to be prevented when airport is full' do + allow(airport).to receive(:stormy?).and_return false + Airport::DEFAULT_CAPACITY.times { airport.land Plane.new } + expect { airport.land Plane.new }.to raise_error 'Airport is full no landing allowed' + end + + it 'is prevented due to stormy weather' do + allow(airport).to receive(:stormy?).and_return true + expect { airport.land plane }.to raise_error "Due to stormy weather no landing allowed" + end + + it 'raise an error when plane is not in the air before landing' do + allow(airport).to receive(:stormy?).and_return false + plane = Plane.new(true) + expect { airport.land plane }.to raise_error "Plane is not in the air" + end + it 'lands multiple planes' do # bonus points + allow(airport).to receive(:stormy?).and_return false + 5.times { airport.land Plane.new } + expect { airport.land Plane.new }.not_to raise_error + end + end + + describe '#take-offs' do + it { is_expected.to respond_to(:takeoff).with(1).argument } + + it 'informs that the plane has taken off' do + allow(airport).to receive(:stormy?).and_return false + airport.planes << plane + expect(airport.takeoff plane).to eq 'plane has taken off' + end + it 'gets weather status' do + allow(airport).to receive(:stormy?).and_return false + expect(weather.stormy).to be(true).or be(false) + end + it 'is prevented due to stormy weather' do + allow(airport).to receive(:stormy?).and_return true + expect { airport.takeoff plane }.to raise_error "Due to stormy weather no landing allowed" + end + + it 'raise an error when plane is not in the airport before taking off' do + allow(airport).to receive(:stormy?).and_return false + plane.in_flight = true + expect { airport.takeoff plane }.to raise_error "Plane is not at the airport" + end + it 'multiple planes' do # bonus points + allow(airport).to receive(:stormy?).and_return false + 5.times { airport.land Plane.new } + expect { airport.takeoff plane }.not_to raise_error + end + end +end diff --git a/spec/plane_spec.rb b/spec/plane_spec.rb new file mode 100644 index 0000000000..f6cf0bf0ac --- /dev/null +++ b/spec/plane_spec.rb @@ -0,0 +1,4 @@ +require_relative '../lib/plane.rb' + +describe Plane do +end diff --git a/spec/weather_spec.rb b/spec/weather_spec.rb new file mode 100644 index 0000000000..e0a521ba5e --- /dev/null +++ b/spec/weather_spec.rb @@ -0,0 +1,19 @@ +require_relative '../lib/weather.rb' + +describe Weather do + let(:weather) { Weather.new } + + it { is_expected.to respond_to(:stormy) } + + describe '#tells the weather condition' do + it "is sunny" do + allow(weather).to receive(:random_outlook).and_return(:fine) + expect(weather.stormy).to eq false + end + + it "is stormy" do + allow(weather).to receive(:random_outlook).and_return(:stormy) + expect(weather.stormy).to eq true + end + end +end