-
Notifications
You must be signed in to change notification settings - Fork 0
/
database.rb
166 lines (151 loc) · 5.19 KB
/
database.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Read the CSV database of benchmarks.
require 'csv'
require 'fileutils'
require 'time'
require_relative 'result'
class Database
attr_reader :in_memory
# The names of expected repositories.
def Database.repositories
["released", "extra-dev"]
end
def initialize(folder)
# {architecture => repository => coq_version =>
# {time: time, results: {name => version => result}}}
@in_memory = {}
Dir.glob("#{folder}/*").map do |name|
# We allow standard files at the root of the database (LICENSE, ...).
if File.directory?(name) then
architecture = File.basename(name)
@in_memory[architecture] ||= {}
for repository in Database.repositories do
@in_memory[architecture][repository] ||= {}
Dir.glob("#{folder}/#{architecture}/#{repository}/*").map do |name|
coq_version = File.basename(name)
regexp = "#{folder}/#{architecture}/#{repository}/#{coq_version}/*.csv"
def time(file_name)
Time.strptime(File.basename(file_name, ".csv"), "%F_%T")
end
file_name = Dir.glob(regexp).max_by {|file_name| time(file_name)}
# TODO: check if file_name is empty
@in_memory[architecture][repository][coq_version] = {
time: time(file_name),
results: {}}
CSV.read(file_name, encoding: "binary")[1..-1].map do |row|
name = row[0][4..-1]
@in_memory[architecture][repository][coq_version][:results][name] ||= {}
version = row[1]
result = Result.new(*row[0..-1])
@in_memory[architecture][repository][coq_version][:results][name][version] = result
end
end
end
end
end
# A cache for the comparison of Coq versions.
@coq_versions_comparison = {}
end
# The architectures present in the database.
def architectures
@in_memory.keys.sort
end
# The more recent bench for an architecture.
def get_last_bench_by_architecture(architecture, repository)
last = @in_memory[architecture][repository].max_by {|_, results| results[:time]}
if last then
last[1][:time]
else
nil
end
end
# This list of bench results which finished during the last nb_hours.
def get_benches_of_the_past_hours(nb_hours, repository)
benches = []
for architecture, architecture_benches in @in_memory do
for coq, bench in architecture_benches[repository] do
if Time.now - bench[:time] < 3600 * nb_hours then
benches << {
architecture: architecture,
coq: coq,
repository: repository,
results: bench[:results]
}
end
end
end
benches
end
# The Coq versions tested for a given architecture and repository.
def coq_versions(architecture, repository)
@in_memory[architecture][repository].keys.sort {|x, y| compare_versions(x, y)}
end
# {name => version => coq_version => result}
def packages_hash(architecture, repository)
output = {}
for coq_version in coq_versions(architecture, repository) do
for name, results in @in_memory[architecture][repository][coq_version][:results] do
output[name] ||= {}
for version, result in results do
output[name][version] ||= {}
output[name][version][coq_version] = result
end
end
end
output
end
# The number of packages for an architecture and repository, using the minimum
# of number of packages for the last bench of each Coq version.
def number_of_packages(architecture, repository)
n_min = nil
for coq_version in coq_versions(architecture, repository) do
n = 0
for _, results in @in_memory[architecture][repository][coq_version][:results] do
n += results.size
end
n_min = n_min ? [n_min, n].min : n
end
n_min ? n_min : 0
end
# Statistics about a package.
def stats(architecture, repository, coq_version)
output = [0, 0, 0, 0, 0]
for name, results in @in_memory[architecture][repository][coq_version][:results] do
for version, result in results do
output[result.status.to_i] += 1
end
end
output
end
# Like `stats`, with the worst results for all coq versions.
def worst_stats(architecture, repository)
output = [0, 0, 0, 0, 0]
for _, results in packages_hash(architecture, repository) do
for _, results in results do
worst = output.size - 1
for coq_version in coq_versions(architecture, repository) do
if result = results[coq_version] then
worst = [worst, result.status.to_i].min
end
end
output[worst] += 1
end
end
output
end
# Compare two version numbers using the dpkg algorithm (the Debian package
# manager). Needs a Debian-based distribution.
def compare_versions(x, y)
@coq_versions_comparison[x] ||= {}
@coq_versions_comparison[x][y] ||= begin
if x == y then
0
# We use `dpkg` to compare two versions numbers according to the OPAM policy.
elsif system("dpkg --compare-versions #{x} lt #{y} 2>/dev/null")
-1
else
+1
end
end
@coq_versions_comparison[x][y]
end
end