Skip to content

Commit 9ff28ed

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 be even better. For simplicity (and because I don't usually write such code) I leave that as an exercise for the future. The message on change can also be improved, but right now I'm not certain that's worth the effort. Just check it manually via the already existing tools.
1 parent 1da0733 commit 9ff28ed

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

bin/compare

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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(path = child_socket.gets(chomp: true))
24+
begin
25+
result = Prism.parse(File.read(path), version: "latest")
26+
child_socket.puts(serialize_parse_result(result).to_json)
27+
rescue Errno::EISDIR
28+
# Folder might end with `.rb` and get caught by the glob
29+
child_socket.puts("{}")
30+
end
31+
end
32+
33+
exit!(0)
34+
end
35+
36+
child_socket.close
37+
parent_socket.gets
38+
[pid, parent_socket]
39+
end
40+
41+
def serialize_parse_result(parse_result)
42+
{
43+
valid: parse_result.success?,
44+
errors: parse_result.errors_format.hash,
45+
ast: parse_result.value.inspect.hash,
46+
}
47+
end
48+
49+
base_ref = ARGV.shift
50+
compare_ref = ARGV.shift
51+
path = ARGV.shift
52+
53+
pid_baseline, socket_baseline = create_prism(base_ref)
54+
pid_compare, socket_compare = create_prism(compare_ref)
55+
56+
result = +""
57+
files = Dir.glob(File.join(path, "**/*.rb"))
58+
files.each_with_index do |source_path, i|
59+
puts "#{i}/#{files.size}" if i % 1000 == 0
60+
61+
socket_baseline.puts(source_path)
62+
socket_compare.puts(source_path)
63+
64+
baseline = JSON.parse(socket_baseline.gets(chomp: true), symbolize_names: true)
65+
compare = JSON.parse(socket_compare.gets(chomp: true), symbolize_names: true)
66+
67+
if compare[:valid] != baseline[:valid]
68+
result << "#{source_path} changed from valid(#{baseline[:valid]}) to valid(#{compare[:valid]})\n"
69+
elsif compare[:valid] && baseline[:valid] && compare[:ast] != baseline[:ast]
70+
result << "#{source_path} is syntax valid with changed ast}\n"
71+
elsif !compare[:valid] && !baseline[:valid] && compare[:errors] != baseline[:errors]
72+
result << "#{source_path} is syntax invalid with changed errors\n"
73+
end
74+
end
75+
76+
if result.empty?
77+
puts "All good!"
78+
else
79+
puts "Oops:"
80+
puts result
81+
end
82+
83+
socket_baseline.close
84+
socket_compare.close
85+
Process.wait(pid_baseline)
86+
Process.wait(pid_compare)

0 commit comments

Comments
 (0)