Skip to content

Commit add4e2e

Browse files
committed
Add bin/compare
Allows to compare the output of two different prism versions. It compiles prism twice and forks to allow for different versions in the same script. It then passes over minimal data to see if anything has changed. Most bugfixes should impact little to no real code and the test suite is already very extensive. Running this can give you even more confidence by comparing against real-world-rails or similar. There are some performance gains to be had here. Basically it is already parallelized because of `fork` but it can likely be even better. For simplicity (and because I don't usually write such code) I leave that as an exercise for the future. Just check it manually via the already existing tools.
1 parent 1da0733 commit add4e2e

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

bin/compare

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
# Usage: bin/compare main feature-branch .
5+
6+
$:.unshift(File.expand_path("../lib", __dir__))
7+
8+
require "json"
9+
require "socket"
10+
11+
def create_prism(ref)
12+
parent_socket, child_socket = UNIXSocket.pair
13+
14+
system("git checkout #{ref}", exception: true)
15+
system("bundle exec rake compile", exception: true)
16+
17+
pid = fork do
18+
parent_socket.close
19+
require "prism"
20+
21+
child_socket.puts("Compiling done for #{ref}")
22+
23+
while (data = child_socket.gets(chomp: true))
24+
command, path = data.split("\x00", 2)
25+
case command
26+
when "dump"
27+
begin
28+
child_socket.puts(Prism.dump_file(path).hash)
29+
rescue Errno::EISDIR
30+
# Folder might end with `.rb` and get caught by the glob
31+
child_socket.puts("")
32+
end
33+
when "details"
34+
parse_result = Prism.parse_file(path)
35+
child_socket.puts({
36+
valid: parse_result.success?,
37+
errors: parse_result.errors_format.hash,
38+
ast: parse_result.value.inspect.hash,
39+
}.to_json)
40+
else
41+
raise "Unknown command #{command}"
42+
end
43+
end
44+
45+
exit!(0)
46+
end
47+
48+
child_socket.close
49+
parent_socket.gets
50+
[pid, parent_socket]
51+
end
52+
53+
base_ref = ARGV.shift
54+
compare_ref = ARGV.shift
55+
path = ARGV.shift
56+
57+
pid_baseline, socket_baseline = create_prism(base_ref)
58+
pid_compare, socket_compare = create_prism(compare_ref)
59+
60+
result = +""
61+
files = Dir.glob(File.join(path, "**/*.rb"))
62+
63+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
64+
65+
def what_changed(baseline, compare, source_path)
66+
if baseline[:valid] != compare[:valid]
67+
"#{source_path} changed from valid(#{baseline[:valid]}) to valid(#{compare[:valid]})"
68+
elsif baseline[:valid] && compare[:valid] && baseline[:ast] != compare[:ast]
69+
"#{source_path} is syntax valid with changed ast}"
70+
elsif !baseline[:valid] && !compare[:valid] && baseline[:errors] != compare[:errors]
71+
"#{source_path} is syntax invalid with changed errors"
72+
else
73+
raise "Unknown condition for #{source_path}"
74+
end
75+
end
76+
77+
files.each_with_index do |source_path, i|
78+
puts "#{i}/#{files.size}" if i % 1000 == 0
79+
80+
socket_baseline.puts("dump\x00#{source_path}")
81+
socket_compare.puts("dump\x00#{source_path}")
82+
83+
dump_baseline = socket_baseline.gets(chomp: true)
84+
dump_compare = socket_compare.gets(chomp: true)
85+
86+
if dump_baseline != dump_compare
87+
socket_baseline.puts("details\x00#{source_path}")
88+
socket_compare.puts("details\x00#{source_path}")
89+
90+
details_baseline = JSON.parse(socket_baseline.gets(chomp: true), symbolize_names: true)
91+
details_compare = JSON.parse(socket_compare.gets(chomp: true), symbolize_names: true)
92+
result << what_changed(details_baseline, details_compare, source_path) + "\n"
93+
end
94+
end
95+
96+
if result.empty?
97+
puts "All good!"
98+
else
99+
puts "Oops:"
100+
puts result
101+
end
102+
103+
puts "Took #{Process.clock_gettime(Process::CLOCK_MONOTONIC) - start} seconds"
104+
105+
socket_baseline.close
106+
socket_compare.close
107+
Process.wait(pid_baseline)
108+
Process.wait(pid_compare)

0 commit comments

Comments
 (0)