liquid-c 4.0.1 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"]
|