liquid-c 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/liquid.yml +24 -2
- data/.gitignore +4 -0
- data/.rubocop.yml +14 -0
- data/Gemfile +14 -5
- data/README.md +29 -5
- data/Rakefile +13 -62
- data/ext/liquid_c/block.c +488 -60
- data/ext/liquid_c/block.h +28 -2
- data/ext/liquid_c/c_buffer.c +42 -0
- data/ext/liquid_c/c_buffer.h +76 -0
- data/ext/liquid_c/context.c +233 -0
- data/ext/liquid_c/context.h +70 -0
- data/ext/liquid_c/document_body.c +89 -0
- data/ext/liquid_c/document_body.h +59 -0
- data/ext/liquid_c/expression.c +116 -0
- data/ext/liquid_c/expression.h +24 -0
- data/ext/liquid_c/extconf.rb +19 -9
- data/ext/liquid_c/intutil.h +22 -0
- data/ext/liquid_c/lexer.c +6 -2
- data/ext/liquid_c/lexer.h +18 -3
- data/ext/liquid_c/liquid.c +76 -6
- data/ext/liquid_c/liquid.h +24 -1
- data/ext/liquid_c/parse_context.c +76 -0
- data/ext/liquid_c/parse_context.h +13 -0
- data/ext/liquid_c/parser.c +141 -65
- data/ext/liquid_c/parser.h +4 -2
- data/ext/liquid_c/raw.c +110 -0
- data/ext/liquid_c/raw.h +6 -0
- data/ext/liquid_c/resource_limits.c +279 -0
- data/ext/liquid_c/resource_limits.h +23 -0
- data/ext/liquid_c/stringutil.h +44 -0
- data/ext/liquid_c/tokenizer.c +149 -35
- data/ext/liquid_c/tokenizer.h +20 -9
- data/ext/liquid_c/usage.c +18 -0
- data/ext/liquid_c/usage.h +9 -0
- data/ext/liquid_c/variable.c +196 -20
- data/ext/liquid_c/variable.h +18 -1
- data/ext/liquid_c/variable_lookup.c +44 -0
- data/ext/liquid_c/variable_lookup.h +8 -0
- data/ext/liquid_c/vm.c +588 -0
- data/ext/liquid_c/vm.h +25 -0
- data/ext/liquid_c/vm_assembler.c +491 -0
- data/ext/liquid_c/vm_assembler.h +240 -0
- data/ext/liquid_c/vm_assembler_pool.c +97 -0
- data/ext/liquid_c/vm_assembler_pool.h +27 -0
- data/lib/liquid/c/compile_ext.rb +44 -0
- data/lib/liquid/c/version.rb +3 -1
- data/lib/liquid/c.rb +225 -46
- data/liquid-c.gemspec +16 -10
- data/performance/c_profile.rb +23 -0
- data/performance.rb +6 -4
- data/rakelib/compile.rake +15 -0
- data/rakelib/integration_test.rake +43 -0
- data/rakelib/performance.rake +43 -0
- data/rakelib/rubocop.rake +6 -0
- data/rakelib/unit_test.rake +14 -0
- data/test/integration_test.rb +11 -0
- data/test/liquid_test_helper.rb +21 -0
- data/test/test_helper.rb +14 -2
- data/test/unit/block_test.rb +130 -0
- data/test/unit/context_test.rb +83 -0
- data/test/unit/expression_test.rb +186 -0
- data/test/unit/gc_stress_test.rb +28 -0
- data/test/unit/raw_test.rb +19 -0
- data/test/unit/resource_limits_test.rb +50 -0
- data/test/unit/tokenizer_test.rb +90 -20
- data/test/unit/variable_test.rb +212 -60
- metadata +59 -11
- data/test/liquid_test.rb +0 -11
data/lib/liquid/c.rb
CHANGED
@@ -1,77 +1,213 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid/c/version"
|
4
|
+
require "liquid"
|
5
|
+
require "liquid_c"
|
6
|
+
require "liquid/c/compile_ext"
|
7
|
+
|
8
|
+
Liquid::C::BlockBody.class_eval do
|
9
|
+
def render(context)
|
10
|
+
render_to_output_buffer(context, +"")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Liquid
|
15
|
+
BlockBody.class_eval do
|
16
|
+
def self.c_rescue_render_node(context, output, line_number, exc, blank_tag)
|
17
|
+
# There seems to be a MRI ruby bug with how the rb_rescue C function,
|
18
|
+
# where $! gets set for its rescue callback, but it doesn't stay set
|
19
|
+
# after a nested exception is raised and handled as is the case in
|
20
|
+
# Liquid::Context#internal_error. This isn't a problem for plain ruby code,
|
21
|
+
# so use a ruby rescue block to have setup $! properly.
|
22
|
+
raise(exc)
|
23
|
+
rescue => exc
|
24
|
+
rescue_render_node(context, output, line_number, exc, blank_tag)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
4
28
|
|
5
29
|
module Liquid
|
6
30
|
module C
|
7
|
-
|
31
|
+
# Placeholder for variables in the Liquid::C::BlockBody#nodelist.
|
32
|
+
class VariablePlaceholder
|
33
|
+
class << self
|
34
|
+
private :new
|
35
|
+
end
|
36
|
+
end
|
8
37
|
|
9
|
-
class
|
10
|
-
|
38
|
+
class Tokenizer
|
39
|
+
MAX_SOURCE_BYTE_SIZE = (1 << 24) - 1
|
11
40
|
end
|
12
41
|
end
|
13
42
|
end
|
14
43
|
|
15
|
-
Liquid::
|
16
|
-
|
17
|
-
|
18
|
-
|
44
|
+
Liquid::Raw.class_eval do
|
45
|
+
alias_method :ruby_parse, :parse
|
46
|
+
|
47
|
+
def parse(tokenizer)
|
48
|
+
if parse_context.liquid_c_nodes_disabled?
|
49
|
+
ruby_parse(tokenizer)
|
19
50
|
else
|
20
|
-
|
51
|
+
c_parse(tokenizer)
|
21
52
|
end
|
22
53
|
end
|
23
54
|
end
|
24
55
|
|
25
|
-
Liquid::
|
26
|
-
|
56
|
+
Liquid::ParseContext.class_eval do
|
57
|
+
class << self
|
58
|
+
attr_accessor :liquid_c_nodes_disabled
|
59
|
+
end
|
60
|
+
self.liquid_c_nodes_disabled = false
|
61
|
+
|
62
|
+
alias_method :ruby_new_block_body, :new_block_body
|
63
|
+
|
64
|
+
def new_block_body
|
65
|
+
if liquid_c_nodes_disabled?
|
66
|
+
ruby_new_block_body
|
67
|
+
else
|
68
|
+
Liquid::C::BlockBody.new(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
alias_method :ruby_new_tokenizer, :new_tokenizer
|
73
|
+
def new_tokenizer(source, start_line_number: nil, for_liquid_tag: false)
|
74
|
+
unless liquid_c_nodes_disabled?
|
75
|
+
source = source.to_s.to_str
|
76
|
+
if source.bytesize <= Liquid::C::Tokenizer::MAX_SOURCE_BYTE_SIZE
|
77
|
+
source = source.encode(Encoding::UTF_8)
|
78
|
+
return Liquid::C::Tokenizer.new(source, start_line_number || 0, for_liquid_tag)
|
79
|
+
else
|
80
|
+
@liquid_c_nodes_disabled = true
|
81
|
+
end
|
82
|
+
end
|
27
83
|
|
28
|
-
|
29
|
-
|
30
|
-
|
84
|
+
ruby_new_tokenizer(source, start_line_number: start_line_number, for_liquid_tag: for_liquid_tag)
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_expression(markup)
|
88
|
+
if liquid_c_nodes_disabled?
|
89
|
+
Liquid::Expression.ruby_parse(markup)
|
31
90
|
else
|
32
|
-
|
91
|
+
Liquid::C::Expression.lax_parse(markup)
|
33
92
|
end
|
34
93
|
end
|
94
|
+
|
95
|
+
# @api private
|
96
|
+
def liquid_c_nodes_disabled?
|
97
|
+
# Liquid::Profiler exposes the internal parse tree that we don't want to build when
|
98
|
+
# parsing with liquid-c, so consider liquid-c to be disabled when using it.
|
99
|
+
# Also, some templates are parsed before the profiler is running, on which case we
|
100
|
+
# provide the `disable_liquid_c_nodes` option to enable the Ruby AST to be produced
|
101
|
+
# so the profiler can use it on future runs.
|
102
|
+
return @liquid_c_nodes_disabled if defined?(@liquid_c_nodes_disabled)
|
103
|
+
@liquid_c_nodes_disabled = !Liquid::C.enabled || @template_options[:profile] ||
|
104
|
+
@template_options[:disable_liquid_c_nodes] || self.class.liquid_c_nodes_disabled
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module Liquid
|
109
|
+
module C
|
110
|
+
module DocumentClassPatch
|
111
|
+
def parse(tokenizer, parse_context)
|
112
|
+
if tokenizer.is_a?(Liquid::C::Tokenizer)
|
113
|
+
if parse_context[:bug_compatible_whitespace_trimming]
|
114
|
+
# Temporary to test rollout of the fix for this bug
|
115
|
+
tokenizer.bug_compatible_whitespace_trimming!
|
116
|
+
end
|
117
|
+
|
118
|
+
begin
|
119
|
+
parse_context.start_liquid_c_parsing
|
120
|
+
super
|
121
|
+
ensure
|
122
|
+
parse_context.cleanup_liquid_c_parsing
|
123
|
+
end
|
124
|
+
else
|
125
|
+
super
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
Liquid::Document.singleton_class.prepend(DocumentClassPatch)
|
130
|
+
end
|
35
131
|
end
|
36
132
|
|
37
133
|
Liquid::Variable.class_eval do
|
38
|
-
|
39
|
-
|
134
|
+
class << self
|
135
|
+
# @api private
|
136
|
+
def call_variable_fallback_stats_callback(parse_context)
|
137
|
+
callbacks = parse_context[:stats_callbacks]
|
138
|
+
callbacks[:variable_fallback]&.call if callbacks
|
139
|
+
end
|
40
140
|
|
41
|
-
|
42
|
-
stats = options[:stats_callbacks]
|
43
|
-
stats[:variable_parse].call if stats
|
141
|
+
private
|
44
142
|
|
45
|
-
|
143
|
+
# helper method for C code
|
144
|
+
def rescue_strict_parse_syntax_error(error, markup, parse_context)
|
145
|
+
error.line_number = parse_context.line_number
|
146
|
+
error.markup_context = "in \"{{#{markup}}}\""
|
147
|
+
case parse_context.error_mode
|
148
|
+
when :strict
|
149
|
+
raise
|
150
|
+
when :warn
|
151
|
+
parse_context.warnings << error
|
152
|
+
end
|
153
|
+
call_variable_fallback_stats_callback(parse_context)
|
154
|
+
lax_parse(markup, parse_context) # Avoid redundant strict parse
|
155
|
+
end
|
156
|
+
|
157
|
+
def lax_parse(markup, parse_context)
|
158
|
+
old_error_mode = parse_context.error_mode
|
46
159
|
begin
|
47
|
-
|
48
|
-
|
49
|
-
|
160
|
+
set_error_mode(parse_context, :lax)
|
161
|
+
new(markup, parse_context)
|
162
|
+
ensure
|
163
|
+
set_error_mode(parse_context, old_error_mode)
|
50
164
|
end
|
51
165
|
end
|
52
166
|
|
53
|
-
|
167
|
+
def set_error_mode(parse_context, mode)
|
168
|
+
parse_context.instance_variable_set(:@error_mode, mode)
|
169
|
+
end
|
54
170
|
end
|
55
171
|
|
172
|
+
alias_method :ruby_strict_parse, :strict_parse
|
173
|
+
|
56
174
|
def strict_parse(markup)
|
57
|
-
if
|
58
|
-
@name = Liquid::Variable.c_strict_parse(markup, @filters = [])
|
59
|
-
else
|
175
|
+
if parse_context.liquid_c_nodes_disabled?
|
60
176
|
ruby_strict_parse(markup)
|
177
|
+
else
|
178
|
+
c_strict_parse(markup)
|
61
179
|
end
|
62
180
|
end
|
63
181
|
end
|
64
182
|
|
65
|
-
Liquid::
|
66
|
-
|
183
|
+
Liquid::StrainerTemplate.class_eval do
|
184
|
+
class << self
|
185
|
+
private
|
67
186
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
187
|
+
def filter_methods_hash
|
188
|
+
@filter_methods_hash ||= {}.tap do |hash|
|
189
|
+
filter_methods.each do |method_name|
|
190
|
+
hash[method_name.to_sym] = true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Convert wrong number of argument error into a liquid exception to
|
196
|
+
# treat it as an error in the template, not an internal error.
|
197
|
+
def arg_exc_to_liquid_exc(argument_error)
|
198
|
+
raise Liquid::ArgumentError, argument_error.message, argument_error.backtrace
|
199
|
+
rescue Liquid::ArgumentError => liquid_exc
|
200
|
+
liquid_exc
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
Liquid::C::Expression.class_eval do
|
206
|
+
class << self
|
207
|
+
def lax_parse(markup)
|
208
|
+
strict_parse(markup)
|
209
|
+
rescue Liquid::SyntaxError
|
210
|
+
Liquid::Expression.ruby_parse(markup)
|
75
211
|
end
|
76
212
|
end
|
77
213
|
end
|
@@ -80,16 +216,59 @@ Liquid::Expression.class_eval do
|
|
80
216
|
class << self
|
81
217
|
alias_method :ruby_parse, :parse
|
82
218
|
|
83
|
-
def
|
84
|
-
|
219
|
+
def c_parse(markup)
|
220
|
+
Liquid::C::Expression.lax_parse(markup)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
Liquid::Context.class_eval do
|
226
|
+
alias_method :ruby_evaluate, :evaluate
|
227
|
+
alias_method :ruby_find_variable, :find_variable
|
228
|
+
alias_method :ruby_strict_variables=, :strict_variables=
|
229
|
+
|
230
|
+
# This isn't entered often by Ruby (most calls stay in C via VariableLookup#evaluate)
|
231
|
+
# so the wrapper method isn't costly.
|
232
|
+
def c_find_variable_kwarg(key, raise_on_not_found: true)
|
233
|
+
c_find_variable(key, raise_on_not_found)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
Liquid::ResourceLimits.class_eval do
|
238
|
+
def self.new(limits)
|
239
|
+
if Liquid::C.enabled
|
240
|
+
Liquid::C::ResourceLimits.new(
|
241
|
+
limits[:render_length_limit],
|
242
|
+
limits[:render_score_limit],
|
243
|
+
limits[:assign_score_limit]
|
244
|
+
)
|
245
|
+
else
|
246
|
+
super
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
module Liquid
|
252
|
+
module C
|
253
|
+
class << self
|
254
|
+
attr_reader :enabled
|
85
255
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
256
|
+
def enabled=(value)
|
257
|
+
@enabled = value
|
258
|
+
if value
|
259
|
+
Liquid::Context.send(:alias_method, :evaluate, :c_evaluate)
|
260
|
+
Liquid::Context.send(:alias_method, :find_variable, :c_find_variable_kwarg)
|
261
|
+
Liquid::Context.send(:alias_method, :strict_variables=, :c_strict_variables=)
|
262
|
+
Liquid::Expression.singleton_class.send(:alias_method, :parse, :c_parse)
|
263
|
+
else
|
264
|
+
Liquid::Context.send(:alias_method, :evaluate, :ruby_evaluate)
|
265
|
+
Liquid::Context.send(:alias_method, :find_variable, :ruby_find_variable)
|
266
|
+
Liquid::Context.send(:alias_method, :strict_variables=, :ruby_strict_variables=)
|
267
|
+
Liquid::Expression.singleton_class.send(:alias_method, :parse, :ruby_parse)
|
90
268
|
end
|
91
269
|
end
|
92
|
-
ruby_parse(markup)
|
93
270
|
end
|
271
|
+
|
272
|
+
self.enabled = true
|
94
273
|
end
|
95
274
|
end
|
data/liquid-c.gemspec
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# rubocop:disable Gemspec/RubyVersionGlobalsUsage
|
5
|
+
|
6
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
7
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
8
|
+
require "liquid/c/version"
|
5
9
|
|
6
10
|
Gem::Specification.new do |spec|
|
7
11
|
spec.name = "liquid-c"
|
@@ -12,19 +16,21 @@ Gem::Specification.new do |spec|
|
|
12
16
|
spec.homepage = "https://github.com/shopify/liquid-c"
|
13
17
|
spec.license = "MIT"
|
14
18
|
|
15
|
-
spec.extensions = [
|
16
|
-
spec.files =
|
19
|
+
spec.extensions = ["ext/liquid_c/extconf.rb"]
|
20
|
+
spec.files = %x(git ls-files -z).split("\x0")
|
17
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
23
|
spec.require_paths = ["lib"]
|
20
24
|
|
25
|
+
spec.required_ruby_version = ">= 2.5.0"
|
26
|
+
|
21
27
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
22
28
|
|
23
|
-
spec.add_dependency
|
29
|
+
spec.add_dependency("liquid", ">= 5.0.1")
|
24
30
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
31
|
+
spec.add_development_dependency("bundler", ">= 1.5") # has bugfix for segfaulting deploys
|
32
|
+
spec.add_development_dependency("minitest")
|
33
|
+
spec.add_development_dependency("rake")
|
34
|
+
spec.add_development_dependency("rake-compiler")
|
35
|
+
spec.add_development_dependency("stackprof") if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
|
30
36
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "liquid"
|
4
|
+
require "liquid/c"
|
5
|
+
liquid_lib_dir = $LOAD_PATH.detect { |p| File.exist?(File.join(p, "liquid.rb")) }
|
6
|
+
require File.join(File.dirname(liquid_lib_dir), "performance/theme_runner")
|
7
|
+
|
8
|
+
TASK_NAMES = ["run", "compile", "render"]
|
9
|
+
task_name = ARGV.first || "run"
|
10
|
+
unless TASK_NAMES.include?(task_name)
|
11
|
+
raise "Unsupported task '#{task_name}' (must be one of #{TASK_NAMES})"
|
12
|
+
end
|
13
|
+
task = ThemeRunner.new.method(task_name)
|
14
|
+
|
15
|
+
runner_id = fork do
|
16
|
+
end_time = Time.now + 5.0
|
17
|
+
task.call until Time.now >= end_time
|
18
|
+
end
|
19
|
+
|
20
|
+
profiler_pid = spawn("instruments -t 'Time Profiler' -p #{runner_id}")
|
21
|
+
|
22
|
+
Process.waitpid(runner_id)
|
23
|
+
Process.waitpid(profiler_pid)
|
data/performance.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
require 'liquid/c' if ARGV.shift == "c"
|
3
|
-
liquid_lib_dir = $LOAD_PATH.detect{ |p| File.exists?(File.join(p, 'liquid.rb')) }
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
3
|
+
require "liquid"
|
4
|
+
require "liquid/c" if ARGV.shift == "c"
|
5
|
+
liquid_lib_dir = $LOAD_PATH.detect { |p| File.exist?(File.join(p, "liquid.rb")) }
|
6
|
+
|
7
|
+
(script = ARGV.shift) || abort("unspecified performance script")
|
6
8
|
require File.join(File.dirname(liquid_lib_dir), "performance/#{script}")
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ext_task = Rake::ExtensionTask.new("liquid_c")
|
4
|
+
|
5
|
+
# For MacOS, generate debug information that ruby can read
|
6
|
+
dsymutil = RbConfig::CONFIG["dsymutil"]
|
7
|
+
unless dsymutil.to_s.empty?
|
8
|
+
ext_lib_path = "lib/#{ext_task.binary}"
|
9
|
+
dsym_path = "#{ext_lib_path}.dSYM"
|
10
|
+
|
11
|
+
file dsym_path => [ext_lib_path] do
|
12
|
+
sh dsymutil, ext_lib_path
|
13
|
+
end
|
14
|
+
Rake::Task["compile"].enhance([dsym_path])
|
15
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :test do
|
4
|
+
integration_test_config = lambda do |t|
|
5
|
+
t.libs << "lib"
|
6
|
+
t.test_files = ["test/integration_test.rb"]
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "run test suite with default parser"
|
10
|
+
Rake::TestTask.new(integration: :compile, &integration_test_config)
|
11
|
+
|
12
|
+
namespace :integration do
|
13
|
+
define_env_integration_tests = lambda do |task_name|
|
14
|
+
rake_task = Rake::Task[task_name]
|
15
|
+
|
16
|
+
[
|
17
|
+
[:lax, { "LIQUID_PARSER_MODE" => "lax" }],
|
18
|
+
[:strict, { "LIQUID_PARSER_MODE" => "strict" }],
|
19
|
+
[:without_vm, { "LIQUID_C_DISABLE_VM" => "true" }],
|
20
|
+
].each do |name, env_vars|
|
21
|
+
task(name) do
|
22
|
+
old_env_values = ENV.to_hash.slice(*env_vars.keys)
|
23
|
+
begin
|
24
|
+
env_vars.each { |key, value| ENV[key] = value }
|
25
|
+
rake_task.invoke
|
26
|
+
ensure
|
27
|
+
old_env_values.each { |key, value| ENV[key] = value }
|
28
|
+
rake_task.reenable
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
task(all: [:lax, :strict, :without_vm])
|
34
|
+
end
|
35
|
+
|
36
|
+
define_env_integration_tests.call("test:integration")
|
37
|
+
|
38
|
+
RubyMemcheck::TestTask.new(valgrind: :compile, &integration_test_config)
|
39
|
+
namespace :valgrind do
|
40
|
+
define_env_integration_tests.call("test:integration:valgrind")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :benchmark do
|
4
|
+
desc "Run the liquid benchmark with lax parsing"
|
5
|
+
task :run do
|
6
|
+
ruby "./performance.rb c benchmark lax"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Run the liquid benchmark with strict parsing"
|
10
|
+
task :strict do
|
11
|
+
ruby "./performance.rb c benchmark strict"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :c_profile do
|
16
|
+
[:run, :compile, :render].each do |task_name|
|
17
|
+
task(task_name) do
|
18
|
+
ruby "./performance/c_profile.rb #{task_name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
namespace :profile do
|
24
|
+
desc "Run the liquid profile/performance coverage"
|
25
|
+
task :run do
|
26
|
+
ruby "./performance.rb c profile lax"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Run the liquid profile/performance coverage with strict parsing"
|
30
|
+
task :strict do
|
31
|
+
ruby "./performance.rb c profile strict"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
namespace :compare do
|
36
|
+
["lax", "warn", "strict"].each do |type|
|
37
|
+
desc "Compare Liquid to Liquid-C in #{type} mode"
|
38
|
+
task type.to_sym do
|
39
|
+
ruby "./performance.rb bare benchmark #{type}"
|
40
|
+
ruby "./performance.rb c benchmark #{type}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :test do
|
4
|
+
unit_test_config = lambda do |t|
|
5
|
+
t.libs << "lib" << "test"
|
6
|
+
t.test_files = FileList["test/unit/**/*_test.rb"]
|
7
|
+
end
|
8
|
+
|
9
|
+
Rake::TestTask.new(unit: :compile, &unit_test_config)
|
10
|
+
|
11
|
+
namespace :unit do
|
12
|
+
RubyMemcheck::TestTask.new(valgrind: :compile, &unit_test_config)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
at_exit { GC.start }
|
4
|
+
|
5
|
+
require_relative "liquid_test_helper"
|
6
|
+
|
7
|
+
test_files = Dir[File.join(LIQUID_TEST_DIR, "integration/**/*_test.rb")]
|
8
|
+
test_files << File.join(LIQUID_TEST_DIR, "unit/tokenizer_unit_test.rb")
|
9
|
+
test_files.each do |test_file|
|
10
|
+
require test_file
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This can be used to setup for running tests from the liquid test suite.
|
4
|
+
# For example, you could run a single liquid test as follows:
|
5
|
+
# $ ruby -r./test/liquid_test_helper `bundle info liquid --path`/test/integration/template_test.rb
|
6
|
+
|
7
|
+
require "bundler/setup"
|
8
|
+
|
9
|
+
liquid_lib_dir = $LOAD_PATH.detect { |p| File.exist?(File.join(p, "liquid.rb")) }
|
10
|
+
LIQUID_TEST_DIR = File.join(File.dirname(liquid_lib_dir), "test")
|
11
|
+
$LOAD_PATH << LIQUID_TEST_DIR << File.expand_path("../lib", __dir__)
|
12
|
+
|
13
|
+
require "test_helper"
|
14
|
+
require "liquid/c"
|
15
|
+
|
16
|
+
if ENV["LIQUID_C_DISABLE_VM"]
|
17
|
+
puts "-- Liquid-C VM Disabled"
|
18
|
+
Liquid::ParseContext.liquid_c_nodes_disabled = true
|
19
|
+
end
|
20
|
+
|
21
|
+
GC.stress = true if ENV["GC_STRESS"]
|
data/test/test_helper.rb
CHANGED
@@ -1,2 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
at_exit { GC.start }
|
4
|
+
|
5
|
+
require "minitest/autorun"
|
6
|
+
require "liquid/c"
|
7
|
+
|
8
|
+
if GC.respond_to?(:verify_compaction_references)
|
9
|
+
# This method was added in Ruby 3.0.0. Calling it this way asks the GC to
|
10
|
+
# move objects around, helping to find object movement bugs.
|
11
|
+
GC.verify_compaction_references(double_heap: true, toward: :empty)
|
12
|
+
end
|
13
|
+
|
14
|
+
GC.stress = true if ENV["GC_STRESS"]
|