summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authora14m <[email protected]>2020-12-14 22:03:57 +0000
committera14m <[email protected]>2020-12-14 22:03:57 +0000
commit50a9f841d6b27a0c842bf5127940d4989a621d10 (patch)
tree0eef3a3f5618a0e7e71164e74db1e49d187fdd92
parent628e5e81596892cadfd5b5dea46001556b6a7d49 (diff)
parentb710263617e019e4ffed3dbafcc6795f760bb151 (diff)
Merge branch 'feature/code-coverage' into 'master'
Feature/code coverage See merge request a14m/game-of-life!10
-rw-r--r--.codecov.yml24
-rw-r--r--.gitlab-ci.yml6
-rw-r--r--CLI/ruby/.gitlab-ci.yml7
-rw-r--r--CLI/ruby/.rubocop.yml12
-rw-r--r--CLI/ruby/CHANGELOG.md1
-rw-r--r--CLI/ruby/Gemfile.lock17
-rw-r--r--CLI/ruby/game_of_life.gemspec4
-rw-r--r--CLI/ruby/spec/game_of_life/cell_spec.rb31
-rw-r--r--CLI/ruby/spec/game_of_life/plane_spec.rb20
-rw-r--r--CLI/ruby/spec/game_of_life_spec.rb65
-rw-r--r--CLI/ruby/spec/spec_helper.rb9
-rw-r--r--README.md1
12 files changed, 149 insertions, 48 deletions
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..0854495
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,24 @@
+codecov:
+ require_ci_to_pass: yes
+
+coverage:
+ status:
+ project:
+ only_pulls: false
+ default:
+ target: 90%
+ ruby:
+ target: 95%
+ flags:
+ - ruby
+
+comment:
+ layout: "reach,diff,flags,files,footer"
+ behavior: default
+ require_changes: no
+
+flags:
+ ruby:
+ paths:
+ - CLI/ruby/
+ carryforward: true
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4926962..28e0986 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,6 +6,6 @@ ruby:
trigger:
include: CLI/ruby/.gitlab-ci.yml
strategy: depend
- only:
- changes:
- - CLI/ruby/*
+ rules:
+ - changes:
+ - CLI/ruby/**/*
diff --git a/CLI/ruby/.gitlab-ci.yml b/CLI/ruby/.gitlab-ci.yml
index d573ce5..6fdf07e 100644
--- a/CLI/ruby/.gitlab-ci.yml
+++ b/CLI/ruby/.gitlab-ci.yml
@@ -1,9 +1,4 @@
image: ruby:2.7
-stages:
- - build
- - test
- - lint
- - release
variables:
BUNDLE_PATH: .bundle/vendor
@@ -58,7 +53,7 @@ lint:
- bundle exec rubocop
release:
- stage: release
+ stage: deploy
needs: ["test:2.7", "test:2.6", "test:2.5", lint]
rules:
- if: $CI_COMMIT_TAG =~ /^ruby\/v.*$/
diff --git a/CLI/ruby/.rubocop.yml b/CLI/ruby/.rubocop.yml
index 970b547..a84ca99 100644
--- a/CLI/ruby/.rubocop.yml
+++ b/CLI/ruby/.rubocop.yml
@@ -1,3 +1,6 @@
+require:
+ - rubocop-rspec
+
AllCops:
TargetRubyVersion: 2.5
NewCops: enable
@@ -10,6 +13,7 @@ Layout/LineLength:
Metrics/BlockLength:
Exclude:
+ - "game_of_life.gemspec"
- "spec/**/*"
Naming/MethodParameterName:
@@ -46,3 +50,11 @@ Style/TrailingCommaInArrayLiteral:
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: comma
+RSpec/ExampleLength:
+ Max: 12
+
+RSpec/MessageSpies:
+ EnforcedStyle: receive
+
+RSpec/MultipleExpectations:
+ Max: 4
diff --git a/CLI/ruby/CHANGELOG.md b/CLI/ruby/CHANGELOG.md
index 48aad2a..3f6f77d 100644
--- a/CLI/ruby/CHANGELOG.md
+++ b/CLI/ruby/CHANGELOG.md
@@ -6,6 +6,7 @@
- Fix `game-of-life --delay -10` error with kernel sleep
- Fix `game-of-life --width -10` error with generated universe width
- Fix `game-of-life --height -10` error with generated universe height
+- Add coverage report
## 1.0.0
- Add ruby testing matrix for versions 2.5, 2.6 and 2.7
diff --git a/CLI/ruby/Gemfile.lock b/CLI/ruby/Gemfile.lock
index 002d5c4..1cd8a09 100644
--- a/CLI/ruby/Gemfile.lock
+++ b/CLI/ruby/Gemfile.lock
@@ -9,8 +9,13 @@ GEM
specs:
ast (2.4.1)
byebug (11.1.3)
+ codecov (0.2.12)
+ json
+ simplecov
coderay (1.1.3)
diff-lcs (1.4.4)
+ docile (1.3.2)
+ json (2.3.1)
method_source (1.0.0)
parallel (1.20.1)
parser (2.7.2.0)
@@ -50,7 +55,16 @@ GEM
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (1.2.0)
parser (>= 2.7.1.5)
+ rubocop-rspec (2.0.1)
+ rubocop (~> 1.0)
+ rubocop-ast (>= 1.1.0)
ruby-progressbar (1.10.1)
+ simplecov (0.20.0)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.2)
thor (1.0.1)
unicode-display_width (1.7.0)
yard (0.9.25)
@@ -60,11 +74,14 @@ PLATFORMS
DEPENDENCIES
bundler
+ codecov
pry-byebug
rake
redcarpet
rspec
rubocop
+ rubocop-rspec
+ simplecov
terminal_game_of_life!
yard
diff --git a/CLI/ruby/game_of_life.gemspec b/CLI/ruby/game_of_life.gemspec
index 9f88e68..b59ea88 100644
--- a/CLI/ruby/game_of_life.gemspec
+++ b/CLI/ruby/game_of_life.gemspec
@@ -31,6 +31,10 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
spec.add_development_dependency "rubocop"
+ spec.add_development_dependency "rubocop-rspec"
+ # Code Coverage dependencies
+ spec.add_development_dependency "codecov"
+ spec.add_development_dependency "simplecov"
# Documentation dependencies
spec.add_development_dependency "redcarpet"
spec.add_development_dependency "yard"
diff --git a/CLI/ruby/spec/game_of_life/cell_spec.rb b/CLI/ruby/spec/game_of_life/cell_spec.rb
index 00f55b3..088bdae 100644
--- a/CLI/ruby/spec/game_of_life/cell_spec.rb
+++ b/CLI/ruby/spec/game_of_life/cell_spec.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+# rubocop:disable RSpec/MultipleSubjects
+# rubocop:disable RSpec/EmptyLineAfterSubject
RSpec.describe GameOfLife::Cell do
subject(:living_cell) { described_class.new(x: nil, y: nil, alive: true) }
subject(:dead_cell) { described_class.new(x: nil, y: nil, alive: false) }
@@ -20,6 +22,8 @@ RSpec.describe GameOfLife::Cell do
end
end
+ # rubocop:disable RSpec/SubjectStub
+ # rubocop:disable RSpec/StubbedMock
describe "#evolve!" do
it "calls #living_neighbors with param: neighbors" do
neighbors = [1, 2, 3, 4, 5]
@@ -29,17 +33,18 @@ RSpec.describe GameOfLife::Cell do
context "when dead" do
it "stays dead" do
- expect(dead_cell).to receive(:living_neighbors).with([]).and_return 0
+ allow(dead_cell).to receive(:living_neighbors).with([]).and_return 0
+ expect(dead_cell).to receive(:living_neighbors)
next_generation = dead_cell.evolve!([])
expect(next_generation.object_id).to eq dead_cell.object_id
- expect(next_generation.dead?).to be_truthy
+ expect(next_generation).to be_dead
end
it "becomes alive when having 3 living neighbors" do
expect(dead_cell).to receive(:living_neighbors).with([]).and_return 3
next_generation = dead_cell.evolve!([])
expect(next_generation.object_id).not_to eq dead_cell.object_id
- expect(next_generation.alive?).to be_truthy
+ expect(next_generation).to be_alive
end
end
@@ -48,40 +53,42 @@ RSpec.describe GameOfLife::Cell do
expect(living_cell).to receive(:living_neighbors).with([]).and_return 2
next_generation = living_cell.evolve!([])
expect(next_generation.object_id).to eq living_cell.object_id
- expect(next_generation.alive?).to be_truthy
+ expect(next_generation).to be_alive
end
it "survives when having 3 neighbors" do
expect(living_cell).to receive(:living_neighbors).with([]).and_return 3
next_generation = living_cell.evolve!([])
expect(next_generation.object_id).to eq living_cell.object_id
- expect(next_generation.alive?).to be_truthy
+ expect(next_generation).to be_alive
end
it "dies when having less than 2 neighbors" do
expect(living_cell).to receive(:living_neighbors).with([]).and_return 1
next_generation = living_cell.evolve!([])
expect(next_generation.object_id).not_to eq living_cell.object_id
- expect(next_generation.dead?).to be_truthy
+ expect(next_generation).to be_dead
end
it "dies when having more than 3 neighbors" do
expect(living_cell).to receive(:living_neighbors).with([]).and_return 4
next_generation = living_cell.evolve!([])
expect(next_generation).not_to eq living_cell
- expect(next_generation.dead?).to be_truthy
+ expect(next_generation).to be_dead
end
end
end
+ # rubocop:enable RSpec/SubjectStub
+ # rubocop:enable RSpec/StubbedMock
describe "#alive?" do
- it { expect(living_cell.alive?).to be_truthy }
- it { expect(dead_cell.alive?).to be_falsy }
+ it { expect(living_cell).to be_alive }
+ it { expect(dead_cell).not_to be_alive }
end
describe "#dead?" do
- it { expect(living_cell.dead?).to be_falsy }
- it { expect(dead_cell.dead?).to be_truthy }
+ it { expect(living_cell).not_to be_dead }
+ it { expect(dead_cell).to be_dead }
end
describe "#living_neighbors" do
@@ -134,3 +141,5 @@ RSpec.describe GameOfLife::Cell do
end
end
end
+# rubocop:enable RSpec/MultipleSubjects
+# rubocop:enable RSpec/EmptyLineAfterSubject
diff --git a/CLI/ruby/spec/game_of_life/plane_spec.rb b/CLI/ruby/spec/game_of_life/plane_spec.rb
index c4efbb4..88ce0dc 100644
--- a/CLI/ruby/spec/game_of_life/plane_spec.rb
+++ b/CLI/ruby/spec/game_of_life/plane_spec.rb
@@ -3,36 +3,36 @@
RSpec.describe GameOfLife::Plane do
describe "#[]" do
context "when empty" do
- subject { described_class.new }
+ subject(:cyclic_array) { described_class.new }
it "returns nil with [-1]" do
- expect(subject[-1]).to be_nil
+ expect(cyclic_array[-1]).to be_nil
end
it "returns nil with [0]" do
- expect(subject[0]).to be_nil
+ expect(cyclic_array[0]).to be_nil
end
it "returns nil with [1]" do
- expect(subject[1]).to be_nil
+ expect(cyclic_array[1]).to be_nil
end
end
context "when not empty" do
- subject { described_class.new([0, 1, 2, 3]) }
+ subject(:cyclic_array) { described_class.new([0, 1, 2, 3]) }
it "returns value at index" do
- expect(subject[0]).to eq 0
+ expect(cyclic_array[0]).to eq 0
end
it "returns value at -index (negative indices count backward from the end of the array)" do
- expect(subject[-5]).to eq 3
- expect(subject[-1]).to eq 3
+ expect(cyclic_array[-5]).to eq 3
+ expect(cyclic_array[-1]).to eq 3
end
it "returns value at any index (indices bigger than size cycle again from the beginning)" do
- expect(subject[5]).to eq 1
- expect(subject[9]).to eq 1
+ expect(cyclic_array[5]).to eq 1
+ expect(cyclic_array[9]).to eq 1
end
end
end
diff --git a/CLI/ruby/spec/game_of_life_spec.rb b/CLI/ruby/spec/game_of_life_spec.rb
index 00b58b8..5e99ca8 100644
--- a/CLI/ruby/spec/game_of_life_spec.rb
+++ b/CLI/ruby/spec/game_of_life_spec.rb
@@ -2,65 +2,79 @@
RSpec.describe GameOfLife do
describe ".parsed_options" do
- context "options['delay']" do
- it "raises GameOfLife::Error when negative delay" do
+ before do
+ console = instance_double("<File:/dev/tty>")
+ allow(IO).to receive(:console).and_return(console)
+ allow(console).to receive(:winsize).and_return([10, 20])
+ end
+
+ context "with options['delay']" do
+ it "raises GameOfLife::Error when negative" do
options = { "delay" => -13 }
expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --delay value/)
end
- it "uses options['delay'] when provided" do
+ it "uses options['delay'] when posotive" do
options = { "delay" => 15 }
expect(described_class.parsed_options(options)).to include("delay" => 15)
end
end
- context "options['width']" do
- it "raises GameOfLife::Error when width < 1" do
+ context "with options['width']" do
+ it "raises GameOfLife::Error when < 1" do
options = { "width" => 0 }
expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --width value/)
end
- it "raises GameOfLife::Error when width > IO.console.winsize[1]" do
+ it "raises GameOfLife::Error when > IO.console.winsize[1]" do
options = { "width" => 23 }
- allow(IO).to receive_message_chain(:console, :winsize).and_return([10, 20])
expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --width value/)
end
- it "uses options['width'] when provided" do
+ it "uses options['width']" do
options = { "width" => 15 }
- allow(IO).to receive_message_chain(:console, :winsize).and_return([10, 20])
expect(described_class.parsed_options(options)).to include("width" => 15)
end
it "uses IO.console.winsize[1] as default" do
- allow(IO).to receive_message_chain(:console, :winsize).and_return([10, 20])
expect(described_class.parsed_options({})).to include("width" => 20)
end
end
- context "options['height']" do
- it "raises GameOfLife::Error when height < 1" do
+ context "with options['height']" do
+ it "raises GameOfLife::Error < 1" do
options = { "height" => 0 }
expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --height value/)
end
- it "raises GameOfLife::Error when height > winsize[0] - 2" do
+ it "raises GameOfLife::Error when > winsize[0] - 2" do
options = { "height" => 9 }
- allow(IO).to receive_message_chain(:console, :winsize).and_return([10, 20])
expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --height value/)
end
- it "uses options['height'] when provided" do
+ it "uses options['height']" do
options = { "height" => 5 }
- allow(IO).to receive_message_chain(:console, :winsize).and_return([10, 20])
expect(described_class.parsed_options(options)).to include("height" => 5)
end
it "uses IO.console.winsize[0] - 2 as default" do
- allow(IO).to receive_message_chain(:console, :winsize).and_return([10, 20])
expect(described_class.parsed_options({})).to include("height" => 8)
end
end
+
+ context "with options['seed']" do
+ it "uses options['seed']" do
+ options = { "seed" => 1337 }
+ expect(described_class.parsed_options(options)).to include("seed" => 1337)
+ end
+
+ # rubocop:disable RSpec/AnyInstance
+ it "defaults to Kernel.random" do
+ allow_any_instance_of(Kernel).to receive(:rand).and_return "random"
+ expect(described_class.parsed_options({})).to include("seed" => "random")
+ end
+ # rubocop:enable RSpec/AnyInstance
+ end
end
describe ".generate" do
@@ -134,5 +148,20 @@ RSpec.describe GameOfLife do
end
end
- describe.pending ".render"
+ describe ".render" do
+ let(:universe) { GameOfLife::Universe.new(width: 3, height: 3) }
+
+ # rubocop:disable RSpec/AnyInstance
+ it "renders the game of life presentation" do
+ stub_const("BANNER", "")
+ stub_const("DELAY", 100.0)
+ expect_any_instance_of(Kernel).to receive(:puts).with("\33c\e[3J")
+ expect_any_instance_of(Kernel).to receive(:puts).twice
+ expect(universe).to receive(:to_s)
+ expect_any_instance_of(Kernel).to receive(:sleep)
+
+ described_class.render(universe)
+ end
+ # rubocop:enable RSpec/AnyInstance
+ end
end
diff --git a/CLI/ruby/spec/spec_helper.rb b/CLI/ruby/spec/spec_helper.rb
index 890ca34..e7feb44 100644
--- a/CLI/ruby/spec/spec_helper.rb
+++ b/CLI/ruby/spec/spec_helper.rb
@@ -1,5 +1,14 @@
# frozen_string_literal: true
+require "simplecov"
+SimpleCov.start do
+ add_filter "/spec/"
+ enable_coverage :branch
+end
+
+require "codecov"
+SimpleCov.formatter = SimpleCov::Formatter::Codecov if ENV["CODECOV_TOKEN"]
+
require "bundler/setup"
require "game_of_life"
diff --git a/README.md b/README.md
index caa030b..e456aaf 100644
--- a/README.md
+++ b/README.md
@@ -5,5 +5,6 @@ in the following languages
[![pipeline status](https://gitlab.com/a14m/game-of-life/badges/master/pipeline.svg)](https://gitlab.com/a14m/game-of-life/-/commits/master)
[![license](https://img.shields.io/badge/license-MIT-brightgreenA)](https://gitlab.com/a14m/game-of-life/-/blob/master/LICENSE.md)
+[![codecov](https://codecov.io/gl/a14m/game-of-life/branch/master/graph/badge.svg?token=8KQ6LN5E6Z)](https://codecov.io/gl/a14m/game-of-life)
- [Ruby](./CLI/ruby) [![Gem Version](https://badge.fury.io/rb/terminal_game_of_life.svg)](https://badge.fury.io/rb/terminal_game_of_life)