cataract 0.1.3 → 0.2.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/ci-manual-rubies.yml +44 -0
- data/.overcommit.yml +1 -1
- data/.rubocop.yml +96 -4
- data/.rubocop_todo.yml +186 -0
- data/BENCHMARKS.md +62 -141
- data/CHANGELOG.md +20 -0
- data/RAGEL_MIGRATION.md +2 -2
- data/README.md +37 -4
- data/Rakefile +72 -32
- data/cataract.gemspec +4 -1
- data/ext/cataract/cataract.c +59 -50
- data/ext/cataract/cataract.h +5 -3
- data/ext/cataract/css_parser.c +173 -65
- data/ext/cataract/extconf.rb +2 -2
- data/ext/cataract/{merge.c → flatten.c} +526 -468
- data/ext/cataract/shorthand_expander.c +164 -115
- data/lib/cataract/at_rule.rb +8 -9
- data/lib/cataract/declaration.rb +18 -0
- data/lib/cataract/import_resolver.rb +63 -43
- data/lib/cataract/import_statement.rb +49 -0
- data/lib/cataract/pure/byte_constants.rb +69 -0
- data/lib/cataract/pure/flatten.rb +1145 -0
- data/lib/cataract/pure/helpers.rb +35 -0
- data/lib/cataract/pure/imports.rb +268 -0
- data/lib/cataract/pure/parser.rb +1340 -0
- data/lib/cataract/pure/serializer.rb +590 -0
- data/lib/cataract/pure/specificity.rb +206 -0
- data/lib/cataract/pure.rb +153 -0
- data/lib/cataract/rule.rb +69 -15
- data/lib/cataract/stylesheet.rb +356 -49
- data/lib/cataract/version.rb +1 -1
- data/lib/cataract.rb +43 -26
- metadata +14 -26
- data/benchmarks/benchmark_harness.rb +0 -193
- data/benchmarks/benchmark_merging.rb +0 -121
- data/benchmarks/benchmark_optimization_comparison.rb +0 -168
- data/benchmarks/benchmark_parsing.rb +0 -153
- data/benchmarks/benchmark_ragel_removal.rb +0 -56
- data/benchmarks/benchmark_runner.rb +0 -70
- data/benchmarks/benchmark_serialization.rb +0 -180
- data/benchmarks/benchmark_shorthand.rb +0 -109
- data/benchmarks/benchmark_shorthand_expansion.rb +0 -176
- data/benchmarks/benchmark_specificity.rb +0 -124
- data/benchmarks/benchmark_string_allocation.rb +0 -151
- data/benchmarks/benchmark_stylesheet_to_s.rb +0 -62
- data/benchmarks/benchmark_to_s_cached.rb +0 -55
- data/benchmarks/benchmark_value_splitter.rb +0 -54
- data/benchmarks/benchmark_yjit.rb +0 -158
- data/benchmarks/benchmark_yjit_workers.rb +0 -61
- data/benchmarks/profile_to_s.rb +0 -23
- data/benchmarks/speedup_calculator.rb +0 -83
- data/benchmarks/system_metadata.rb +0 -81
- data/benchmarks/templates/benchmarks.md.erb +0 -221
- data/benchmarks/yjit_tests.rb +0 -141
- data/scripts/fuzzer/run.rb +0 -828
- data/scripts/fuzzer/worker.rb +0 -99
- data/scripts/generate_benchmarks_md.rb +0 -155
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'benchmark_harness'
|
|
4
|
-
|
|
5
|
-
# Load the local development version, not installed gem
|
|
6
|
-
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
7
|
-
require 'cataract'
|
|
8
|
-
|
|
9
|
-
# CSS Parsing Performance Benchmark
|
|
10
|
-
class ParsingBenchmark < BenchmarkHarness
|
|
11
|
-
def self.benchmark_name
|
|
12
|
-
'parsing'
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def self.description
|
|
16
|
-
'Time to parse CSS into internal data structures'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def self.metadata
|
|
20
|
-
instance = new
|
|
21
|
-
{
|
|
22
|
-
'test_cases' => [
|
|
23
|
-
{
|
|
24
|
-
'name' => "Small CSS (#{instance.css1.lines.count} lines, #{(instance.css1.length / 1024.0).round(1)}KB)",
|
|
25
|
-
'fixture' => 'CSS1',
|
|
26
|
-
'lines' => instance.css1.lines.count,
|
|
27
|
-
'bytes' => instance.css1.length
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
'name' => "Medium CSS with @media (#{instance.css2.lines.count} lines, #{(instance.css2.length / 1024.0).round(1)}KB)",
|
|
31
|
-
'fixture' => 'CSS2',
|
|
32
|
-
'lines' => instance.css2.lines.count,
|
|
33
|
-
'bytes' => instance.css2.length
|
|
34
|
-
}
|
|
35
|
-
]
|
|
36
|
-
# speedups will be calculated automatically by harness
|
|
37
|
-
}
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Uses default speedup_config from harness (css_parser vs cataract, test_case_key: :fixture)
|
|
41
|
-
|
|
42
|
-
def sanity_checks
|
|
43
|
-
# Check css_parser gem is available
|
|
44
|
-
require 'css_parser'
|
|
45
|
-
|
|
46
|
-
# Verify fixtures parse correctly
|
|
47
|
-
parser = Cataract::Stylesheet.new
|
|
48
|
-
parser.add_block(css1)
|
|
49
|
-
raise 'CSS1 sanity check failed: expected rules' if parser.rules_count.zero?
|
|
50
|
-
|
|
51
|
-
parser = Cataract::Stylesheet.new
|
|
52
|
-
parser.add_block(css2)
|
|
53
|
-
raise 'CSS2 sanity check failed: expected rules' if parser.rules_count.zero?
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def call
|
|
57
|
-
run_css1_benchmark
|
|
58
|
-
run_css2_benchmark
|
|
59
|
-
show_correctness_comparison
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def css1
|
|
63
|
-
@css1 ||= File.read(File.join(fixtures_dir, 'css1_sample.css'))
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def css2
|
|
67
|
-
@css2 ||= File.read(File.join(fixtures_dir, 'css2_sample.css'))
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
private
|
|
71
|
-
|
|
72
|
-
def fixtures_dir
|
|
73
|
-
@fixtures_dir ||= File.expand_path('../test/fixtures', __dir__)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def run_css1_benchmark
|
|
77
|
-
puts '=' * 80
|
|
78
|
-
puts "TEST: CSS1 (#{css1.lines.count} lines, #{css1.length} chars)"
|
|
79
|
-
puts '=' * 80
|
|
80
|
-
|
|
81
|
-
benchmark('css1') do |x|
|
|
82
|
-
x.config(time: 5, warmup: 2)
|
|
83
|
-
|
|
84
|
-
x.report('css_parser gem: CSS1') do
|
|
85
|
-
parser = CssParser::Parser.new(import: false, io_exceptions: false)
|
|
86
|
-
parser.add_block!(css1)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
x.report('cataract: CSS1') do
|
|
90
|
-
parser = Cataract::Stylesheet.new
|
|
91
|
-
parser.add_block(css1)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
x.compare!
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def run_css2_benchmark
|
|
99
|
-
puts "\n#{'=' * 80}"
|
|
100
|
-
puts "TEST: CSS2 with @media (#{css2.lines.count} lines, #{css2.length} chars)"
|
|
101
|
-
puts '=' * 80
|
|
102
|
-
|
|
103
|
-
benchmark('css2') do |x|
|
|
104
|
-
x.config(time: 5, warmup: 2)
|
|
105
|
-
|
|
106
|
-
x.report('css_parser gem: CSS2') do
|
|
107
|
-
parser = CssParser::Parser.new(import: false, io_exceptions: false)
|
|
108
|
-
parser.add_block!(css2)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
x.report('cataract: CSS2') do
|
|
112
|
-
parser = Cataract::Stylesheet.new
|
|
113
|
-
parser.add_block(css2)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
x.compare!
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def show_correctness_comparison
|
|
121
|
-
puts "\n#{'=' * 80}"
|
|
122
|
-
puts 'CORRECTNESS VALIDATION (CSS2)'
|
|
123
|
-
puts '=' * 80
|
|
124
|
-
|
|
125
|
-
# Test Cataract
|
|
126
|
-
parser = Cataract::Stylesheet.new
|
|
127
|
-
parser.add_block(css2)
|
|
128
|
-
cataract_rules = parser.rules_count
|
|
129
|
-
puts "Cataract found #{cataract_rules} rules"
|
|
130
|
-
|
|
131
|
-
# Test css_parser
|
|
132
|
-
css_parser = CssParser::Parser.new(import: false, io_exceptions: false)
|
|
133
|
-
css_parser.add_block!(css2)
|
|
134
|
-
css_parser_rules = 0
|
|
135
|
-
css_parser.each_selector { css_parser_rules += 1 }
|
|
136
|
-
puts "css_parser found #{css_parser_rules} rules"
|
|
137
|
-
|
|
138
|
-
unless cataract_rules == css_parser_rules
|
|
139
|
-
puts '⚠️ Different number of rules parsed'
|
|
140
|
-
puts ' Note: css_parser has a known bug with ::after pseudo-elements'
|
|
141
|
-
puts ' (it concatenates them with previous rules instead of parsing separately)'
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Show sample output
|
|
145
|
-
puts "\nSample Cataract output:"
|
|
146
|
-
parser.select(&:selector?).first(5).each do |rule|
|
|
147
|
-
puts " #{rule.selector}: #{rule.declarations} (spec: #{rule.specificity})"
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Run if executed directly
|
|
153
|
-
ParsingBenchmark.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'benchmark/ips'
|
|
4
|
-
|
|
5
|
-
# Load the local development version, not installed gem
|
|
6
|
-
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
7
|
-
require 'cataract'
|
|
8
|
-
|
|
9
|
-
# Get current branch name
|
|
10
|
-
branch = `git rev-parse --abbrev-ref HEAD`.strip
|
|
11
|
-
|
|
12
|
-
puts '=' * 80
|
|
13
|
-
puts 'RAGEL REMOVAL BENCHMARK - Parsing Performance Comparison'
|
|
14
|
-
puts '=' * 80
|
|
15
|
-
puts "Current branch: #{branch}"
|
|
16
|
-
puts
|
|
17
|
-
|
|
18
|
-
# Load bootstrap.css fixture (large real-world CSS file)
|
|
19
|
-
fixtures_dir = File.expand_path('../test/fixtures', __dir__)
|
|
20
|
-
bootstrap_css = File.read(File.join(fixtures_dir, 'bootstrap.css'))
|
|
21
|
-
|
|
22
|
-
puts "Bootstrap CSS: #{bootstrap_css.lines.count} lines, #{bootstrap_css.bytesize} bytes"
|
|
23
|
-
puts
|
|
24
|
-
|
|
25
|
-
# Verify parsing works
|
|
26
|
-
puts 'Verifying parsing works...'
|
|
27
|
-
parser = Cataract::Stylesheet.new
|
|
28
|
-
parser.add_block(bootstrap_css)
|
|
29
|
-
puts " ✅ Parsed successfully (#{parser.rules_count} rules)"
|
|
30
|
-
puts
|
|
31
|
-
|
|
32
|
-
puts '=' * 80
|
|
33
|
-
puts 'BENCHMARK: Bootstrap CSS Parsing'
|
|
34
|
-
puts '=' * 80
|
|
35
|
-
puts
|
|
36
|
-
|
|
37
|
-
Benchmark.ips do |x|
|
|
38
|
-
x.config(time: 5, warmup: 2)
|
|
39
|
-
|
|
40
|
-
x.report("#{branch}:parse_bootstrap") do
|
|
41
|
-
parser = Cataract::Stylesheet.new
|
|
42
|
-
parser.add_block(bootstrap_css)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
x.compare!
|
|
46
|
-
|
|
47
|
-
# Save results to file for cross-branch comparison
|
|
48
|
-
x.save! 'test/.benchmark_results/ragel_removal.json'
|
|
49
|
-
x.hold! 'test/.benchmark_results/ragel_removal.json'
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
puts
|
|
53
|
-
puts '=' * 80
|
|
54
|
-
puts 'Results saved to test/.benchmark_results/ragel_removal.json'
|
|
55
|
-
puts 'Switch git branches, recompile, and run again to compare!'
|
|
56
|
-
puts '=' * 80
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'benchmark/ips'
|
|
4
|
-
require 'json'
|
|
5
|
-
require 'fileutils'
|
|
6
|
-
|
|
7
|
-
# Unified benchmark runner that outputs both human-readable console output
|
|
8
|
-
# and structured JSON for documentation generation.
|
|
9
|
-
class BenchmarkRunner
|
|
10
|
-
RESULTS_DIR = File.expand_path('.results', __dir__)
|
|
11
|
-
|
|
12
|
-
attr_reader :name, :description, :metadata
|
|
13
|
-
|
|
14
|
-
# @param name [String] Short name for this benchmark (e.g., "parsing", "specificity")
|
|
15
|
-
# @param description [String] One-line description of what's being measured
|
|
16
|
-
# @param metadata [Hash] Additional metadata (fixture info, test cases, etc.)
|
|
17
|
-
def initialize(name:, description:, metadata: {})
|
|
18
|
-
@name = name
|
|
19
|
-
@description = description
|
|
20
|
-
@metadata = metadata
|
|
21
|
-
@results = []
|
|
22
|
-
|
|
23
|
-
FileUtils.mkdir_p(RESULTS_DIR)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Run a benchmark-ips block and capture results
|
|
27
|
-
# @yield [Benchmark::IPS::Job] The benchmark-ips job
|
|
28
|
-
def run
|
|
29
|
-
json_path = File.join(RESULTS_DIR, "#{@name}.json")
|
|
30
|
-
|
|
31
|
-
Benchmark.ips do |x|
|
|
32
|
-
# Allow benchmark to configure itself
|
|
33
|
-
yield x
|
|
34
|
-
|
|
35
|
-
# Enable JSON output
|
|
36
|
-
x.json!(json_path)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Read the generated JSON and enhance with metadata
|
|
40
|
-
raw_data = JSON.parse(File.read(json_path))
|
|
41
|
-
enhanced_data = {
|
|
42
|
-
'name' => @name,
|
|
43
|
-
'description' => @description,
|
|
44
|
-
'metadata' => @metadata,
|
|
45
|
-
'timestamp' => Time.now.iso8601,
|
|
46
|
-
'results' => raw_data
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
File.write(json_path, JSON.pretty_generate(enhanced_data))
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Helper to format results as a comparison hash
|
|
53
|
-
# @param label [String] Label for this result
|
|
54
|
-
# @param baseline [String] Baseline label to compare against
|
|
55
|
-
# @return [Hash] Structured comparison data
|
|
56
|
-
def self.format_comparison(label:, baseline:, results:)
|
|
57
|
-
baseline_result = results.find { |r| r['name'] == baseline }
|
|
58
|
-
comparison_result = results.find { |r| r['name'] == label }
|
|
59
|
-
|
|
60
|
-
return nil unless baseline_result && comparison_result
|
|
61
|
-
|
|
62
|
-
speedup = comparison_result['central_tendency'].to_f / baseline_result['central_tendency']
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
'label' => label,
|
|
66
|
-
'baseline' => baseline,
|
|
67
|
-
'speedup' => speedup.round(2)
|
|
68
|
-
}
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'benchmark_harness'
|
|
4
|
-
|
|
5
|
-
# Load the local development version, not installed gem
|
|
6
|
-
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
7
|
-
require 'cataract'
|
|
8
|
-
|
|
9
|
-
# CSS Serialization Performance Benchmark
|
|
10
|
-
class SerializationBenchmark < BenchmarkHarness
|
|
11
|
-
def self.benchmark_name
|
|
12
|
-
'serialization'
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def self.description
|
|
16
|
-
'Time to convert parsed CSS back to string format'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def self.metadata
|
|
20
|
-
instance = new
|
|
21
|
-
{
|
|
22
|
-
'test_cases' => [
|
|
23
|
-
{
|
|
24
|
-
'name' => "Full Serialization (Bootstrap CSS - #{(instance.bootstrap_css.length / 1024.0).round}KB)",
|
|
25
|
-
'key' => 'all',
|
|
26
|
-
'bytes' => instance.bootstrap_css.length
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
'name' => 'Media Type Filtering (print only)',
|
|
30
|
-
'key' => 'print',
|
|
31
|
-
'bytes' => instance.bootstrap_css.length
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Uses default speedup_config (test_case_key differs from parsing)
|
|
38
|
-
def self.speedup_config
|
|
39
|
-
{
|
|
40
|
-
baseline_matcher: SpeedupCalculator::Matchers.css_parser,
|
|
41
|
-
comparison_matcher: SpeedupCalculator::Matchers.cataract,
|
|
42
|
-
test_case_key: :key # serialization uses 'key' not 'fixture'
|
|
43
|
-
}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def sanity_checks
|
|
47
|
-
# Check css_parser gem is available
|
|
48
|
-
require 'css_parser'
|
|
49
|
-
|
|
50
|
-
# Verify Bootstrap fixture exists
|
|
51
|
-
raise "Bootstrap CSS fixture not found at #{bootstrap_path}" unless File.exist?(bootstrap_path)
|
|
52
|
-
|
|
53
|
-
# Verify parsing and serialization work
|
|
54
|
-
cataract_sheet = Cataract.parse_css(bootstrap_css)
|
|
55
|
-
raise 'Failed to parse Bootstrap CSS' if cataract_sheet.empty?
|
|
56
|
-
|
|
57
|
-
cataract_output = cataract_sheet.to_s
|
|
58
|
-
raise 'Serialization produced empty output' if cataract_output.empty?
|
|
59
|
-
|
|
60
|
-
# Verify output can be re-parsed
|
|
61
|
-
reparsed = Cataract.parse_css(cataract_output)
|
|
62
|
-
raise 'Failed to re-parse serialized output' if reparsed.empty?
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def call
|
|
66
|
-
validate_correctness
|
|
67
|
-
run_full_serialization_benchmark
|
|
68
|
-
run_media_filtering_benchmark
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def bootstrap_css
|
|
72
|
-
@bootstrap_css ||= File.read(bootstrap_path)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def bootstrap_path
|
|
78
|
-
@bootstrap_path ||= File.expand_path('../test/fixtures/bootstrap.css', __dir__)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def validate_correctness
|
|
82
|
-
puts '=' * 80
|
|
83
|
-
puts 'CORRECTNESS VALIDATION'
|
|
84
|
-
puts '=' * 80
|
|
85
|
-
puts "Input: Bootstrap CSS (#{bootstrap_css.length} bytes)"
|
|
86
|
-
|
|
87
|
-
# Parse with both libraries
|
|
88
|
-
cataract_sheet = Cataract.parse_css(bootstrap_css)
|
|
89
|
-
css_parser = CssParser::Parser.new
|
|
90
|
-
css_parser.add_block!(bootstrap_css)
|
|
91
|
-
|
|
92
|
-
# Serialize
|
|
93
|
-
cataract_output = cataract_sheet.to_s
|
|
94
|
-
css_parser_output = css_parser.to_s
|
|
95
|
-
|
|
96
|
-
puts "Cataract output: #{cataract_output.length} bytes (#{cataract_sheet.size} rules)"
|
|
97
|
-
puts "css_parser output: #{css_parser_output.length} bytes"
|
|
98
|
-
|
|
99
|
-
# Basic sanity check - outputs should be similar in size
|
|
100
|
-
size_ratio = cataract_output.length.to_f / css_parser_output.length
|
|
101
|
-
unless size_ratio > 0.8 && size_ratio < 1.2
|
|
102
|
-
puts "⚠️ Output sizes differ significantly (ratio: #{size_ratio.round(2)})"
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Check that output can be re-parsed
|
|
106
|
-
begin
|
|
107
|
-
reparsed = Cataract.parse_css(cataract_output)
|
|
108
|
-
puts "Re-parsed output: #{reparsed.size} rules"
|
|
109
|
-
rescue StandardError => e
|
|
110
|
-
puts "❌ Failed to re-parse: #{e.message}"
|
|
111
|
-
raise
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def run_full_serialization_benchmark
|
|
116
|
-
puts "\n#{'=' * 80}"
|
|
117
|
-
puts 'TEST: Full serialization (to_s)'
|
|
118
|
-
puts '=' * 80
|
|
119
|
-
puts '(Parsing done once before benchmark, not included in measurements)'
|
|
120
|
-
|
|
121
|
-
# Pre-parse CSS once (outside benchmark loop)
|
|
122
|
-
cataract_parsed = Cataract.parse_css(bootstrap_css)
|
|
123
|
-
css_parser_parsed = CssParser::Parser.new
|
|
124
|
-
css_parser_parsed.add_block!(bootstrap_css)
|
|
125
|
-
|
|
126
|
-
benchmark('all') do |x|
|
|
127
|
-
x.config(time: 5, warmup: 2)
|
|
128
|
-
|
|
129
|
-
x.report('css_parser: all') do
|
|
130
|
-
# Clear memoization if any
|
|
131
|
-
if css_parser_parsed.instance_variable_defined?(:@css_string)
|
|
132
|
-
css_parser_parsed.instance_variable_set(:@css_string, nil)
|
|
133
|
-
end
|
|
134
|
-
css_parser_parsed.to_s
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
x.report('cataract: all') do
|
|
138
|
-
# Clear memoization
|
|
139
|
-
cataract_parsed.instance_variable_set(:@serialized, nil)
|
|
140
|
-
cataract_parsed.to_s
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
x.compare!
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def run_media_filtering_benchmark
|
|
148
|
-
puts "\n#{'=' * 80}"
|
|
149
|
-
puts 'TEST: Media type filtering - to_s(:print)'
|
|
150
|
-
puts '=' * 80
|
|
151
|
-
puts 'Note: Using Parser API (css_parser compatible) not Stylesheet'
|
|
152
|
-
|
|
153
|
-
# Pre-parse using Parser API for media filtering
|
|
154
|
-
cataract_parser = Cataract::Stylesheet.new
|
|
155
|
-
cataract_parser.add_block(bootstrap_css)
|
|
156
|
-
|
|
157
|
-
css_parser_for_filter = CssParser::Parser.new
|
|
158
|
-
css_parser_for_filter.add_block!(bootstrap_css)
|
|
159
|
-
|
|
160
|
-
benchmark('print') do |x|
|
|
161
|
-
x.config(time: 5, warmup: 2)
|
|
162
|
-
|
|
163
|
-
x.report('css_parser: print') do
|
|
164
|
-
if css_parser_for_filter.instance_variable_defined?(:@css_string)
|
|
165
|
-
css_parser_for_filter.instance_variable_set(:@css_string, nil)
|
|
166
|
-
end
|
|
167
|
-
css_parser_for_filter.to_s(:print)
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
x.report('cataract: print') do
|
|
171
|
-
cataract_parser.to_s(media: :print)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
x.compare!
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Run if executed directly
|
|
180
|
-
SerializationBenchmark.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'benchmark/ips'
|
|
4
|
-
|
|
5
|
-
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
6
|
-
require 'cataract'
|
|
7
|
-
|
|
8
|
-
# Get current git branch
|
|
9
|
-
branch = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
|
|
10
|
-
branch = 'unknown' if branch.empty?
|
|
11
|
-
|
|
12
|
-
puts '=' * 60
|
|
13
|
-
puts "Shorthand Expansion/Creation Benchmark: #{branch}"
|
|
14
|
-
puts '=' * 60
|
|
15
|
-
puts ''
|
|
16
|
-
|
|
17
|
-
# Test cases for expansion
|
|
18
|
-
expansion_tests = {
|
|
19
|
-
'margin' => '10px 20px',
|
|
20
|
-
'padding' => '5px 10px 15px 20px',
|
|
21
|
-
'border' => '1px solid red',
|
|
22
|
-
'border-color' => 'red blue',
|
|
23
|
-
'font' => 'bold 14px/1.5 Arial, sans-serif',
|
|
24
|
-
'background' => 'url(image.png) no-repeat center/cover'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
# Test cases for shorthand creation
|
|
28
|
-
creation_tests = {
|
|
29
|
-
'margin' => {
|
|
30
|
-
'margin-top' => '10px',
|
|
31
|
-
'margin-right' => '20px',
|
|
32
|
-
'margin-bottom' => '10px',
|
|
33
|
-
'margin-left' => '20px'
|
|
34
|
-
},
|
|
35
|
-
'border' => {
|
|
36
|
-
'border-width' => '1px',
|
|
37
|
-
'border-style' => 'solid',
|
|
38
|
-
'border-color' => 'red'
|
|
39
|
-
},
|
|
40
|
-
'font' => {
|
|
41
|
-
'font-style' => 'italic',
|
|
42
|
-
'font-weight' => 'bold',
|
|
43
|
-
'font-size' => '14px',
|
|
44
|
-
'line-height' => '1.5',
|
|
45
|
-
'font-family' => 'Arial, sans-serif'
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
puts 'Expansion test cases:'
|
|
50
|
-
expansion_tests.each do |prop, value|
|
|
51
|
-
puts " #{prop}: '#{value}'"
|
|
52
|
-
end
|
|
53
|
-
puts ''
|
|
54
|
-
|
|
55
|
-
puts 'Shorthand creation test cases:'
|
|
56
|
-
creation_tests.each do |name, props|
|
|
57
|
-
puts " #{name}: #{props.length} properties"
|
|
58
|
-
end
|
|
59
|
-
puts ''
|
|
60
|
-
|
|
61
|
-
Benchmark.ips do |x|
|
|
62
|
-
x.config(time: 10, warmup: 3)
|
|
63
|
-
|
|
64
|
-
# Benchmark expansions
|
|
65
|
-
x.report("#{branch}:expand_margin ") do
|
|
66
|
-
Cataract.expand_margin(expansion_tests['margin'])
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
x.report("#{branch}:expand_padding ") do
|
|
70
|
-
Cataract.expand_padding(expansion_tests['padding'])
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
x.report("#{branch}:expand_border ") do
|
|
74
|
-
Cataract.expand_border(expansion_tests['border'])
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
x.report("#{branch}:expand_font ") do
|
|
78
|
-
Cataract.expand_font(expansion_tests['font'])
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
x.report("#{branch}:expand_background ") do
|
|
82
|
-
Cataract.expand_background(expansion_tests['background'])
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Benchmark shorthand creation
|
|
86
|
-
x.report("#{branch}:create_margin ") do
|
|
87
|
-
Cataract.create_margin_shorthand(creation_tests['margin'])
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
x.report("#{branch}:create_border ") do
|
|
91
|
-
Cataract.create_border_shorthand(creation_tests['border'])
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
x.report("#{branch}:create_font ") do
|
|
95
|
-
Cataract.create_font_shorthand(creation_tests['font'])
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
x.compare!
|
|
99
|
-
|
|
100
|
-
# Save results to file for cross-branch comparison
|
|
101
|
-
x.save! 'test/.benchmark_results/shorthand.json'
|
|
102
|
-
x.hold! 'test/.benchmark_results/shorthand.json'
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
puts ''
|
|
106
|
-
puts '=' * 60
|
|
107
|
-
puts 'Results saved to test/.benchmark_results/shorthand.json'
|
|
108
|
-
puts 'Switch git branches and run again to compare!'
|
|
109
|
-
puts '=' * 60
|