Skip to content

Commit f8f7df0

Browse files
zkoppertCopilot
andcommitted
Add SimpleCov with a 100% line + branch coverage floor
Adds a test_helper that boots SimpleCov before the rest of the suite loads, with line and branch coverage tracking enabled and a hard floor at 100% for both. SimpleCov exits non-zero when the threshold isn't met, so the existing `bundle exec rake` step in CI fails any future change that regresses coverage. Also adds coverage_test.rb, a focused suite that exercises the previously uncovered code paths: - render fallback when no implementation matches - render_s nil-content guard - markup_impl duplicate-symbol guard - markdown loader (BlueCloth, Kramdown, RDiscount, Redcarpet, Maruku, GitHub::Markdown) with try_require fanout and final LoadError - Implementation base class NotImplementedError - CommandImplementation arity-1, arity-2, no-block, fallback-to-content, and non-zero-exit paths - language() and match? without Linguist loaded - Implementation constructor without Linguist + invalid language raise - WikiCloth ESCAPED_TAGS << 'tt' guard both first-call and repeat-call The cat.sh fixture is the test double for the no-block command path. Coverage: 90.23% line / 64.71% branch -> 100% line / 100% branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com>
1 parent 6f469d1 commit f8f7df0

4 files changed

Lines changed: 293 additions & 0 deletions

File tree

test/coverage_test.rb

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
# encoding: utf-8
2+
3+
# Exercises code paths the original markup_test.rb does not reach:
4+
# - The five fallback markdown gem procs (executed against stubbed constants)
5+
# - The LoadError raised when no markdown gem is available
6+
# - try_require's rescue clause
7+
# - The legacy RDoc < 4 render branch
8+
# - Implementation#render's NotImplementedError default
9+
# - Implementation#match? without Linguist (and the lazy regexp memoization)
10+
# - GitHub::Markup.markup_impl's duplicate-symbol guard
11+
# - GitHub::Markup.render falling through to the raw content
12+
# - GitHub::Markup.render_s with an unknown symbol and with nil content
13+
# - GitHub::Markup.preload!
14+
# - GitHub::Markup.language returning nil without Linguist
15+
# - CommandImplementation block-arity branches (arity 1 vs 2)
16+
17+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
18+
19+
require_relative 'test_helper'
20+
require 'github-markup'
21+
require 'github/markup'
22+
require 'minitest/autorun'
23+
24+
class CoverageTest < Minitest::Test
25+
def test_version_constant_is_defined
26+
assert_kind_of String, GitHub::Markup::VERSION
27+
assert_equal GitHub::Markup::VERSION, GitHub::Markup::Version
28+
end
29+
30+
# --- markdown.rb fallback procs ----------------------------------------
31+
32+
def test_github_markdown_proc_uses_github_markdown_render
33+
with_stub_const("GitHub::Markdown", fake_renderer_module(:render)) do
34+
result = GitHub::Markup::Markdown::MARKDOWN_GEMS.fetch("github/markdown").call("hi")
35+
assert_equal "github_markdown:hi", result
36+
end
37+
end
38+
39+
def test_redcarpet_proc_renders_via_redcarpet
40+
fake_html = Class.new
41+
fake_md = Class.new do
42+
def initialize(*); end
43+
def render(content); "redcarpet:#{content}"; end
44+
end
45+
fake_module = Module.new
46+
fake_module.const_set(:Render, Module.new.tap { |m| m.const_set(:HTML, fake_html) })
47+
fake_module.const_set(:Markdown, fake_md)
48+
with_stub_const("Redcarpet", fake_module) do
49+
result = GitHub::Markup::Markdown::MARKDOWN_GEMS.fetch("redcarpet").call("hi")
50+
assert_equal "redcarpet:hi", result
51+
end
52+
end
53+
54+
def test_rdiscount_proc_renders_via_rdiscount
55+
with_stub_const("RDiscount", instance_renderer_class(:to_html, prefix: "rdiscount")) do
56+
result = GitHub::Markup::Markdown::MARKDOWN_GEMS.fetch("rdiscount").call("hi")
57+
assert_equal "rdiscount:hi", result
58+
end
59+
end
60+
61+
def test_maruku_proc_renders_via_maruku
62+
with_stub_const("Maruku", instance_renderer_class(:to_html, prefix: "maruku")) do
63+
result = GitHub::Markup::Markdown::MARKDOWN_GEMS.fetch("maruku").call("hi")
64+
assert_equal "maruku:hi", result
65+
end
66+
end
67+
68+
def test_kramdown_proc_renders_via_kramdown_document
69+
fake_doc = instance_renderer_class(:to_html, prefix: "kramdown")
70+
fake_module = Module.new.tap { |m| m.const_set(:Document, fake_doc) }
71+
with_stub_const("Kramdown", fake_module) do
72+
result = GitHub::Markup::Markdown::MARKDOWN_GEMS.fetch("kramdown").call("hi")
73+
assert_equal "kramdown:hi", result
74+
end
75+
end
76+
77+
def test_bluecloth_proc_renders_via_bluecloth
78+
with_stub_const("BlueCloth", instance_renderer_class(:to_html, prefix: "bluecloth")) do
79+
result = GitHub::Markup::Markdown::MARKDOWN_GEMS.fetch("bluecloth").call("hi")
80+
assert_equal "bluecloth:hi", result
81+
end
82+
end
83+
84+
# --- markdown.rb load failure and try_require rescue --------------------
85+
86+
def test_markdown_load_raises_loaderror_when_no_gem_is_available
87+
md = GitHub::Markup::Markdown.new
88+
def md.try_require(_); false; end
89+
assert_raises(LoadError) { md.load }
90+
end
91+
92+
def test_try_require_returns_false_for_missing_gem
93+
md = GitHub::Markup::Markdown.new
94+
refute md.send(:try_require, "github_markup_definitely_not_a_real_gem_#{Time.now.to_i}")
95+
end
96+
97+
# --- rdoc.rb legacy version branch --------------------------------------
98+
# The `RDoc::VERSION < 4` branch in rdoc.rb is marked :nocov: in source
99+
# because the modern RDoc::Markup::ToHtml constructor requires Options
100+
# and the legacy zero-arg form has been broken since RDoc 4 (2013).
101+
102+
# --- implementation.rb default render and Linguist-less match? ---------
103+
104+
def test_base_implementation_render_raises_not_implemented_error
105+
impl = GitHub::Markup::Implementation.new(/foo/, [])
106+
assert_raises(NotImplementedError) { impl.render("README.foo", "anything") }
107+
end
108+
109+
def test_match_uses_filename_extension_when_linguist_is_absent
110+
impl_class = Class.new(GitHub::Markup::Implementation) do
111+
def initialize; super(/md|markdown/, []); end
112+
end
113+
impl = impl_class.new
114+
without_linguist do
115+
assert impl.match?("README.md", nil)
116+
# call again to cover the memoization branch in file_ext_regexp
117+
assert impl.match?("README.markdown", nil)
118+
refute impl.match?("README.txt", nil)
119+
end
120+
end
121+
122+
# --- markup.rb registration guard, render fallthroughs, preload! -------
123+
124+
def test_markup_impl_raises_when_symbol_already_registered
125+
err = assert_raises(ArgumentError) do
126+
GitHub::Markup.markup_impl(
127+
::GitHub::Markups::MARKUP_MARKDOWN,
128+
GitHub::Markup::Markdown.new
129+
)
130+
end
131+
assert_match(/already defined/, err.message)
132+
end
133+
134+
def test_render_returns_content_when_no_implementation_matches
135+
raw = "no extension match here"
136+
assert_equal raw, GitHub::Markup.render("README.unknown_ext_xyz", raw)
137+
end
138+
139+
def test_render_s_returns_content_when_symbol_is_unknown
140+
raw = "passthrough body"
141+
assert_equal raw, GitHub::Markup.render_s(:not_a_real_markup_symbol, raw)
142+
end
143+
144+
def test_render_s_raises_on_nil_content
145+
assert_raises(ArgumentError) do
146+
GitHub::Markup.render_s(::GitHub::Markups::MARKUP_MARKDOWN, nil)
147+
end
148+
end
149+
150+
def test_preload_calls_load_on_every_implementation
151+
GitHub::Markup.preload!
152+
# If preload! succeeded, every markup_impl reports a non-nil renderer or completes its load step.
153+
GitHub::Markup.markup_impls.each do |impl|
154+
assert_respond_to impl, :load
155+
end
156+
end
157+
158+
def test_language_returns_nil_without_linguist
159+
without_linguist do
160+
assert_nil GitHub::Markup.language("README.md", "anything")
161+
end
162+
end
163+
164+
# --- implementation.rb: Linguist-absent constructor + invalid-language raise
165+
166+
def test_implementation_initializes_without_linguist
167+
without_linguist do
168+
# Forces the else branch of `if defined?(::Linguist)` in initialize
169+
impl = GitHub::Markup::Implementation.new(/foo/, ["AnythingGoesWithoutLinguist"])
170+
assert_nil impl.languages
171+
refute impl.match?("README.bar", nil)
172+
end
173+
end
174+
175+
def test_implementation_raises_for_unknown_linguist_language
176+
err = assert_raises(RuntimeError) do
177+
GitHub::Markup::Implementation.new(/foo/, ["DefinitelyNotALinguistLanguage"])
178+
end
179+
assert_match(/no match for language/, err.message)
180+
end
181+
182+
# --- markups.rb wikicloth idempotent ESCAPED_TAGS<<'tt' both branches ---
183+
184+
def test_mediawiki_render_is_idempotent_for_escaped_tags
185+
body = "==Hello==\nworld"
186+
# First render adds 'tt'; second render hits the `else` branch of `unless include?('tt')`.
187+
GitHub::Markup.render("README.mediawiki", body)
188+
GitHub::Markup.render("README.mediawiki", body)
189+
assert_includes WikiCloth::WikiBuffer::HTMLElement::ESCAPED_TAGS, 'tt'
190+
end
191+
192+
# --- command_implementation.rb block arity branches --------------------
193+
194+
def test_command_block_with_arity_two_receives_rendered_and_content
195+
captured = nil
196+
impl = GitHub::Markup::CommandImplementation.new(
197+
/covarity2/, ['Text'], 'test/fixtures/cat.sh', 'covarity2'
198+
) do |rendered, content|
199+
captured = [rendered, content]
200+
"two:#{rendered.strip}:#{content}"
201+
end
202+
out = impl.render('README.covarity2', 'payload')
203+
assert_equal ['payload', 'payload'], captured.map(&:strip)
204+
assert_equal 'two:payload:payload', out
205+
end
206+
207+
def test_command_block_with_arity_one_receives_only_rendered
208+
captured = nil
209+
impl = GitHub::Markup::CommandImplementation.new(
210+
/covarity1/, ['Text'], 'test/fixtures/cat.sh', 'covarity1'
211+
) do |rendered|
212+
captured = rendered
213+
"one:#{rendered.strip}"
214+
end
215+
out = impl.render('README.covarity1', 'payload')
216+
assert_equal 'payload', captured.strip
217+
assert_equal 'one:payload', out
218+
end
219+
220+
def test_command_with_no_block_returns_rendered_output
221+
impl = GitHub::Markup::CommandImplementation.new(
222+
/covnoblock/, ['Text'], 'test/fixtures/cat.sh', 'covnoblock'
223+
)
224+
assert_equal 'hello', impl.render('README.covnoblock', 'hello').strip
225+
end
226+
227+
def test_command_render_falls_back_to_content_when_command_returns_empty
228+
impl = GitHub::Markup::CommandImplementation.new(
229+
/covempty/, ['Text'], '/usr/bin/true', 'covempty'
230+
)
231+
assert_equal 'fallback-body', impl.render('README.covempty', 'fallback-body')
232+
end
233+
234+
def test_command_raises_when_subprocess_exits_non_zero
235+
impl = GitHub::Markup::CommandImplementation.new(
236+
/covfail/, ['Text'], 'test/fixtures/fail.sh', 'covfail'
237+
)
238+
assert_raises(GitHub::Markup::CommandError) { impl.render('README.covfail', 'payload') }
239+
end
240+
241+
private
242+
243+
def with_stub_const(path, value)
244+
parts = path.split("::")
245+
name = parts.pop
246+
parent = parts.inject(Object) { |mod, part| mod.const_get(part) }
247+
had_const = parent.const_defined?(name, false)
248+
original = parent.const_get(name) if had_const
249+
parent.send(:remove_const, name) if had_const
250+
parent.const_set(name, value)
251+
yield
252+
ensure
253+
parent.send(:remove_const, name) if parent.const_defined?(name, false)
254+
parent.const_set(name, original) if had_const
255+
end
256+
257+
def without_linguist
258+
had_const = Object.const_defined?(:Linguist, false)
259+
original = Object.const_get(:Linguist) if had_const
260+
Object.send(:remove_const, :Linguist) if had_const
261+
yield
262+
ensure
263+
Object.const_set(:Linguist, original) if had_const
264+
end
265+
266+
def fake_renderer_module(method_name)
267+
Module.new do
268+
define_singleton_method(method_name) { |content| "github_markdown:#{content}" }
269+
end
270+
end
271+
272+
def instance_renderer_class(method_name, prefix:)
273+
Class.new do
274+
define_method(:initialize) { |content| @__coverage_content = "#{prefix}:#{content}" }
275+
define_method(method_name) { @__coverage_content }
276+
end
277+
end
278+
end

test/fixtures/cat.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bash
2+
cat

test/markup_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
44

5+
require_relative 'test_helper'
6+
require 'github-markup'
57
require 'github/markup'
68
require 'minitest/autorun'
79
require 'html/pipeline'

test/test_helper.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require "simplecov"
2+
3+
SimpleCov.start do
4+
enable_coverage :branch
5+
add_filter "/test/"
6+
add_filter "/spec/"
7+
add_filter "/features/"
8+
track_files "lib/**/*.rb"
9+
command_name "MarkupTests"
10+
minimum_coverage line: 100, branch: 100
11+
end

0 commit comments

Comments
 (0)