use crate::tests::sdd::*;
track_file!("ref/asciidoctor/test/test_helper.rb");
non_normative!(
r#"
# frozen_string_literal: true
ASCIIDOCTOR_TEST_DIR = File.absolute_path __dir__
ASCIIDOCTOR_LIB_DIR = ENV['ASCIIDOCTOR_LIB_DIR'] || (File.join ASCIIDOCTOR_TEST_DIR, '../lib')
require 'simplecov' if ENV['COVERAGE'] == 'true'
require File.join ASCIIDOCTOR_LIB_DIR, 'asciidoctor'
Dir.chdir Asciidoctor::ROOT_DIR
require 'nokogiri'
# NOTE rouge has all sorts of warnings we don't want to see, so silence them
proc do
old_verbose, $VERBOSE = $VERBOSE, nil
require 'rouge'
$VERBOSE = old_verbose
end.call
require 'socket'
require 'tempfile'
require 'tmpdir'
autoload :FileUtils, 'fileutils'
autoload :Pathname, 'pathname'
RE_XMLNS_ATTRIBUTE = / xmlns="[^"]+"/
RE_DOCTYPE = /\s*<!DOCTYPE (.*)/
require 'minitest/autorun'
# Minitest 4 doesn't have Minitest::Test
Minitest::Test = MiniTest::Unit::TestCase unless defined? Minitest::Test
"#
);
non_normative!(
r#"
class Minitest::Test
def jruby?
RUBY_ENGINE == 'jruby'
end
def self.jruby_9_1_windows?
RUBY_ENGINE == 'jruby' && windows? && (JRUBY_VERSION.start_with? '9.1.')
end
def jruby_9_1_windows?
Minitest::Test.jruby_9_1_windows?
end
def self.windows?
/mswin|msys|mingw/.match? RbConfig::CONFIG['host_os']
end
def windows?
Minitest::Test.windows?
end
def disk_root
%(#{windows? ? (Asciidoctor::ROOT_DIR.partition '/')[0] : ''}/)
end
def empty_document options = {}
options[:parse] ? (Asciidoctor::Document.new [], options).parse : (Asciidoctor::Document.new [], options)
end
def empty_safe_document options = {}
Asciidoctor::Document.new [], (options.merge safe: :safe)
end
def sample_doc_path name
unless (name = name.to_s).include? '.'
%w(adoc asciidoc txt).each do |ext|
if File.exist? fixture_path %(#{name}.#{ext})
name = %(#{name}.#{ext})
break
end
end
end
fixture_path name
end
def bindir
File.join Asciidoctor::ROOT_DIR, 'bin'
end
def testdir
ASCIIDOCTOR_TEST_DIR
end
def fixturedir
File.join testdir, 'fixtures'
end
def fixture_path name
File.join fixturedir, name
end
def example_document name, opts = {}
document_from_string (File.read (sample_doc_path name), mode: Asciidoctor::FILE_READ_MODE), opts
end
def xmlnodes_at_css css, content, count = nil
xmlnodes_at_path :css, css, content, count
end
def xmlnodes_at_xpath xpath, content, count = nil
xmlnodes_at_path :xpath, xpath, content, count
end
def xmlnodes_at_path type, path, content, count = nil
doc = xmldoc_from_string content
case type
when :xpath
namespaces = (doc.respond_to? :root) ? doc.root.namespaces : {}
results = doc.xpath (path.sub '/', './'), namespaces
when :css
results = doc.css path
end
count == 1 ? results.first : results
end
# Generate an xpath attribute matcher that matches a name in the class attribute
def contains_class name
%(contains(concat(' ', normalize-space(@class), ' '), ' #{name} '))
end
def assert_css css, content, count = nil
assert_path :css, css, content, count
end
def assert_xpath xpath, content, count = nil
assert_path :xpath, xpath, content, count
end
def assert_path type, path, content, count = nil
case type
when :xpath
type_name = 'XPath'
when :css
type_name = 'CSS'
end
results = xmlnodes_at_path type, path, content
if count == true || count == false
unless count == results
flunk %(#{type_name} #{path} yielded #{results} rather than #{count} for:\n#{content})
end
elsif count && results.size != count
flunk %(#{type_name} #{path} yielded #{results.size} elements rather than #{count} for:\n#{content})
elsif count.nil? && results.empty?
flunk %(#{type_name} #{path} not found in:\n#{content})
end
end
def assert_include expected, actual
assert_includes actual, expected
end
def refute_include not_expected, actual
refute_includes actual, not_expected
end
def assert_message logger, severity, expected_message, kind = String, idx = nil
unless idx
assert_equal 1, logger.messages.size
idx = 0
end
message = logger.messages[idx]
assert_equal severity, message[:severity]
assert_kind_of kind, message[:message]
if kind == String
actual_message = message[:message]
else
refute_nil message[:message][:source_location]
actual_message = message[:message].inspect
end
if expected_message.start_with? '~'
assert_includes actual_message, expected_message[1..-1]
else
assert_equal expected_message, actual_message
end
end
def assert_messages logger, expected_messages
assert_equal expected_messages.size, logger.messages.size
expected_messages.each_with_index do |expected_message_details, idx|
severity, expected_message, kind = expected_message_details
assert_message logger, severity, expected_message, (kind || String), idx
end
end
def xmldoc_from_string content
if (content.start_with? '<?xml ') || (RE_XMLNS_ATTRIBUTE.match? content)
Nokogiri::XML::Document.parse content
elsif RE_DOCTYPE !~ content
Nokogiri::HTML::DocumentFragment.parse content
elsif $1.start_with? 'html'
Nokogiri::HTML::Document.parse content
else
Nokogiri::XML::Document.parse content
end
end
def document_from_string src, opts = {}
assign_default_test_options opts
opts[:parse] ? (Asciidoctor::Document.new src.lines, opts).parse : (Asciidoctor::Document.new src.lines, opts)
end
def block_from_string src, opts = {}
(document_from_string src, (opts.merge standalone: false)).blocks.first
end
def convert_string src, opts = {}
keep_namespaces = opts.delete :keep_namespaces
if keep_namespaces
(document_from_string src, opts).convert
else
# this is required because nokogiri is easily confused by namespaces
result = (document_from_string src, opts).convert
result ? (result.sub RE_XMLNS_ATTRIBUTE, '') : result
end
end
def convert_string_to_embedded src, opts = {}
(document_from_string src, (opts.merge standalone: false)).convert
end
def convert_inline_string src, opts = {}
(document_from_string src, (opts.merge doctype: :inline)).convert
end
def parse_header_metadata source, doc = nil
reader = Asciidoctor::Reader.new source.split Asciidoctor::LF
[(Asciidoctor::Parser.parse_header_metadata reader, doc), reader]
end
def assign_default_test_options opts
opts[:standalone] = true unless opts.key? :standalone
opts[:parse] = true unless opts.key? :parse
if opts[:standalone]
# don't embed stylesheet unless test requests the default behavior
if opts.key? :linkcss_default
opts.delete :linkcss_default
else
opts[:attributes] ||= {}
opts[:attributes]['linkcss'] = ''
end
end
if (template_dir = ENV['TEMPLATE_DIR'])
opts[:template_dir] = template_dir unless opts.key? :template_dir
end
nil
end
# Decode the numeric character reference, such as 8212, to a Unicode glyph
# so it may be used in an XPath expression.
#
# Examples
#
# decode_char 60
# # => "<"
#
# Returns the decoded String that corresponds to the numeric character reference
def decode_char number
[number].pack 'U1'
end
def haml_template_class
(defined? Haml::Template) ? Haml::Template : Tilt::HamlTemplate
end
"#
);
non_normative!(
r#"
def invoke_cli_with_filenames argv = [], filenames = [], &block
filepaths = []
filenames.each do |filename|
if filenames.nil? || (Pathname.new filename).absolute?
filepaths << filename
else
filepaths << (fixture_path filename)
end
end
invoker = Asciidoctor::Cli::Invoker.new argv + filepaths
invoker.invoke!(&block)
invoker
end
def invoke_cli_to_buffer argv = [], filename = 'sample.adoc', &block
invoke_cli argv, filename, [StringIO.new, StringIO.new], &block
end
def invoke_cli argv = [], filename = 'sample.adoc', buffers = nil, &block
if filename.nil? || filename == '-' || (Pathname.new filename).absolute?
filepath = filename
else
filepath = fixture_path filename
end
invoker = Asciidoctor::Cli::Invoker.new argv + [filepath]
invoker.redirect_streams(*buffers) if buffers
invoker.invoke!(&block)
invoker
end
def redirect_streams
old_stdout, $stdout = $stdout, StringIO.new
old_stderr, $stderr = $stderr, StringIO.new
old_logger = Asciidoctor::LoggerManager.logger
old_logger_level = old_logger.level
new_logger = (Asciidoctor::LoggerManager.logger = Asciidoctor::Logger.new $stderr)
new_logger.level = old_logger_level
yield $stdout, $stderr
ensure
$stdout, $stderr = old_stdout, old_stderr
Asciidoctor::LoggerManager.logger = old_logger
end
def resolve_localhost
Socket.ip_address_list.find(&:ipv4?).ip_address
end
def using_memory_logger level = nil
old_logger = Asciidoctor::LoggerManager.logger
memory_logger = Asciidoctor::MemoryLogger.new
memory_logger.level = level if level
begin
Asciidoctor::LoggerManager.logger = memory_logger
yield memory_logger
ensure
Asciidoctor::LoggerManager.logger = old_logger
end
end
def in_verbose_mode
begin
old_logger_level, Asciidoctor::LoggerManager.logger.level = Asciidoctor::LoggerManager.logger.level, Logger::Severity::DEBUG
yield
ensure
Asciidoctor::LoggerManager.logger.level = old_logger_level
end
end
def asciidoctor_cmd ruby_args = nil
[Gem.ruby, *ruby_args, (File.join bindir, 'asciidoctor')]
end
# NOTE run_command fails on JRuby 9.1 for Windows with the following error:
# Java::JavaLang::ClassCastException at org.jruby.util.ShellLauncher.getModifiedEnv(ShellLauncher.java:271)
def run_command cmd, *args, &block
if Array === cmd
args.unshift(*cmd)
cmd = args.shift
end
kw_args = Hash === args[-1] ? args.pop : {}
env = kw_args[:env]
(env ||= {})['RUBYOPT'] = nil unless kw_args[:use_bundler]
# JRuby 9.1 on Windows doesn't support popen options; therefore, test cannot capture / assert on stderr
opts = jruby_9_1_windows? ? {} : { err: [:child, :out] }
if env
# NOTE while JRuby 9.2.10.0 implements support for unsetenv_others, it doesn't work in child
#if jruby? && (Gem::Version.new JRUBY_VERSION) < (Gem::Version.new '9.2.10.0')
if jruby?
begin
env = (old_env = ENV.to_h).merge env
env.each {|key, val| env.delete key if val.nil? } if env.value? nil
ENV.replace env
popen [cmd, *args, opts], &block
ensure
ENV.replace old_env
end
elsif env.value? nil
env = env.each_with_object ENV.to_h do |(key, val), accum|
val.nil? ? (accum.delete key) : (accum[key] = val)
end
popen [env, cmd, *args, (opts.merge unsetenv_others: true)], &block
else
popen [env, cmd, *args, opts], &block
end
else
popen [cmd, *args, opts], &block
end
end
def popen args, &block
# When block is passed to IO.popen, JRuby for Windows does not return value of block as return value
if jruby? && windows?
result = nil
IO.popen args do |io|
result = yield io
end
result
else
IO.popen args, &block
end
end
def using_test_webserver host = resolve_localhost, port = 9876
base_dir = testdir
server = TCPServer.new host, port
server_thread = Thread.start do
Thread.current[:requests] = requests = []
while (session = server.accept)
requests << (request = session.gets)
if /^GET (\S+) HTTP\/1\.1$/ =~ request.chomp
resource = (resource = $1) == '' ? '.' : resource
else
session.print %(HTTP/1.1 405 Method Not Allowed\r\nContent-Type: text/plain\r\n\r\n)
session.print %(405 - Method not allowed\n)
session.close
next
end
if resource == '/name/asciidoctor'
session.print %(HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n)
session.print %({"name": "asciidoctor"}\n)
elsif File.file?(resource_file = (File.join base_dir, resource))
mimetype = if (ext = File.extname(resource_file)[1..-1])
ext == 'adoc' ? 'text/plain' : %(image/#{ext})
else
'text/plain'
end
session.print %(HTTP/1.1 200 OK\r\nContent-Type: #{mimetype}\r\n\r\n)
File.open resource_file, Asciidoctor::FILE_READ_MODE do |fd|
session.write fd.read 256 until fd.eof?
end
else
session.print %(HTTP/1.1 404 File Not Found\r\nContent-Type: text/plain\r\n\r\n)
session.print %(404 - Resource not found.\n)
end
session.close
end
end
begin
yield %(http://#{host}:#{port}), server_thread
ensure
server_thread.exit
server_thread.value
server.close
end
end
end
"#
);
non_normative!(
r#"
###
#
# Context goodness provided by @citrusbyte's contest.
# See https://github.com/citrusbyte/contest
#
###
# Contest adds +teardown+, +test+ and +context+ as class methods, and the
# instance methods +setup+ and +teardown+ now iterate on the corresponding
# blocks. Note that all setup and teardown blocks must be defined with the
# block syntax. Adding setup or teardown instance methods defeats the purpose
# of this library.
class Minitest::Test
class << self
def setup &block
define_method :setup do
super(&block)
instance_eval(&block)
end
end
def teardown &block
define_method :teardown do
instance_eval(&block)
super(&block)
end
end
def context name, opts = {}, &block
if opts.key? :if
return unless opts[:if]
elsif opts.key? :unless
return if opts[:unless]
end
subclass = Class.new self
remove_tests subclass
subclass.class_eval(&block) if block_given?
const_set (context_name name), subclass
end
def test name, opts = {}, &block
if opts.key? :if
return unless opts[:if]
elsif opts.key? :unless
return if opts[:unless]
end
define_method (test_name name), &block
end
def remove_tests subclass
subclass.public_instance_methods.each do |m|
subclass.send :undef_method, m if m.to_s.start_with? 'test_'
end
end
alias should test
alias describe context
private
def context_name name
%(Test#{(sanitize_name name).gsub(/(^| )(\w)/) { $2.upcase }}).to_sym
end
def test_name name
%(test_#{(sanitize_name name).gsub %r/\s+/, '_'}).to_sym
end
def sanitize_name name
(name.gsub %r/\W+/, ' ').strip
end
end
end
"#
);
non_normative!(
r#"
def context name, &block
Minitest::Test.context name, &block
end
"#
);