Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add main formatter #1

Merged
merged 2 commits into from
Aug 21, 2024
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
.rspec_status
/vendor/
/.env
/.byebug_history
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ gem 'rspec', '~> 3.0'

gem 'rubocop', '~> 1.21'
gem 'rubocop-rspec', require: false

gem 'byebug'
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ PATH
remote: .
specs:
simplecov-inline (0.1.0)
rainbow (~> 3.1)
simplecov (~> 0.22)

GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
byebug (11.1.3)
diff-lcs (1.5.1)
docile (1.4.0)
json (2.7.2)
Expand Down Expand Up @@ -66,6 +68,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
byebug
rake (~> 13.0)
rspec (~> 3.0)
rubocop (~> 1.21)
Expand Down
1 change: 1 addition & 0 deletions lib/simplecov/inline.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require_relative 'inline/version'
require_relative 'inline/formatter'

module SimpleCov
module Inline
Expand Down
101 changes: 101 additions & 0 deletions lib/simplecov/inline/formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
require 'rainbow'

module Simplecov
module Inline
class Formatter
Result = Struct.new(:file, :start_line, :end_line, :type) do
def to_s
lines = [start_line, end_line].uniq.join('-')
"#{file}:#{lines} (#{type})"
end
end

Config = Struct.new(:files) do
def no_output!(reason:)
@no_output = reason
end

attr_reader :no_output
end

def self.reset_config = @config = Config.new
reset_config

def self.config(&block)
return @config if block.nil?

block.call(@config)

return if @config.files.nil?

@config.files = @config.files.to_set.freeze
end

def format(result, putter: Kernel)
missing_coverage = process_files(filtered_files(result:).reject { |file| fully_covered?(file) })
success_message = build_success_message(missing_coverage:)

if success_message
putter.puts success_message
return
end

putter.puts
putter.puts Rainbow('Missing coverage:').yellow
putter.puts Rainbow(missing_coverage).yellow
putter.puts
end

private

def build_success_message(missing_coverage:)
if self.class.config.no_output
return Rainbow("Coverage output skipped. Reason: #{self.class.config.no_output}.").yellow
end

return Rainbow("All branches covered (#{human_filter} files) ✔ ").green if missing_coverage.empty?

nil
end

def fully_covered?(file)
[file.covered_percent, file.branches_coverage_percent].all? { |coverage| coverage == 100 }
end

def human_filter
self.class.config.files&.length || 'all'
end

def filtered_files(result:)
filter = self.class.config.files

return result.files if filter.nil?

result.files.filter { |file| filter.include?(file.filename) }
end

def process_files(files)
files.map do |file|
[
build_line_coverage(file),
build_branch_coverage(file),
]
end.flatten.compact.join("\n")
end

def build_line_coverage(file)
file.missed_lines.map do |uncovered|
# uncovered is a SimpleCov::SourceFile::Line
Result.new(file.project_filename, uncovered.line_number, uncovered.line_number, 'line')
end
end

def build_branch_coverage(file)
file.missed_branches.map do |uncovered|
# uncovered is a SimpleCov::SourceFile::Branch
Result.new(file.project_filename, uncovered.report_line, uncovered.report_line, "branch:#{uncovered.type}")
end
end
end
end
end
1 change: 1 addition & 0 deletions simplecov-inline.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_dependency 'rainbow', '~> 3.1'
spec.add_dependency 'simplecov', '~> 0.22'

# For more information and examples about making a new gem, check out our
Expand Down
5 changes: 5 additions & 0 deletions spec/fixtures/file1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class File1
def method(arg)
arg ? 1 : 2
end
end
5 changes: 5 additions & 0 deletions spec/fixtures/file2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class File2
def method(arg)
arg ? 1 : 2
end
end
103 changes: 103 additions & 0 deletions spec/lib/simplecov/inline/formatter_spec.rb
Copy link
Member Author

@gondalez gondalez Aug 21, 2024

Choose a reason for hiding this comment

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

btw see spec_helper, simplecov is configured and set to enforce 100% branching coverage 😌

It is run by the github action here: https://github.com/appbot/simplecov-inline/actions/runs/10483006312/job/29034996680?pr=1

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
RSpec.describe Simplecov::Inline::Formatter do
after { described_class.reset_config }

describe '#format' do
subject { described_class.new.format(result, putter:) }

let(:putter) { class_double(Kernel) }

before { allow(putter).to receive(:puts) }

context 'missing coverage' do
let(:result) do
SimpleCov::Result.new({
File.join(fixture_directory, 'file1.rb') => {
# 0 means line 3 is uncovered
'lines' => [1, 1, 0, 1, 1],
# => 0 means the else branch is uncovered.
# 2 is an idenditifier, 3 is the start line, 12 is the start col, 3 is the end line and 14 is the end col.
'branches' => {[:if, 0, 3, 4, 3, 8] => {[:then, 1, 3, 8, 3, 10] => 1, [:else, 2, 3, 12, 3, 14] => 0}},
},
})
end

it 'prints a yellow message saying coverage is missing' do
subject

expect(putter).to have_received(:puts).with(Rainbow('Missing coverage:').yellow)
end

it 'prints the missing lines and branches in yellow' do
subject

expect(putter).to have_received(:puts).with(Rainbow([
'/spec/fixtures/file1.rb:3 (line)',
'/spec/fixtures/file1.rb:3 (branch:else)',
].join("\n")).yellow)
end
end

context 'fully covered' do
let(:result) do
SimpleCov::Result.new({
File.join(fixture_directory, 'file1.rb') => {
'lines' => [1, 1, 1, 1, 1],
'branches' => {[:if, 0, 3, 4, 3, 8] => {[:then, 1, 3, 8, 3, 10] => 1, [:else, 2, 3, 12, 3, 14] => 1}},
},
})
end

it 'prints a green message saying fully covered' do
subject
expect(putter).to have_received(:puts).with(Rainbow('All branches covered (all files) ✔ ').green)
end
end

context 'output disabled' do
let(:result) { SimpleCov::Result.new({}) }

before do
described_class.config { |config| config.no_output!(reason: 'no output thanks') }
end

it 'prints a yellow message saying output is disabled' do
subject
expect(putter).to have_received(:puts).with(
Rainbow('Coverage output skipped. Reason: no output thanks.').yellow,
)
end
end

context 'filtering out of uncovered files' do
let(:result) do
SimpleCov::Result.new({
File.join(fixture_directory, 'file1.rb') => {'lines' => [1, 1, 1, 1, 1]},
File.join(fixture_directory, 'file2.rb') => {'lines' => [1, 1, 0, 1, 1]},
})
end

before do
described_class.config { |config| config.files = [File.join(fixture_directory, 'file1.rb')] }
end

it 'prints a green message saying fully covered, but notes only 1 file was considered' do
subject
expect(putter).to have_received(:puts).with(Rainbow('All branches covered (1 files) ✔ ').green)
end
end
end

describe '.config' do
subject { described_class.config }

it { is_expected.to have_attributes files: nil }
it { is_expected.to respond_to :no_output! }

it 'can change the files' do
expect { described_class.config { |config| config.files = ['test.txt'] } }
.to change { described_class.config.files }
.from(nil)
.to(['test.txt'])
end
end
end
3 changes: 3 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'byebug'
require 'simplecov'

SimpleCov.start do
Expand All @@ -19,3 +20,5 @@
c.syntax = :expect
end
end

def fixture_directory = @fixture_directory ||= File.join(File.dirname(__FILE__), 'fixtures')
Loading