Compare commits

...

5 Commits

Author SHA1 Message Date
Parker Moore
29d1f21df7 specs: use subject 2016-03-31 13:48:00 -07:00
Parker Moore
07c8e3e333 Try to get --trace working properly. 2016-03-31 13:43:56 -07:00
Parker Moore
b0c42090ea Add 2 more errors
- ConversionError
- LiquidRenderError
2016-03-31 13:43:56 -07:00
Parker Moore
de8547b208 Add 5 errors for config, render, doctor errors
- UnhealthySiteError is thrown when a site doesn’t pass all the `jekyll
doctor` tests
- InvalidConfigurationError is thrown if a configuration file returns
an invalid value (e.g. false) or when the configuration has set a value
that requires human intervention to change (e.g. using maruku and
setting to kramdown would cause problems so site needs to be audited
for upgrade to work properly
- InvalidFileFormatError is thrown when a non-.toml/.y(a)ml file is
given as a config file to be read in. It may be used later for invalid
source files.
- FileNotFoundError is thrown when a configuration file could not be
found. It will be used later for other errors, including trying to
include a file that’s not there.
- IncorrectDependencyError is thrown when a dependency is activated
that is not a valid version, e.g. a version of rouge that is < 1.3.0 is
activated.

Specs are also added.
2016-03-31 13:43:56 -07:00
Parker Moore
4db922d95e Add rspec to script/test and enable script/spec 2016-03-31 13:43:56 -07:00
17 changed files with 203 additions and 33 deletions

View File

@@ -1,6 +1,8 @@
module Jekyll
class Command
class << self
attr_accessor :trace
# A list of subclasses of Jekyll::Command
def subclasses
@subclasses ||= []
@@ -26,8 +28,15 @@ module Jekyll
site.process
rescue Jekyll::Errors::FatalException => e
Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:"
Jekyll.logger.error "", "------------------------------------"
Jekyll.logger.error "", "------------------------------------------"
Jekyll.logger.error "", e.message
if self.class.trace
Jekyll.logger.error "", "------------------------------------------"
Jekyll.logger.error "Stacktrace:", e.backtrace.join("\n")
else
Jekyll.logger.error "", ""
Jekyll.logger.error "", "Run jekyll with --trace for more information."
end
exit(1)
end

View File

@@ -13,6 +13,7 @@ module Jekyll
c.action do |_, options|
options["serving"] = false
self.class.trace = c.trace
Jekyll::Commands::Build.process(options)
end
end

View File

@@ -23,7 +23,7 @@ module Jekyll
if healthy?(site)
Jekyll.logger.info "Your test results", "are in. Everything looks fine."
else
abort
raise Jekyll::Errors::UnhealthySiteError
end
end

View File

@@ -33,6 +33,7 @@ module Jekyll
cmd.action do |_, opts|
opts["serving"] = true
opts["watch" ] = true unless opts.key?("watch")
self.class.trace = cmd.trace
Build.process(opts)
Serve.process(opts)
end

View File

@@ -2,6 +2,8 @@
module Jekyll
class Configuration < Hash
attr_accessor :default_config_file
# Default options. Overridden by values in _config.yml.
# Strings rather than symbols are used for compatibility with YAML.
DEFAULTS = Configuration[{
@@ -74,6 +76,10 @@ module Jekyll
}
}]
def self.from(other_hash = {})
Jekyll::Utils.deep_merge_hashes DEFAULTS, other_hash
end
# Public: Turn all keys into string
#
# Return a copy of the hash where all its keys are strings
@@ -112,7 +118,8 @@ module Jekyll
when /\.ya?ml/i
SafeYAML.load_file(filename) || {}
else
raise ArgumentError, "No parser for '#{filename}' is available. Use a .toml or .y(a)ml file instead."
raise Jekyll::Errors::InvalidFileFormatError,
"No parser for '#{filename}' is available. Use a .toml or .y(a)ml file instead."
end
end
@@ -132,7 +139,7 @@ module Jekyll
File.exist?(Jekyll.sanitized_path(source(override), "_config.#{ext}"))
end
config_files = Jekyll.sanitized_path(source(override), "_config.#{default}")
@default_config_file = true
self.default_config_file = true
end
config_files = [config_files] unless config_files.is_a? Array
config_files
@@ -148,13 +155,13 @@ module Jekyll
check_config_is_hash!(next_config, file)
Jekyll.logger.info "Configuration file:", file
next_config
rescue SystemCallError
if @default_config_file
rescue SystemCallError, Errno::ENOENT
if default_config_file
Jekyll.logger.warn "Configuration file:", "none"
{}
else
Jekyll.logger.error "Fatal:", "The configuration file '#{file}' could not be found."
raise LoadError, "The Configuration file '#{file}' could not be found."
raise Jekyll::Errors::FileNotFoundError,
"The configuration file '#{file}' could not be found."
end
end
@@ -246,7 +253,7 @@ module Jekyll
end
if config.fetch('markdown', 'kramdown').to_s.downcase.eql?("maruku")
Jekyll.logger.abort_with "Error:", "You're using the 'maruku' " \
raise Jekyll::Errors::InvalidConfigurationError, "You're using the 'maruku' " \
"Markdown processor, which has been removed as of 3.0.0. " \
"We recommend you switch to Kramdown. To do this, replace " \
"`markdown: maruku` with `markdown: kramdown` in your " \
@@ -317,7 +324,8 @@ module Jekyll
# Raises an ArgumentError if given config is not a hash
def check_config_is_hash!(extracted_config, file)
unless extracted_config.is_a?(Hash)
raise ArgumentError.new("Configuration file: (INVALID) #{file}".yellow)
raise Jekyll::Errors::InvalidConfigurationError,
"The configuration file '#{file}' is invalid: it is not a Hash."
end
end
end

View File

@@ -76,7 +76,8 @@ module Jekyll
))
unless Gem::Version.new(Rouge.version) > Gem::Version.new("1.3.0")
abort "Please install Rouge 1.3.0 or greater and try running Jekyll again."
raise Jekyll::Errors::IncorrectDependencyError,
"Please install Rouge 1.3.0 or greater and try running Jekyll again."
end
include Rouge::Plugins::Redcarpet

View File

@@ -79,9 +79,9 @@ module Jekyll
begin
converter.convert output
rescue => e
Jekyll.logger.error "Conversion error:", "#{converter.class} encountered an error while converting '#{path}':"
Jekyll.logger.error("", e.to_s)
raise e
raise Jekyll::Errors::ConversionError, "" \
"#{converter.class} encountered an error while converting '#{path}':" \
"\n#{e.to_s}"
end
end
end
@@ -112,11 +112,17 @@ module Jekyll
def render_liquid(content, payload, info, path)
site.liquid_renderer.file(path).parse(content).render!(payload, info)
rescue Tags::IncludeTagError => e
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || self.path}"
raise e
rescue Exception => e
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || self.path}"
raise e
raise Jekyll::Errors::LiquidRenderError,
"Liquid exception in #{e.path} included in #{path || self.path}:" \
"\n#{e.message}"
rescue Liquid::Error => e
raise Jekyll::Errors::LiquidRenderError,
"Liquid exception on line #{e.line_number} in #{path || self.path}:" \
"\n#{e.message}"
rescue => e
raise Jekyll::Errors::LiquidRenderError,
"Liquid exception in #{path || self.path}:" \
"\n#{e.message}"
end
# Convert this Convertible's data to a Hash suitable for use by Liquid.

View File

@@ -2,11 +2,26 @@ module Jekyll
module Errors
FatalException = Class.new(::RuntimeError)
# Errors for commands
UnhealthySiteError = Class.new(FatalException)
# Configuration errors
InvalidConfigurationError = Class.new(FatalException)
InvalidFileFormatError = Class.new(FatalException)
# IO Errors
FileNotFoundError = Class.new(FatalException)
# Render errors
DropMutationException = Class.new(FatalException)
InvalidPermalinkError = Class.new(FatalException)
InvalidYAMLFrontMatterError = Class.new(FatalException)
MissingDependencyException = Class.new(FatalException)
MissingDependencyError = Class.new(FatalException)
IncorrectDependencyError = Class.new(FatalException)
ConversionError = Class.new(FatalException)
LiquidRenderError = Class.new(FatalException)
# post_url tag errors
InvalidDateError = Class.new(FatalException)
InvalidPostNameError = Class.new(FatalException)
PostURLError = Class.new(FatalException)

View File

@@ -98,7 +98,11 @@ module Jekyll
#
# Returns the formatted message
def message(topic, message)
msg = formatted_topic(topic) + message.to_s.gsub(/\s+/, ' ')
lines = message.to_s.split("\n")
first_line = formatted_topic(topic) + lines.shift.to_s
msg = ([first_line] + lines.map {|line|
formatted_topic("") + line.gsub(/[ ]+/, ' ')
}).join("\n")
messages << msg
msg
end

View File

@@ -107,11 +107,17 @@ module Jekyll
def render_liquid(content, payload, info, path = nil)
site.liquid_renderer.file(path).parse(content).render!(payload, info)
rescue Tags::IncludeTagError => e
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || document.relative_path}"
raise e
rescue Exception => e
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || document.relative_path}"
raise e
raise Jekyll::Errors::LiquidRenderError,
"Liquid exception in #{e.path} included in #{path || self.path}:" \
"\n#{e.message}"
rescue Liquid::Error => e
raise Jekyll::Errors::LiquidRenderError,
"Liquid exception on line #{e.line_number} in #{path || self.path}:" \
"\n#{e.message}"
rescue => e
raise Jekyll::Errors::LiquidRenderError,
"Liquid exception in #{path || self.path}:" \
"\n#{e.message}"
end
# Checks if the layout specified in the document actually exists

View File

@@ -300,11 +300,12 @@ module Jekyll
# Returns
def relative_permalinks_are_deprecated
if config['relative_permalinks']
Jekyll.logger.abort_with "Since v3.0, permalinks for pages" \
" in subfolders must be relative to the" \
" site source directory, not the parent" \
" directory. Check http://jekyllrb.com/docs/upgrading/"\
" for more info."
raise Jekyll::Errors::InvalidConfigurationError,
"Since v3.0, permalinks for pages" \
" in subfolders must be relative to the" \
" site source directory, not the parent" \
" directory. Check http://jekyllrb.com/docs/upgrading/"\
" for more info."
end
end

2
script/spec Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
bundle exec rspec --color --require spec_helper $@

View File

@@ -48,9 +48,13 @@ for ruby in $rubies; do
set -x
time $ruby -S bundle exec \
rake TESTOPTS='--profile' test
time $ruby -S bundle exec rspec --color \
--require spec_helper
else
set -x
time $ruby -S bundle exec ruby -Itest \
"$@" --profile
time $ruby -S bundle exec rspec --color \
--require spec_helper --profile
fi
done

View File

@@ -0,0 +1,37 @@
RSpec.describe(Jekyll::Configuration) do
subject { described_class.from({}) }
describe "#safe_load_file" do
it "throws an InvalidFileFormat on a bad extension" do
filename = "my_config.json"
expect(-> { subject.safe_load_file(filename) }).to raise_error(
Jekyll::Errors::InvalidFileFormatError,
"No parser for '#{filename}' is available. Use a .toml or .y(a)ml file instead."
)
end
end
describe "#read_config_file" do
it "throws a FileNotFoundError if the file doesn't exist" do
filename = "_config.yml"
expect(-> { subject.read_config_file(filename) }).to raise_error(
Jekyll::Errors::FileNotFoundError,
"The configuration file '#{filename}' could not be found."
)
end
end
describe "#check_config_is_hash!" do
it "does not throw if the input is a hash" do
expect(subject.send(:check_config_is_hash!, {}, "_config.yml")).to eq(nil)
end
it "throws an InvalidConfigurationError if the input is not a hash" do
filename = "_config.yml"
expect(-> { subject.send(:check_config_is_hash!, false, filename) }).to raise_error(
Jekyll::Errors::InvalidConfigurationError,
"The configuration file '#{filename}' is invalid: it is not a Hash."
)
end
end
end

View File

@@ -0,0 +1,7 @@
require 'spec_helper'
RSpec.describe(Jekyll::Errors) do
it "defines the base exception, FatalException" do
expect(described_class).to be_const_defined(:FatalException)
end
end

66
spec/spec_helper.rb Normal file
View File

@@ -0,0 +1,66 @@
ROOT = Pathname.new(File.expand_path("../", __dir__))
require ROOT.join("lib", "jekyll.rb")
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
# config.example_status_persistence_file_path = "spec/examples.txt"
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
config.disable_monkey_patching!
# This setting enables warnings. It's recommended, but in some cases may
# be too noisy due to issues in dependencies.
config.warnings = true
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = 'doc'
end
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
end

View File

@@ -174,13 +174,15 @@ class TestConfiguration < JekyllUnitTest
allow(SafeYAML).to receive(:load_file).with(@path).and_return(Array.new)
allow($stderr).to receive(:puts).and_return(("WARNING: ".rjust(20) + "Error reading configuration. Using defaults (and options).").yellow)
allow($stderr).to receive(:puts).and_return("Configuration file: (INVALID) #{@path}".yellow)
assert_equal @@defaults, Jekyll.configuration({})
assert_raises Jekyll::Errors::InvalidConfigurationError do
Jekyll.configuration({})
end
end
should "fire warning when user-specified config file isn't there" do
allow(SafeYAML).to receive(:load_file).with(@user_config) { raise SystemCallError, "No such file or directory - #{@user_config}" }
allow($stderr).to receive(:puts).with(("Fatal: ".rjust(20) + "The configuration file '#{@user_config}' could not be found.").red)
assert_raises LoadError do
assert_raises Jekyll::Errors::FileNotFoundError do
Jekyll.configuration({'config' => [@user_config]})
end
end