Compare commits

..

1 Commits

Author SHA1 Message Date
Maarten van Grootel
d7e7285fcb Fix leaking error when comparison leads to TypeError 2017-01-16 12:26:19 -05:00
26 changed files with 108 additions and 261 deletions

View File

@@ -19,10 +19,6 @@ matrix:
allow_failures:
- rvm: jruby-head
install:
- gem install rainbow -v 2.2.1
- bundle install
script: "bundle exec rake"
notifications:

View File

@@ -1,14 +1,11 @@
source 'https://rubygems.org'
gemspec
gem 'stackprof', platforms: :mri
group :benchmark, :test do
gem 'benchmark-ips'
end
gem 'stackprof', platforms: :mri_21
group :test do
gem 'spy', '0.4.1'
gem 'benchmark-ips'
gem 'rubocop', '0.34.2'
platform :mri do

View File

@@ -20,13 +20,10 @@
* Add concat filter to concatenate arrays (#429) [Diogo Beato]
* Ruby 1.9 support dropped (#491) [Justin Li]
* Liquid::Template.file_system's read_template_file method is no longer passed the context. (#441) [James Reid-Smith]
* Remove `liquid_methods` (See https://github.com/Shopify/liquid/pull/568 for replacement)
* Remove support for `liquid_methods`
* Liquid::Template.register_filter raises when the module overrides registered public methods as private or protected (#705) [Gaurav Chande]
### Fixed
* Fix variable names being detected as an operator when starting with contains (#788) [Michael Angell]
* Fix include tag used with strict_variables (#828) [QuickPay]
* Fix map filter when value is a Proc (#672) [Guillaume Malette]
* Fix truncate filter when value is not a string (#672) [Guillaume Malette]
* Fix behaviour of escape filter when input is nil (#665) [Tanel Jakobsoo]

View File

@@ -1,7 +1,5 @@
module Liquid
class Block < Tag
MAX_DEPTH = 100
def initialize(tag_name, markup, options)
super
@blank = true
@@ -50,25 +48,17 @@ module Liquid
protected
def parse_body(body, tokens)
if parse_context.depth >= MAX_DEPTH
raise StackLevelError, "Nesting too deep".freeze
end
parse_context.depth += 1
begin
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
@blank &&= body.blank?
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
@blank &&= body.blank?
return false if end_tag_name == block_delimiter
unless end_tag_name
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
end
# this tag is not registered with the system
# pass it to the current block for special handling or error reporting
unknown_tag(end_tag_name, end_tag_params, tokens)
return false if end_tag_name == block_delimiter
unless end_tag_name
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
end
ensure
parse_context.depth -= 1
# this tag is not registered with the system
# pass it to the current block for special handling or error reporting
unknown_tag(end_tag_name, end_tag_params, tokens)
end
true

View File

@@ -93,11 +93,10 @@ module Liquid
rescue MemoryError => e
raise e
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
context.handle_error(e, token.line_number)
context.handle_error(e, token.line_number, token.raw)
output << nil
rescue ::StandardError => e
line_number = token.is_a?(String) ? nil : token.line_number
output << context.handle_error(e, line_number)
output << context.handle_error(e, token.line_number, token.raw)
end
end
@@ -107,7 +106,7 @@ module Liquid
private
def render_node(node, context)
node_output = node.is_a?(String) ? node : node.render(context)
node_output = (node.respond_to?(:render) ? node.render(context) : node)
node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
context.resource_limits.render_length += node_output.length

View File

@@ -41,22 +41,16 @@ module Liquid
end
def evaluate(context = Context.new)
condition = self
result = nil
loop do
result = interpret_condition(condition.left, condition.right, condition.operator, context)
result = interpret_condition(left, right, operator, context)
case condition.child_relation
when :or
break if result
when :and
break unless result
else
break
end
condition = condition.child_condition
case @child_relation
when :or
result || @child_condition.evaluate(context)
when :and
result && @child_condition.evaluate(context)
else
result
end
result
end
def or(condition)
@@ -81,10 +75,6 @@ module Liquid
"#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
end
protected
attr_reader :child_relation, :child_condition
private
def equal_variables(left, right)
@@ -120,10 +110,10 @@ module Liquid
if operation.respond_to?(:call)
operation.call(self, left, right)
elsif left.respond_to?(operation) && right.respond_to?(operation) && !left.is_a?(Hash) && !right.is_a?(Hash)
elsif left.respond_to?(operation) && right.respond_to?(operation)
begin
left.send(operation, right)
rescue ::ArgumentError => e
rescue ::ArgumentError, TypeError => e
raise Liquid::ArgumentError.new(e.message)
end
end

View File

@@ -74,7 +74,7 @@ module Liquid
@interrupts.pop
end
def handle_error(e, line_number = nil)
def handle_error(e, line_number = nil, raw_token = nil)
e = internal_error unless e.is_a?(Liquid::Error)
e.template_name ||= template_name
e.line_number ||= line_number
@@ -89,7 +89,7 @@ module Liquid
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
def push(new_scope = {})
@scopes.unshift(new_scope)
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100
end
# Merge a hash of variables in the current local scope
@@ -160,7 +160,7 @@ module Liquid
end
# Fetches an object starting at the local scope and then moving up the hierachy
def find_variable(key, raise_on_not_found: true)
def find_variable(key)
# This was changed from find() to find_index() because this is a very hot
# path and find_index() is optimized in MRI to reduce object allocation
index = @scopes.find_index { |s| s.key?(key) }
@@ -170,7 +170,7 @@ module Liquid
if scope.nil?
@environments.each do |e|
variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found)
variable = lookup_and_evaluate(e, key)
unless variable.nil?
scope = e
break
@@ -179,7 +179,7 @@ module Liquid
end
scope ||= @environments.last || @scopes.last
variable ||= lookup_and_evaluate(scope, key, raise_on_not_found: raise_on_not_found)
variable ||= lookup_and_evaluate(scope, key)
variable = variable.to_liquid
variable.context = self if variable.respond_to?(:context=)
@@ -187,8 +187,8 @@ module Liquid
variable
end
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
def lookup_and_evaluate(obj, key)
if @strict_variables && obj.respond_to?(:key?) && !obj.key?(key)
raise Liquid::UndefinedVariable, "undefined variable #{key}"
end

View File

@@ -18,10 +18,10 @@ module Liquid
DOUBLE_STRING_LITERAL = /"[^\"]*"/
NUMBER_LITERAL = /-?\d+(\.\d+)?/
DOTDOT = /\.\./
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains/
def initialize(input)
@ss = StringScanner.new(input)
@ss = StringScanner.new(input.rstrip)
end
def tokenize
@@ -29,7 +29,6 @@ module Liquid
until @ss.eos?
@ss.skip(/\s*/)
break if @ss.eos?
tok = case
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]

View File

@@ -1,13 +1,12 @@
module Liquid
class ParseContext
attr_accessor :locale, :line_number, :trim_whitespace, :depth
attr_accessor :locale, :line_number, :trim_whitespace
attr_reader :partial, :warnings, :error_mode
def initialize(options = {})
@template_options = options ? options.dup : {}
@locale = @template_options[:locale] ||= I18n.new
@warnings = []
self.depth = 0
self.partial = false
end

View File

@@ -33,7 +33,7 @@ module Liquid
end
def escape(input)
CGI.escapeHTML(input.to_s).untaint unless input.nil?
CGI.escapeHTML(input).untaint unless input.nil?
end
alias_method :h, :escape
@@ -42,11 +42,11 @@ module Liquid
end
def url_encode(input)
CGI.escape(input.to_s) unless input.nil?
CGI.escape(input) unless input.nil?
end
def url_decode(input)
CGI.unescape(input.to_s) unless input.nil?
CGI.unescape(input) unless input.nil?
end
def slice(input, offset, length = nil)
@@ -384,7 +384,7 @@ module Liquid
end
def join(glue)
to_a.join(glue.to_s)
to_a.join(glue)
end
def concat(args)

View File

@@ -27,7 +27,7 @@ module Liquid
def self.add_filter(filter)
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
unless self.include?(filter)
unless self.class.include?(filter)
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
if invokable_non_public_methods.any?
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"

View File

@@ -23,7 +23,7 @@ module Liquid
# {{ item.name }}
# {% end %}
#
# To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
# To reverse the for loop simply use {% for item in collection reversed %}
#
# == Available variables:
#
@@ -46,9 +46,6 @@ module Liquid
class For < Block
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
attr_reader :collection_name
attr_reader :variable_name
def initialize(tag_name, markup, options)
super
@from = @limit = nil
@@ -129,7 +126,7 @@ module Liquid
end
collection = context.evaluate(@collection_name)
collection = collection.step(1).to_a if collection.is_a?(Range)
collection = collection.to_a if collection.is_a?(Range)
limit = context.evaluate(@limit)
to = limit ? limit.to_i + from : nil

View File

@@ -83,20 +83,17 @@ module Liquid
def strict_parse(markup)
p = Parser.new(markup)
condition = parse_binary_comparisons(p)
condition = parse_binary_comparison(p)
p.consume(:end_of_string)
condition
end
def parse_binary_comparisons(p)
def parse_binary_comparison(p)
condition = parse_comparison(p)
first_condition = condition
while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
child_condition = parse_comparison(p)
condition.send(op, child_condition)
condition = child_condition
if op = (p.id?('and'.freeze) || p.id?('or'.freeze))
condition.send(op, parse_binary_comparison(p))
end
first_condition
condition
end
def parse_comparison(p)

View File

@@ -50,7 +50,7 @@ module Liquid
variable = if @variable_name_expr
context.evaluate(@variable_name_expr)
else
context.find_variable(template_name, raise_on_not_found: false)
context.find_variable(template_name)
end
old_template_name = context.template_name

View File

@@ -5,7 +5,7 @@ Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
profiler = ThemeRunner.new
Benchmark.ips do |x|
x.time = 10
x.time = 60
x.warmup = 5
puts
@@ -13,6 +13,5 @@ Benchmark.ips do |x|
puts
x.report("parse:") { profiler.compile }
x.report("render:") { profiler.render }
x.report("parse & render:") { profiler.run }
x.report("parse & run:") { profiler.run }
end

View File

@@ -21,100 +21,53 @@ class ThemeRunner
end
end
# Initialize a new liquid ThemeRunner instance
# Will load all templates into memory, do this now so that we don't profile IO.
# Load all templates into memory, do this now so that
# we don't profile IO.
def initialize
@tests = Dir[__dir__ + '/tests/**/*.liquid'].collect do |test|
next if File.basename(test) == 'theme.liquid'
theme_path = File.dirname(test) + '/theme.liquid'
{
liquid: File.read(test),
layout: (File.file?(theme_path) ? File.read(theme_path) : nil),
template_name: test
}
end.compact
compile_all_tests
[File.read(test), (File.file?(theme_path) ? File.read(theme_path) : nil), test]
end.compact
end
# `compile` will test just the compilation portion of liquid without any templates
def compile
@tests.each do |test_hash|
Liquid::Template.new.parse(test_hash[:liquid])
Liquid::Template.new.parse(test_hash[:layout])
# Dup assigns because will make some changes to them
@tests.each do |liquid, layout, template_name|
tmpl = Liquid::Template.new
tmpl.parse(liquid)
tmpl = Liquid::Template.new
tmpl.parse(layout)
end
end
# `run` is called to benchmark rendering and compiling at the same time
def run
each_test do |liquid, layout, assigns, page_template, template_name|
# Dup assigns because will make some changes to them
assigns = Database.tables.dup
@tests.each do |liquid, layout, template_name|
# Compute page_tempalte outside of profiler run, uninteresting to profiler
page_template = File.basename(template_name, File.extname(template_name))
compile_and_render(liquid, layout, assigns, page_template, template_name)
end
end
# `render` is called to benchmark just the render portion of liquid
def render
@compiled_tests.each do |test|
tmpl = test[:tmpl]
assigns = test[:assigns]
layout = test[:layout]
if layout
assigns['content_for_layout'] = tmpl.render!(assigns)
layout.render!(assigns)
else
tmpl.render!(assigns)
end
end
end
private
def compile_and_render(template, layout, assigns, page_template, template_file)
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
assigns['content_for_layout'] = compiled_test[:tmpl].render!(assigns)
compiled_test[:layout].render!(assigns) if layout
end
def compile_all_tests
@compiled_tests = []
each_test do |liquid, layout, assigns, page_template, template_name|
@compiled_tests << compile_test(liquid, layout, assigns, page_template, template_name)
end
@compiled_tests
end
def compile_test(template, layout, assigns, page_template, template_file)
tmpl = init_template(page_template, template_file)
parsed_template = tmpl.parse(template).dup
if layout
parsed_layout = tmpl.parse(layout)
{ tmpl: parsed_template, assigns: assigns, layout: parsed_layout }
else
{ tmpl: parsed_template, assigns: assigns }
end
end
# utility method with similar functionality needed in `compile_all_tests` and `run`
def each_test
# Dup assigns because will make some changes to them
assigns = Database.tables.dup
@tests.each do |test_hash|
# Compute page_template outside of profiler run, uninteresting to profiler
page_template = File.basename(test_hash[:template_name], File.extname(test_hash[:template_name]))
yield(test_hash[:liquid], test_hash[:layout], assigns, page_template, test_hash[:template_name])
end
end
# set up a new Liquid::Template object for use in `compile_and_render` and `compile_test`
def init_template(page_template, template_file)
tmpl = Liquid::Template.new
tmpl.assigns['page_title'] = 'Page title'
tmpl.assigns['template'] = page_template
tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file))
tmpl
content_for_layout = tmpl.parse(template).render!(assigns)
if layout
assigns['content_for_layout'] = content_for_layout
tmpl.parse(layout).render!(assigns)
else
content_for_layout
end
end
end

View File

@@ -115,8 +115,4 @@ class ParsingQuirksTest < Minitest::Test
assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
end
end
def test_contains_in_id
assert_template_result(' YES ', '{% if containsallshipments == true %} YES {% endif %}', 'containsallshipments' => true)
end
end # ParsingQuirksTest

View File

@@ -63,18 +63,4 @@ class SecurityTest < Minitest::Test
assert_equal [], (Symbol.all_symbols - current_symbols)
end
def test_max_depth_nested_blocks_does_not_raise_exception
depth = Liquid::Block::MAX_DEPTH
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
assert_equal "rendered", Template.parse(code).render!
end
def test_more_than_max_depth_nested_blocks_raises_exception
depth = Liquid::Block::MAX_DEPTH + 1
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
assert_raises(Liquid::StackLevelError) do
Template.parse(code).render!
end
end
end # SecurityTest

View File

@@ -128,16 +128,8 @@ class StandardFiltersTest < Minitest::Test
def test_escape
assert_equal '&lt;strong&gt;', @filters.escape('<strong>')
assert_equal '1', @filters.escape(1)
assert_equal '2001-02-03', @filters.escape(Date.new(2001, 2, 3))
assert_nil @filters.escape(nil)
end
def test_h
assert_equal nil, @filters.escape(nil)
assert_equal '&lt;strong&gt;', @filters.h('<strong>')
assert_equal '1', @filters.h(1)
assert_equal '2001-02-03', @filters.h(Date.new(2001, 2, 3))
assert_nil @filters.h(nil)
end
def test_escape_once
@@ -146,18 +138,14 @@ class StandardFiltersTest < Minitest::Test
def test_url_encode
assert_equal 'foo%2B1%40example.com', @filters.url_encode('foo+1@example.com')
assert_equal '1', @filters.url_encode(1)
assert_equal '2001-02-03', @filters.url_encode(Date.new(2001, 2, 3))
assert_nil @filters.url_encode(nil)
assert_equal nil, @filters.url_encode(nil)
end
def test_url_decode
assert_equal 'foo bar', @filters.url_decode('foo+bar')
assert_equal 'foo bar', @filters.url_decode('foo%20bar')
assert_equal 'foo+1@example.com', @filters.url_decode('foo%2B1%40example.com')
assert_equal '1', @filters.url_decode(1)
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
assert_nil @filters.url_decode(nil)
assert_equal nil, @filters.url_decode(nil)
end
def test_truncatewords
@@ -182,7 +170,6 @@ class StandardFiltersTest < Minitest::Test
def test_join
assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
assert_equal '1121314', @filters.join([1, 2, 3, 4], 1)
end
def test_sort
@@ -342,7 +329,7 @@ class StandardFiltersTest < Minitest::Test
assert_equal "#{Date.today.year}", @filters.date('today', '%Y')
assert_equal "#{Date.today.year}", @filters.date('Today', '%Y')
assert_nil @filters.date(nil, "%B")
assert_equal nil, @filters.date(nil, "%B")
assert_equal '', @filters.date('', "%B")
@@ -355,8 +342,8 @@ class StandardFiltersTest < Minitest::Test
def test_first_last
assert_equal 1, @filters.first([1, 2, 3])
assert_equal 3, @filters.last([1, 2, 3])
assert_nil @filters.first([])
assert_nil @filters.last([])
assert_equal nil, @filters.first([])
assert_equal nil, @filters.last([])
end
def test_replace

View File

@@ -48,10 +48,6 @@ HERE
def test_for_with_variable_range
assert_template_result(' 1 2 3 ', '{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1..3.0))
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.0..3))
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.0..3.0))
assert_template_result(' 1.5 2.5 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.5..3))
end
def test_for_with_hash_value_range

View File

@@ -137,7 +137,7 @@ class IncludeTagTest < Minitest::Test
Liquid::Template.file_system = infinite_file_system.new
assert_raises(Liquid::StackLevelError) do
assert_raises(Liquid::StackLevelError, SystemStackError) do
Template.parse("{% include 'loop' %}").render!
end
end
@@ -235,11 +235,4 @@ class IncludeTagTest < Minitest::Test
assert_template_result "Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}", "foo" => { 'title' => 'Draft 151cm' }
end
def test_including_with_strict_variables
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
template.render(nil, strict_variables: true)
assert_equal [], template.errors
end
end # IncludeTagTest

View File

@@ -2,6 +2,7 @@
ENV["MT_NO_EXPECTATIONS"] = "1"
require 'minitest/autorun'
require 'spy/integration'
$LOAD_PATH.unshift(File.join(File.expand_path(__dir__), '..', 'lib'))
require 'liquid.rb'

View File

@@ -57,6 +57,11 @@ class ConditionUnitTest < Minitest::Test
assert_evaluates_argument_error 1, '~~', 0
end
def test_comparing_hash_and_integer
assert_evaluates_argument_error({a: 1}, '>', 1)
assert_evaluates_argument_error(1, '>', {a: 1})
end
def test_comparation_of_int_and_str
assert_evaluates_argument_error '1', '>', 0
assert_evaluates_argument_error '1', '<', 0
@@ -64,14 +69,6 @@ class ConditionUnitTest < Minitest::Test
assert_evaluates_argument_error '1', '<=', 0
end
def test_hash_compare_backwards_compatibility
assert_nil Condition.new({}, '>', 2).evaluate
assert_nil Condition.new(2, '>', {}).evaluate
assert_equal false, Condition.new({}, '==', 2).evaluate
assert_equal true, Condition.new({ 'a' => 1 }, '==', { 'a' => 1 }).evaluate
assert_equal true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate
end
def test_contains_works_on_arrays
@context = Liquid::Context.new
@context['array'] = [1, 2, 3, 4, 5]

View File

@@ -70,6 +70,10 @@ class ContextUnitTest < Minitest::Test
@context = Liquid::Context.new
end
def teardown
Spy.teardown
end
def test_variables
@context['string'] = 'string'
assert_equal 'string', @context['string']
@@ -94,12 +98,12 @@ class ContextUnitTest < Minitest::Test
assert_equal false, @context['bool']
@context['nil'] = nil
assert_nil @context['nil']
assert_nil @context['nil']
assert_equal nil, @context['nil']
assert_equal nil, @context['nil']
end
def test_variables_not_existing
assert_nil @context['does_not_exist']
assert_equal nil, @context['does_not_exist']
end
def test_scoping
@@ -181,7 +185,7 @@ class ContextUnitTest < Minitest::Test
@context['test'] = 'test'
assert_equal 'test', @context['test']
@context.pop
assert_nil @context['test']
assert_equal nil, @context['test']
end
def test_hierachical_data
@@ -296,7 +300,7 @@ class ContextUnitTest < Minitest::Test
@context['hash'] = { 'first' => 'Hello' }
assert_equal 1, @context['array.first']
assert_nil @context['array["first"]']
assert_equal nil, @context['array["first"]']
assert_equal 'Hello', @context['hash["first"]']
end
@@ -446,10 +450,14 @@ class ContextUnitTest < Minitest::Test
assert_equal @context, @context['category'].context
end
def test_interrupt_avoids_object_allocations
assert_no_object_allocations do
@context.interrupt?
end
def test_use_empty_instead_of_any_in_interrupt_handling_to_avoid_lots_of_unnecessary_object_allocations
mock_any = Spy.on_instance_method(Array, :any?)
mock_empty = Spy.on_instance_method(Array, :empty?)
@context.interrupt?
refute mock_any.has_been_called?
assert mock_empty.has_been_called?
end
def test_context_initialization_with_a_proc_in_environment
@@ -472,18 +480,4 @@ class ContextUnitTest < Minitest::Test
context = Context.new
assert_equal 'hi', context.apply_global_filter('hi')
end
private
def assert_no_object_allocations
unless RUBY_ENGINE == 'ruby'
skip "stackprof needed to count object allocations"
end
require 'stackprof'
profile = StackProf.run(mode: :object) do
yield
end
assert_equal 0, profile[:samples]
end
end # ContextTest

View File

@@ -19,7 +19,7 @@ class LexerUnitTest < Minitest::Test
end
def test_comparison
tokens = Lexer.new('== <> contains ').tokenize
tokens = Lexer.new('== <> contains').tokenize
assert_equal [[:comparison, '=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens
end

View File

@@ -145,20 +145,4 @@ class StrainerUnitTest < Minitest::Test
Strainer.global_filter(LateAddedFilter)
assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
end
def test_add_filter_does_not_include_already_included_module
mod = Module.new do
class << self
attr_accessor :include_count
def included(mod)
self.include_count += 1
end
end
self.include_count = 0
end
strainer = Context.new.strainer
strainer.class.add_filter(mod)
strainer.class.add_filter(mod)
assert_equal 1, mod.include_count
end
end # StrainerTest