mesa_test 0.2.15 → 1.0.4
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/bin/mesa_test +262 -432
- data/lib/mesa_test.rb +460 -1172
- metadata +6 -7
data/lib/mesa_test.rb
CHANGED
|
@@ -11,9 +11,10 @@ require 'json'
|
|
|
11
11
|
MesaDirError = Class.new(StandardError)
|
|
12
12
|
TestCaseDirError = Class.new(StandardError)
|
|
13
13
|
InvalidDataType = Class.new(StandardError)
|
|
14
|
+
GitHubError = Class.new(StandardError)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
GITHUB_HTTPS = 'https://github.com/MESAHub/mesa.git'.freeze
|
|
17
|
+
GITHUB_SSH = 'git@github.com:MESAHub/mesa.git'.freeze
|
|
17
18
|
|
|
18
19
|
class MesaTestSubmitter
|
|
19
20
|
DEFAULT_URI = 'https://mesa-test-hub.herokuapp.com'.freeze
|
|
@@ -28,7 +29,8 @@ shown in parentheses at the end of a prompt. Pressing enter will accept the
|
|
|
28
29
|
default values.
|
|
29
30
|
|
|
30
31
|
To submit to MESATestHub, a valid computer name, email address, and password
|
|
31
|
-
are all required.
|
|
32
|
+
are all required. To actually run a test, you need to specify a location for
|
|
33
|
+
your base MESA git repository. All other data are useful, but optional. Any data
|
|
32
34
|
transferred to MESATestHub will be encrypted via HTTPS, but be warned that your
|
|
33
35
|
e-mail and password will be stored in plain text.'
|
|
34
36
|
# Get computer name
|
|
@@ -36,11 +38,6 @@ e-mail and password will be stored in plain text.'
|
|
|
36
38
|
"(#{s.computer_name}):", :blue)
|
|
37
39
|
s.computer_name = response unless response.empty?
|
|
38
40
|
|
|
39
|
-
# Get user name
|
|
40
|
-
response = shell.ask 'What is the name of the operator of this ' \
|
|
41
|
-
"computer? (#{s.user_name}):", :blue
|
|
42
|
-
s.user_name = response unless response.empty?
|
|
43
|
-
|
|
44
41
|
# Get user e-mail
|
|
45
42
|
response = shell.ask 'What is the email you can be reached ' \
|
|
46
43
|
"at (required)? (#{s.email}):", :blue
|
|
@@ -51,59 +48,54 @@ e-mail and password will be stored in plain text.'
|
|
|
51
48
|
"#{s.email} (required)? (#{s.password})", :blue
|
|
52
49
|
s.password = response unless response.empty?
|
|
53
50
|
|
|
51
|
+
# Determine if we'll use ssh or https to access github
|
|
52
|
+
response = shell.ask 'When accessing GitHub, which protocol do you '\
|
|
53
|
+
'want to use? ', :blue, limited_to: %w[ssh https]
|
|
54
|
+
s.github_protocol = response.strip.downcase.to_sym
|
|
55
|
+
|
|
56
|
+
# Get location of source MESA repo (the mirror)
|
|
57
|
+
response = shell.ask "Where is/should your mirrored MESA repository " \
|
|
58
|
+
"located? This is where a mirror will be stored from which test " \
|
|
59
|
+
"repos will be generated. You won't touch this in regular operation. " \
|
|
60
|
+
"(#{s.mesa_mirror}):", :blue
|
|
61
|
+
s.mesa_mirror = response unless response.empty?
|
|
62
|
+
|
|
63
|
+
# Get location of source MESA work (where testing happens)
|
|
64
|
+
response = shell.ask "Where is/should your working directory for "\
|
|
65
|
+
"testing be located? This is where testing actually occurs, but all "\
|
|
66
|
+
"files it uses are cached in the mirror repo to save time later. " \
|
|
67
|
+
"(#{s.mesa_work}):", :blue
|
|
68
|
+
s.mesa_work = response unless response.empty?
|
|
69
|
+
|
|
54
70
|
# Get platform information
|
|
55
71
|
response = shell.ask 'What is the platform of this computer (eg. ' \
|
|
56
72
|
"macOS, Ubuntu)? (#{s.platform}):", :blue
|
|
57
73
|
s.platform = response unless response.empty?
|
|
58
|
-
response = shell.ask 'What is the version of the platform (eg. 10.
|
|
59
|
-
"16.04)? (#{s.platform_version}):", :blue
|
|
74
|
+
response = shell.ask 'What is the version of the platform (eg. 10.15.5, ' \
|
|
75
|
+
"Ubuntu 16.04)? (#{s.platform_version}):", :blue
|
|
60
76
|
s.platform_version = response unless response.empty?
|
|
61
77
|
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
#
|
|
68
|
-
response = shell.ask 'How much RAM (in integer GB) does this computer ' \
|
|
69
|
-
"have (eg. 8)? (#{s.ram_gb}) ", :blue
|
|
70
|
-
s.ram_gb = response.to_i unless response.empty?
|
|
71
|
-
|
|
72
|
-
# Get compiler information
|
|
73
|
-
response = shell.ask "Which compiler are you using? (#{s.compiler}):",
|
|
74
|
-
:blue, limited_to: ['', 'SDK', 'gfortran', 'ifort']
|
|
75
|
-
s.compiler = response unless response.empty?
|
|
76
|
-
|
|
77
|
-
# Get compiler version
|
|
78
|
-
response = shell.ask 'What version of the compiler (eg. 20170921 or ' \
|
|
79
|
-
"7.2.0)? (#{s.compiler_version}): ", :blue
|
|
80
|
-
s.compiler_version = response unless response.empty?
|
|
81
|
-
|
|
82
|
-
# Get earliest revision to check
|
|
83
|
-
response = shell.ask "What's the earliest revision to search back to " \
|
|
84
|
-
'when finding the latest testable revision (eg. 10000)? ' \
|
|
85
|
-
"(#{s.last_tested}): ", :blue
|
|
86
|
-
s.last_tested = response.to_i unless response.empty?
|
|
87
|
-
|
|
88
|
-
# Confirm save location
|
|
89
|
-
response = shell.ask "This will be saved in #{s.config_file}. Press " \
|
|
90
|
-
'enter to accept or enter a new location:', :blue, path: true
|
|
91
|
-
s.config_file = response unless response.empty?
|
|
78
|
+
# we are powerless to do change the location for now, so stop asking
|
|
79
|
+
# about it
|
|
80
|
+
# # Confirm save location
|
|
81
|
+
# response = shell.ask "This will be saved in #{s.config_file}. Press " \
|
|
82
|
+
# 'enter to accept or enter a new location:', :blue, path: true
|
|
83
|
+
# s.config_file = response unless response.empty?
|
|
92
84
|
end
|
|
93
85
|
|
|
94
86
|
# Confirm data. If not confirmed, restart whole wizard.
|
|
95
87
|
if confirm_computer_data
|
|
96
88
|
save_computer_data
|
|
97
89
|
else
|
|
98
|
-
|
|
90
|
+
shell.say "Restarting wizard.\n"
|
|
99
91
|
setup
|
|
100
92
|
end
|
|
101
93
|
end
|
|
102
94
|
|
|
103
95
|
def self.new_from_config(
|
|
104
|
-
config_file: File.join(ENV['HOME'], '.mesa_test.yml'),
|
|
96
|
+
config_file: File.join(ENV['HOME'], '.mesa_test', 'config.yml'),
|
|
97
|
+
force_setup: false,
|
|
105
98
|
base_uri: DEFAULT_URI
|
|
106
|
-
# base_uri: 'http://localhost:3000'
|
|
107
99
|
)
|
|
108
100
|
new_submitter = new(config_file: config_file, base_uri: base_uri)
|
|
109
101
|
if force_setup
|
|
@@ -117,22 +109,27 @@ e-mail and password will be stored in plain text.'
|
|
|
117
109
|
end
|
|
118
110
|
|
|
119
111
|
attr_accessor :computer_name, :user_name, :email, :password, :platform,
|
|
120
|
-
:
|
|
121
|
-
:
|
|
112
|
+
:mesa_mirror, :mesa_work, :platform_version, :processor,
|
|
113
|
+
:config_file, :base_uri, :last_tested, :github_protocol
|
|
122
114
|
|
|
123
115
|
attr_reader :shell
|
|
124
116
|
|
|
125
117
|
# many defaults are set in body
|
|
126
118
|
def initialize(
|
|
127
|
-
computer_name: nil, user_name: nil, email: nil,
|
|
128
|
-
|
|
129
|
-
|
|
119
|
+
computer_name: nil, user_name: nil, email: nil, github_protocol: nil,
|
|
120
|
+
mesa_mirror: nil, platform: nil, platform_version: nil, processor: nil,
|
|
121
|
+
config_file: nil, base_uri: nil, last_tested: nil
|
|
130
122
|
)
|
|
131
123
|
@computer_name = computer_name || Socket.gethostname.scan(/^[^\.]+\.?/)[0]
|
|
132
124
|
@computer_name.chomp!('.') if @computer_name
|
|
133
125
|
@user_name = user_name || (ENV['USER'] || ENV['USERNAME'])
|
|
134
126
|
@email = email || ''
|
|
135
127
|
@password = password || ''
|
|
128
|
+
@github_protocol = github_protocol || :ssh
|
|
129
|
+
@mesa_mirror = mesa_mirror ||
|
|
130
|
+
File.join(ENV['HOME'], '.mesa_test', 'mirror')
|
|
131
|
+
@mesa_work = mesa_work ||
|
|
132
|
+
File.join(ENV['HOME'], '.mesa_test', 'work')
|
|
136
133
|
@platform = platform
|
|
137
134
|
if @platform.nil?
|
|
138
135
|
@platform =
|
|
@@ -146,17 +143,15 @@ e-mail and password will be stored in plain text.'
|
|
|
146
143
|
end
|
|
147
144
|
@platform_version = platform_version || ''
|
|
148
145
|
@processor = processor || ''
|
|
149
|
-
@
|
|
150
|
-
|
|
151
|
-
@compiler_version = compiler_version || ''
|
|
152
|
-
@config_file = config_file || File.join(ENV['HOME'], '.mesa_test.yml')
|
|
146
|
+
@config_file = config_file || File.join(ENV['HOME'], '.mesa_test',
|
|
147
|
+
'config.yml')
|
|
153
148
|
@base_uri = base_uri
|
|
154
|
-
@last_tested = last_tested || DEFAULT_REVISION
|
|
155
149
|
|
|
156
150
|
# set up thor-proof way to get responses from user. Thor hijacks the
|
|
157
151
|
# gets command, so we have to use its built-in "ask" method, which is
|
|
158
152
|
# actually more useful
|
|
159
153
|
@shell = Thor::Shell::Color.new
|
|
154
|
+
|
|
160
155
|
yield self if block_given?
|
|
161
156
|
end
|
|
162
157
|
|
|
@@ -169,15 +164,13 @@ e-mail and password will be stored in plain text.'
|
|
|
169
164
|
puts 'Ready to submit the following data:'
|
|
170
165
|
puts '-------------------------------------------------------'
|
|
171
166
|
puts "Computer Name #{computer_name}"
|
|
172
|
-
puts "User Name #{user_name}"
|
|
173
167
|
puts "User email #{email}"
|
|
174
168
|
puts 'Password ***********'
|
|
169
|
+
puts "GitHub Protocol #{github_protocol}"
|
|
170
|
+
puts "MESA Mirror Location #{mesa_mirror}"
|
|
171
|
+
puts "MESA Work Location #{mesa_work}"
|
|
175
172
|
puts "Platform #{platform} #{platform_version}"
|
|
176
|
-
puts "
|
|
177
|
-
puts "RAM #{ram_gb} GB"
|
|
178
|
-
puts "Compiler #{compiler} #{compiler_version}"
|
|
179
|
-
puts "Last tested revision #{last_tested}"
|
|
180
|
-
puts "Config location #{config_file}"
|
|
173
|
+
# puts "Config location #{config_file}"
|
|
181
174
|
puts '-------------------------------------------------------'
|
|
182
175
|
puts ''
|
|
183
176
|
response = shell.ask 'Is this correct? (y/Y = Yes, anything else = No):'
|
|
@@ -189,158 +182,114 @@ e-mail and password will be stored in plain text.'
|
|
|
189
182
|
# change platforms (i.e. switch from mac to linux, or change between linux
|
|
190
183
|
# flavors), you should create a new computer account. Similarly, create new
|
|
191
184
|
# computer accounts if you change your RAM or processor. You do not need
|
|
192
|
-
# to change computers if you upgrade your platform (macOS 10.12 -> 10.13
|
|
193
|
-
# if you try different compilers
|
|
194
|
-
#
|
|
195
|
-
# Note this is NOT checked! The server really only uses the test-by-test
|
|
196
|
-
# quantities (platform version, compiler, compiler version) and the
|
|
197
|
-
# computer name. Once the computer is found (by the name) all the other
|
|
198
|
-
# data is assumed to be fixed. The others... probably shouldn't be here,
|
|
199
|
-
# but remain so you can confirm that the computer on the web server is the
|
|
200
|
-
# same one you think you are working with locally.
|
|
185
|
+
# to change computers if you upgrade your platform (macOS 10.12 -> 10.13
|
|
201
186
|
def save_computer_data
|
|
202
187
|
data_hash = {
|
|
203
188
|
'computer_name' => computer_name,
|
|
204
|
-
'user_name' => user_name,
|
|
205
189
|
'email' => email,
|
|
206
190
|
'password' => password,
|
|
191
|
+
'github_protocol' => github_protocol,
|
|
192
|
+
'mesa_mirror' => mesa_mirror,
|
|
193
|
+
'mesa_work' => mesa_work,
|
|
207
194
|
'platform' => platform,
|
|
208
|
-
'processor' => processor,
|
|
209
|
-
'ram_gb' => ram_gb,
|
|
210
195
|
'platform_version' => platform_version,
|
|
211
|
-
'compiler' => compiler,
|
|
212
|
-
'compiler_version' => compiler_version,
|
|
213
|
-
'last_tested' => last_tested
|
|
214
196
|
}
|
|
197
|
+
# make sure there's a directory to write to
|
|
198
|
+
unless dir_or_symlink_exists? File.dirname(config_file)
|
|
199
|
+
FileUtils.mkdir_p File.dirname(config_file)
|
|
200
|
+
end
|
|
215
201
|
File.open(config_file, 'w') { |f| f.write(YAML.dump(data_hash)) }
|
|
216
202
|
end
|
|
217
203
|
|
|
218
204
|
def load_computer_data
|
|
219
205
|
data_hash = YAML.safe_load(File.read(config_file), [Symbol])
|
|
220
206
|
@computer_name = data_hash['computer_name']
|
|
221
|
-
@user_name = data_hash['user_name']
|
|
222
207
|
@email = data_hash['email']
|
|
223
208
|
@password = data_hash['password']
|
|
209
|
+
@github_protocol = data_hash['github_protocol'].to_sym
|
|
210
|
+
@mesa_mirror = data_hash['mesa_mirror']
|
|
211
|
+
@mesa_work = data_hash['mesa_work']
|
|
224
212
|
@platform = data_hash['platform']
|
|
225
|
-
@processor = data_hash['processor']
|
|
226
|
-
@ram_gb = data_hash['ram_gb']
|
|
227
213
|
@platform_version = data_hash['platform_version']
|
|
228
|
-
@compiler = data_hash['compiler']
|
|
229
|
-
@compiler_version = data_hash['compiler_version']
|
|
230
|
-
@last_tested = data_hash['last_tested'] || @last_tested
|
|
231
214
|
end
|
|
232
215
|
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
res = {
|
|
238
|
-
test_case: test_case.test_name,
|
|
239
|
-
mod: test_case.mod,
|
|
240
|
-
computer: computer_name,
|
|
216
|
+
# Parameters to be submitted in JSON format for reporting information about
|
|
217
|
+
# the submitting user and computer
|
|
218
|
+
def submitter_params
|
|
219
|
+
{
|
|
241
220
|
email: email,
|
|
242
221
|
password: password,
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
total_runtime_seconds: test_case.total_runtime_seconds,
|
|
246
|
-
mesa_version: test_case.mesa_version,
|
|
247
|
-
passed: test_case.passed? ? 1 : 0,
|
|
248
|
-
compiler: test_case.compiler || compiler,
|
|
249
|
-
compiler_version: test_case.compiler_version || compiler_version,
|
|
250
|
-
platform_version: platform_version,
|
|
251
|
-
omp_num_threads: test_case.test_omp_num_threads,
|
|
252
|
-
success_type: test_case.success_type,
|
|
253
|
-
failure_type: test_case.failure_type,
|
|
254
|
-
steps: test_case.steps,
|
|
255
|
-
retries: test_case.retries,
|
|
256
|
-
backups: test_case.backups,
|
|
257
|
-
diff: test_case.diff,
|
|
258
|
-
checksum: test_case.checksum,
|
|
259
|
-
rn_mem: test_case.rn_mem,
|
|
260
|
-
re_mem: test_case.re_mem,
|
|
261
|
-
summary_text: test_case.summary_text
|
|
222
|
+
computer: computer_name,
|
|
223
|
+
platform_version: platform_version
|
|
262
224
|
}
|
|
263
|
-
|
|
264
|
-
# enter in test-specific data, DISABLED FOR NOW
|
|
265
|
-
# test_case.data_names.each do |data_name|
|
|
266
|
-
# unless test_case.data[data_name].nil?
|
|
267
|
-
# res[data_name] = test_case.data[data_name]
|
|
268
|
-
# end
|
|
269
|
-
# end
|
|
270
|
-
res
|
|
271
225
|
end
|
|
272
226
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
227
|
+
# Parameters to be submitted in JSON format for reporting information about
|
|
228
|
+
# the overall commit being tested; used even if only submitting an entire
|
|
229
|
+
# test. This also determines if the submission is for an entire commit
|
|
230
|
+
# (compilation information and every test), an empty commit (just
|
|
231
|
+
# compilation information), or a non-empty, but also non-entire submission
|
|
232
|
+
# (results for a single test without compilation information)
|
|
233
|
+
def commit_params(mesa, entire: true, empty: false)
|
|
234
|
+
# the compiler data should be able to be used as-is, but right now the
|
|
235
|
+
# names don't match with what the database expects, so we do some renaming
|
|
236
|
+
# shenanigans.
|
|
237
|
+
#
|
|
238
|
+
####################################
|
|
239
|
+
# THIS SHOULD GO BEFORE PRODUCTION #
|
|
240
|
+
{
|
|
241
|
+
sha: mesa.sha,
|
|
242
|
+
compiled: mesa.installed?,
|
|
243
|
+
entire: entire,
|
|
244
|
+
empty: empty,
|
|
245
|
+
}.merge(mesa.compiler_hash)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Given a valid +Mesa+ object, create an array of hashes that describe the
|
|
249
|
+
# test cases and the test results. These will be encoded as an array of
|
|
250
|
+
# JSON objects.
|
|
251
|
+
def instance_params(mesa)
|
|
296
252
|
has_errors = []
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
# information that requires the web app to work, stored in :extra
|
|
300
|
-
mesa.test_names.each do |mod, names|
|
|
253
|
+
res = []
|
|
254
|
+
mesa.test_case_names.each do |mod, names|
|
|
301
255
|
names.each do |test_name|
|
|
302
256
|
begin
|
|
303
257
|
test_case = mesa.test_cases[mod][test_name]
|
|
304
|
-
res
|
|
305
|
-
test_instance: {
|
|
306
|
-
runtime_seconds: test_case.runtime_seconds,
|
|
307
|
-
re_time: test_case.re_time,
|
|
308
|
-
total_runtime_seconds: test_case.total_runtime_seconds,
|
|
309
|
-
passed: test_case.passed?,
|
|
310
|
-
compiler: test_case.compiler || compiler,
|
|
311
|
-
compiler_version: test_case.compiler_version || compiler_version,
|
|
312
|
-
platform_version: platform_version,
|
|
313
|
-
omp_num_threads: test_case.test_omp_num_threads,
|
|
314
|
-
success_type: test_case.success_type,
|
|
315
|
-
failure_type: test_case.failure_type,
|
|
316
|
-
steps: test_case.steps,
|
|
317
|
-
retries: test_case.retries,
|
|
318
|
-
backups: test_case.backups,
|
|
319
|
-
diff: test_case.diff,
|
|
320
|
-
checksum: test_case.checksum,
|
|
321
|
-
rn_mem: test_case.rn_mem,
|
|
322
|
-
re_mem: test_case.re_mem,
|
|
323
|
-
summary_text: test_case.summary_text
|
|
324
|
-
},
|
|
325
|
-
extra: { test_case: test_name, mod: mod }
|
|
326
|
-
}
|
|
258
|
+
res << test_case.results_hash
|
|
327
259
|
rescue TestCaseDirError
|
|
328
|
-
shell.say "
|
|
329
|
-
|
|
260
|
+
# shell.say "It appears that #{test_case.test_name} has not been "\
|
|
261
|
+
# 'run yet. Unable to submit data for this test.', :red
|
|
330
262
|
has_errors << test_case
|
|
331
263
|
end
|
|
332
264
|
end
|
|
333
265
|
end
|
|
334
|
-
|
|
266
|
+
unless has_errors.empty?
|
|
267
|
+
shell.say "The following test cases could NOT be read for submission:",
|
|
268
|
+
:red
|
|
269
|
+
has_errors.each do |test_case|
|
|
270
|
+
shell.say "- #{test_case.test_name}", :red
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
res
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Parameters for a single test case. +mesa+ is an instance of +Mesa+, and
|
|
277
|
+
# +test_case+ is an instance of MesaTestCase representing the test case to
|
|
278
|
+
# be submitted
|
|
279
|
+
def single_instance_params(test_case)
|
|
280
|
+
[test_case.results_hash]
|
|
335
281
|
end
|
|
336
282
|
|
|
283
|
+
# Phone home to testhub and confirm that computer and user are valid. Useful
|
|
284
|
+
# for confirming that submissions will be accepted before wasting time on a
|
|
285
|
+
# test later.
|
|
337
286
|
def confirm_computer
|
|
338
287
|
uri = URI.parse(base_uri + '/check_computer.json')
|
|
339
288
|
https = Net::HTTP.new(uri.hostname, uri.port)
|
|
340
289
|
https.use_ssl = base_uri.include? 'https'
|
|
341
290
|
|
|
342
291
|
request = Net::HTTP::Post.new(
|
|
343
|
-
uri, initheader = { 'Content-Type' => 'application/json' }
|
|
292
|
+
uri, initheader = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
|
|
344
293
|
)
|
|
345
294
|
request.body = {
|
|
346
295
|
email: email,
|
|
@@ -350,295 +299,241 @@ e-mail and password will be stored in plain text.'
|
|
|
350
299
|
JSON.parse(https.request(request).body).to_hash
|
|
351
300
|
end
|
|
352
301
|
|
|
353
|
-
#
|
|
354
|
-
#
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
302
|
+
# submit entire commit's worth of test cases, OR submit compilation status
|
|
303
|
+
# and NO test cases
|
|
304
|
+
def submit_commit(mesa, empty: false)
|
|
305
|
+
unless mesa.install_attempted?
|
|
306
|
+
raise MesaDirError, 'No testhub.yml file found in installation; '\
|
|
307
|
+
'must attempt to install before subitting.'
|
|
308
|
+
end
|
|
309
|
+
uri = URI.parse(base_uri + '/submissions/create.json')
|
|
359
310
|
https = Net::HTTP.new(uri.hostname, uri.port)
|
|
360
311
|
https.use_ssl = true if base_uri.include? 'https'
|
|
361
312
|
|
|
362
313
|
request = Net::HTTP::Post.new(
|
|
363
314
|
uri,
|
|
364
|
-
initheader = { 'Content-Type' => 'application/json' }
|
|
315
|
+
initheader = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
|
|
365
316
|
)
|
|
366
|
-
begin
|
|
367
|
-
request.body = submit_params(test_case).to_json
|
|
368
|
-
rescue TestCaseDirError
|
|
369
|
-
shell.say "\nPassage status for #{test_case.test_name} not yet known. " \
|
|
370
|
-
'Run test first and then submit.', :red
|
|
371
|
-
return false
|
|
372
|
-
end
|
|
373
317
|
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
#
|
|
318
|
+
# create the request body for submission to the submissions API
|
|
319
|
+
#
|
|
320
|
+
# if we have an empty submission, then it is necessarily not entire.
|
|
321
|
+
# Similarly, a non-empty submission is necessarily entire (otherwise one
|
|
322
|
+
# would use +submit_instance+)
|
|
323
|
+
request_data = {submitter: submitter_params,
|
|
324
|
+
commit: commit_params(mesa, empty: empty, entire: !empty)}
|
|
325
|
+
# don't need test instances if it's an empty submission or if compilation
|
|
326
|
+
# failed
|
|
327
|
+
if !empty && request_data[:commit][:compiled]
|
|
328
|
+
request_data[:instances] = instance_params(mesa)
|
|
329
|
+
end
|
|
330
|
+
request.body = request_data.to_json
|
|
377
331
|
|
|
332
|
+
# actually do the submission
|
|
378
333
|
response = https.request request
|
|
379
|
-
# puts JSON.parse(response.body).to_hash if verbose
|
|
380
|
-
response.is_a? Net::HTTPCreated
|
|
381
|
-
end
|
|
382
334
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
success = true
|
|
388
|
-
mesa.test_names.each_key do |this_mod|
|
|
389
|
-
success &&= submit_all(mesa, mod = this_mod)
|
|
390
|
-
end
|
|
335
|
+
if !response.is_a? Net::HTTPCreated
|
|
336
|
+
shell.say "\nFailed to submit some or all test case instances and/or "\
|
|
337
|
+
'commit data.', :red
|
|
338
|
+
false
|
|
391
339
|
else
|
|
392
|
-
mesa.
|
|
393
|
-
|
|
394
|
-
test_case = mesa.test_cases[mod][test_name]
|
|
395
|
-
# try to submit and note if it does or doesn't successfully submit
|
|
396
|
-
submitted = false
|
|
397
|
-
submitted = submit(test_case) unless test_case.outcome == :not_tested
|
|
398
|
-
if submitted
|
|
399
|
-
submitted_cases << test_name
|
|
400
|
-
else
|
|
401
|
-
unsubmitted_cases << test_name
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
puts "\nSubmission results for #{mod} module:"
|
|
405
|
-
puts '#####################################'
|
|
406
|
-
if !submitted_cases.empty?
|
|
407
|
-
shell.say 'Submitted the following cases:', :green
|
|
408
|
-
puts submitted_cases.join("\n")
|
|
409
|
-
else
|
|
410
|
-
shell.say 'Did not successfully submit any cases.', :red
|
|
411
|
-
end
|
|
412
|
-
unless unsubmitted_cases.empty?
|
|
413
|
-
puts "\n\n\n"
|
|
414
|
-
shell.say 'Failed to submit the following cases:', :red
|
|
415
|
-
puts unsubmitted_cases.join("\n")
|
|
416
|
-
end
|
|
417
|
-
# return true and update last tested if all cases were submitted
|
|
418
|
-
success = submitted_cases.length == mesa.test_names[mod].length
|
|
419
|
-
if success
|
|
420
|
-
@last_tested = mesa.version_number
|
|
421
|
-
shell.say "\n\nUpdating last tested revision to #{last_tested}."
|
|
422
|
-
save_computer_data
|
|
423
|
-
end
|
|
340
|
+
shell.say "\nSuccessfully submitted commit #{mesa.sha}.", :green
|
|
341
|
+
true
|
|
424
342
|
end
|
|
425
|
-
# return boolean indicating whether or not all cases successfully
|
|
426
|
-
# SUBMITTED (irrespective of passing status)
|
|
427
|
-
success
|
|
428
343
|
end
|
|
429
344
|
|
|
430
|
-
#
|
|
431
|
-
#
|
|
432
|
-
def
|
|
433
|
-
|
|
345
|
+
# submit results for a single test case instance. Does *not* report overall
|
|
346
|
+
# compilation status to testhub. Use an empty commit submission for that
|
|
347
|
+
def submit_instance(mesa, test_case)
|
|
348
|
+
unless mesa.install_attempted?
|
|
349
|
+
raise MesaDirError, 'No testhub.yml file found in installation; '\
|
|
350
|
+
'must attempt to install before subitting.'
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
uri = URI.parse(base_uri + '/submissions/create.json')
|
|
434
354
|
https = Net::HTTP.new(uri.hostname, uri.port)
|
|
435
355
|
https.use_ssl = true if base_uri.include? 'https'
|
|
436
356
|
|
|
437
357
|
request = Net::HTTP::Post.new(
|
|
438
358
|
uri,
|
|
439
|
-
initheader = { 'Content-Type' => 'application/json' }
|
|
359
|
+
initheader = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
|
|
440
360
|
)
|
|
441
|
-
request_data, error_cases = revision_submit_params(mesa)
|
|
442
|
-
if request_data[:instances].empty? && mesa.installed?
|
|
443
|
-
shell.say "No completed test data found in #{mesa.mesa_dir}. Aborting.",
|
|
444
|
-
:red
|
|
445
|
-
return false
|
|
446
|
-
end
|
|
447
|
-
request.body = request_data.to_json
|
|
448
361
|
|
|
449
|
-
#
|
|
450
|
-
#
|
|
451
|
-
#
|
|
362
|
+
# create the request body for submission to the submissions API
|
|
363
|
+
#
|
|
364
|
+
# submission is not empty (there is one test case), and it is also not
|
|
365
|
+
# entire (... there is only test case)
|
|
366
|
+
request_data = {submitter: submitter_params,
|
|
367
|
+
commit: commit_params(mesa, empty: false, entire: false),
|
|
368
|
+
instances: single_instance_params(test_case)}
|
|
369
|
+
request.body = request_data.to_json
|
|
452
370
|
|
|
371
|
+
# actually do the submission
|
|
453
372
|
response = https.request request
|
|
454
|
-
|
|
373
|
+
|
|
455
374
|
if !response.is_a? Net::HTTPCreated
|
|
456
|
-
shell.say "\nFailed to submit
|
|
457
|
-
:red
|
|
458
|
-
false
|
|
459
|
-
elsif !error_cases.empty?
|
|
460
|
-
shell.say "\nFailed to gather data for the following cases:", :red
|
|
461
|
-
error_cases.each { |tc| shell.say " #{tc.test_name}", :red }
|
|
375
|
+
shell.say "\nFailed to submit #{test_case.test_name} for commit "\
|
|
376
|
+
"#{mesa.sha}", :red
|
|
462
377
|
false
|
|
463
378
|
else
|
|
464
|
-
shell.say "\nSuccessfully submitted
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
save_computer_data
|
|
468
|
-
true
|
|
379
|
+
shell.say "\nSuccessfully submitted instance of #{test_case.test_name} "\
|
|
380
|
+
"for commit #{mesa.sha}.", :green
|
|
381
|
+
true
|
|
469
382
|
end
|
|
470
383
|
end
|
|
471
384
|
end
|
|
472
385
|
|
|
473
386
|
class Mesa
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
attr_reader :mesa_dir, :test_data, :test_names, :test_cases, :shell,
|
|
477
|
-
:svn_version, :svn_author, :svn_log, :using_sdk
|
|
478
|
-
attr_accessor :update_checksums
|
|
479
|
-
|
|
480
|
-
def self.download(version_number: nil, new_mesa_dir: nil, use_svn: true,
|
|
481
|
-
using_sdk: true)
|
|
482
|
-
new_mesa_dir ||= File.join(ENV['HOME'], 'mesa-test-r' + version_number.to_s)
|
|
483
|
-
svn_command = "svn co -r #{version_number} #{SVN_URI} #{new_mesa_dir}"
|
|
484
|
-
success = bash_execute(svn_command)
|
|
485
|
-
unless success
|
|
486
|
-
raise MesaDirError, 'Encountered a problem in downloading mesa ' \
|
|
487
|
-
"revision #{version_number}. Perhaps svn isn't " \
|
|
488
|
-
'working properly?' + "\n\n"\
|
|
489
|
-
'Tried the following command: ' + svn_command
|
|
490
|
-
|
|
491
|
-
end
|
|
492
|
-
Mesa.new(mesa_dir: new_mesa_dir, use_svn: use_svn, using_sdk: using_sdk)
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
def self.log_since(last_tested = DEFAULT_REVISION)
|
|
496
|
-
# svn commit log back to, but excluding, the last revision tested
|
|
497
|
-
`svn log #{SVN_URI} -r #{last_tested + 1}:HEAD`
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def self.log_lines_since(last_tested = DEFAULT_REVISION)
|
|
501
|
-
log_since(last_tested).split("\n").reject(&:empty?)
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
def self.add_commit(commits, revision, author)
|
|
505
|
-
commits << Commit.new
|
|
506
|
-
commits.last.revision = revision.to_i
|
|
507
|
-
commits.last.author = author
|
|
508
|
-
commits.last.message = []
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def self.process_line(commits, line)
|
|
512
|
-
last = commits.last
|
|
513
|
-
if line =~ /^-+$/
|
|
514
|
-
# dashed lines separate commits
|
|
515
|
-
# Done with last commit (if it exists), so clean up message
|
|
516
|
-
last.message = last.message.join("\n") unless last.nil?
|
|
517
|
-
elsif line =~ /^r(\d+) \| (\w+) \| .* \| \d+ lines?$/
|
|
518
|
-
# first line of a commit, scrape data and make new commit
|
|
519
|
-
add_commit(commits, $1, $2)
|
|
520
|
-
else
|
|
521
|
-
# add lines to the message (will concatenate later to single String)
|
|
522
|
-
last.message << line.strip
|
|
523
|
-
end
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
# all commits since the given version number
|
|
527
|
-
def self.commits_since(last_tested = DEFAULT_REVISION)
|
|
528
|
-
commits = []
|
|
529
|
-
log_lines_since(last_tested).each { |line| process_line(commits, line) }
|
|
530
|
-
commits.sort_by(&:revision).reverse
|
|
531
|
-
end
|
|
387
|
+
attr_reader :mesa_dir, :mirror_dir, :names_to_numbers, :shell,
|
|
388
|
+
:test_case_names, :test_cases, :github_protocol
|
|
532
389
|
|
|
533
|
-
def self.
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
390
|
+
def self.checkout(sha: nil, work_dir: nil, mirror_dir: nil,
|
|
391
|
+
github_protocol: :ssh)
|
|
392
|
+
m = Mesa.new(mesa_dir: work_dir, mirror_dir: mirror_dir,
|
|
393
|
+
github_protocol: github_protocol)
|
|
394
|
+
m.checkout(new_sha: sha)
|
|
395
|
+
m
|
|
539
396
|
end
|
|
540
397
|
|
|
541
|
-
def initialize(mesa_dir: ENV['MESA_DIR'],
|
|
398
|
+
def initialize(mesa_dir: ENV['MESA_DIR'], mirror_dir: nil,
|
|
399
|
+
github_protocol: :ssh)
|
|
542
400
|
# absolute_path ensures that it doesn't matter where commands are executed
|
|
543
401
|
# from
|
|
544
402
|
@mesa_dir = File.absolute_path(mesa_dir)
|
|
545
|
-
@
|
|
546
|
-
|
|
547
|
-
|
|
403
|
+
@mirror_dir = File.absolute_path(mirror_dir)
|
|
404
|
+
|
|
405
|
+
# don't worry about validity of github protocol until it is needed in a
|
|
406
|
+
# checkout. This way you can have garbage in there if you never really need
|
|
407
|
+
# it.
|
|
408
|
+
@github_protocol = if github_protocol.respond_to? :to_sym
|
|
409
|
+
github_protocol.to_sym
|
|
410
|
+
else
|
|
411
|
+
github_protocol
|
|
412
|
+
end
|
|
548
413
|
|
|
549
414
|
# these get populated by calling #load_test_data
|
|
550
|
-
@test_data = {}
|
|
551
|
-
@test_names = {}
|
|
552
415
|
@test_cases = {}
|
|
416
|
+
@test_case_names = {}
|
|
417
|
+
@names_to_numbers = {}
|
|
553
418
|
|
|
554
419
|
# way to output colored text
|
|
555
420
|
@shell = Thor::Shell::Color.new
|
|
556
|
-
|
|
557
|
-
# these can be populated by calling load_svn_data
|
|
558
|
-
@svn_version = nil
|
|
559
|
-
@svn_author = nil
|
|
560
|
-
@svn_log = nil
|
|
561
|
-
load_svn_data if use_svn?
|
|
562
421
|
end
|
|
563
422
|
|
|
564
|
-
def
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
# diffs or true (DO do diffs). Only works if svn data has ALREADY been
|
|
571
|
-
# loaded
|
|
572
|
-
|
|
573
|
-
# don't do anything to @update_checksums if we haven't loaded svn data
|
|
574
|
-
return unless @svn_log
|
|
575
|
-
|
|
576
|
-
# by default, DON'T do diffs
|
|
577
|
-
@update_checksums = true
|
|
578
|
-
|
|
579
|
-
# list of phrases, which, if present in the log entry, will trigger diffs
|
|
580
|
-
[
|
|
581
|
-
/updated? checksums?/i,
|
|
582
|
-
/checksums? updated?/i,
|
|
583
|
-
/ready for diffs?/i,
|
|
584
|
-
].each { |trigger| @update_checksums = false if trigger =~ @svn_log }
|
|
585
|
-
if @update_checksums
|
|
586
|
-
shell.say "\nFrom svn log, didn't decide to tak diffs."
|
|
423
|
+
def checkout(new_sha: 'HEAD')
|
|
424
|
+
# before anything confirm that git-lfs has been installed
|
|
425
|
+
shell.say "\nEnsuring that git-lfs is installed... ", :blue
|
|
426
|
+
command = 'git lfs help >> /dev/null 2>&1'
|
|
427
|
+
if bash_execute(command)
|
|
428
|
+
shell.say "yes", :green
|
|
587
429
|
else
|
|
588
|
-
shell.say "
|
|
430
|
+
shell.say "no", :red
|
|
431
|
+
raise(GitHubError, "The command #{command} returned with an error "\
|
|
432
|
+
'status, indicating that git-lfs is not installed. '\
|
|
433
|
+
'Make sure it is installed and try again.')
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# set up mirror if it doesn't exist
|
|
437
|
+
unless dir_or_symlink_exists?(mirror_dir)
|
|
438
|
+
shell.say "\nCreating initial mirror at #{mirror_dir}. "\
|
|
439
|
+
'This might take awhile...', :blue
|
|
440
|
+
FileUtils.mkdir_p mirror_dir
|
|
441
|
+
case github_protocol
|
|
442
|
+
when :ssh
|
|
443
|
+
command = "git clone --mirror #{GITHUB_SSH} #{mirror_dir}"
|
|
444
|
+
shell.say command
|
|
445
|
+
# fail loudly if this doesn't work
|
|
446
|
+
unless bash_execute(command)
|
|
447
|
+
# nuke the mirror directory since it is probably bogus (make this
|
|
448
|
+
# code fire off the next time checkout is done)
|
|
449
|
+
shell.say "Failed. Removing the [potentially corrupted] mirror.", :red
|
|
450
|
+
command = "rm -rf #{mirror_dir}"
|
|
451
|
+
shell.say command
|
|
452
|
+
bash_execute(command)
|
|
453
|
+
|
|
454
|
+
raise(GitHubError, 'Error while executing the following command:'\
|
|
455
|
+
"#{command}. Perhaps you haven't set up "\
|
|
456
|
+
'ssh keys with your GitHub account?')
|
|
457
|
+
end
|
|
458
|
+
when :https
|
|
459
|
+
command = "git clone --mirror #{GITHUB_HTTPS} #{mirror_dir}"
|
|
460
|
+
shell.say command
|
|
461
|
+
# fail loudly if this doesn't work
|
|
462
|
+
unless bash_execute(command)
|
|
463
|
+
# nuke the mirror directory since it is probably bogus (make this
|
|
464
|
+
# code fire off the next time checkout is done)
|
|
465
|
+
shell.say "Failed. Removing the [potentially corrupted] mirror.", :red
|
|
466
|
+
command = "rm -rf #{mirror_dir}"
|
|
467
|
+
shell.say command
|
|
468
|
+
bash_execute(command)
|
|
469
|
+
|
|
470
|
+
raise(GitHubError, 'Error while executing the following command: '\
|
|
471
|
+
"#{command}. Perhaps you need to configure "\
|
|
472
|
+
'global GitHub account settings for https '\
|
|
473
|
+
'authentication to work properly?')
|
|
474
|
+
end
|
|
475
|
+
else
|
|
476
|
+
raise(GitHubError, "Invalid GitHub protocol: \"#{github_protocol}\"")
|
|
477
|
+
end
|
|
589
478
|
end
|
|
590
|
-
shell.say "log entry: #{@svn_log}"
|
|
591
|
-
end
|
|
592
479
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
#
|
|
596
|
-
|
|
597
|
-
|
|
480
|
+
update_mirror
|
|
481
|
+
|
|
482
|
+
# ensure "work" directory is removed from worktree
|
|
483
|
+
remove
|
|
484
|
+
|
|
485
|
+
# create "work" directory with proper commit
|
|
486
|
+
shell.say "\nSetting up worktree repo...", :blue
|
|
487
|
+
FileUtils.mkdir_p mesa_dir
|
|
488
|
+
command = "git -C #{mirror_dir} worktree add #{mesa_dir} #{new_sha}"
|
|
489
|
+
shell.say command
|
|
490
|
+
return if bash_execute(command)
|
|
491
|
+
|
|
492
|
+
raise(GitHubError, 'Failed while executing the following command: '\
|
|
493
|
+
"\"#{command}\".")
|
|
598
494
|
end
|
|
599
495
|
|
|
600
|
-
def
|
|
601
|
-
|
|
496
|
+
def update_mirror
|
|
497
|
+
shell.say "\nFetching MESA history...", :blue
|
|
498
|
+
command = "git -C #{mirror_dir} fetch origin"
|
|
499
|
+
shell.say command
|
|
500
|
+
# fail loudly
|
|
501
|
+
return if bash_execute(command)
|
|
502
|
+
|
|
503
|
+
raise(GitHubError, 'Failed while executing the following command: '\
|
|
504
|
+
"\"#{command}\".")
|
|
602
505
|
end
|
|
603
506
|
|
|
604
|
-
def
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
507
|
+
def remove
|
|
508
|
+
return unless File.exist? mesa_dir
|
|
509
|
+
shell.say "\nRemoving work directory from worktree (clearing old data)...",
|
|
510
|
+
:blue
|
|
511
|
+
command = "git -C #{mirror_dir} worktree remove --force #{mesa_dir}"
|
|
512
|
+
shell.say command
|
|
513
|
+
return if bash_execute(command)
|
|
514
|
+
|
|
515
|
+
shell.say "Failed. Simply trying to remove the directory.", :red
|
|
516
|
+
command = "rm -rf #{mesa_dir}"
|
|
517
|
+
shell.say command
|
|
518
|
+
# fail loudly (the "true" tells bash_execute to raise an exception if
|
|
519
|
+
# the command fails)
|
|
520
|
+
bash_execute(command, true)
|
|
612
521
|
end
|
|
613
522
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
# match output of svn info to a line with the revision, capturing the
|
|
617
|
-
# number, and defaulting to 0 if none is found.
|
|
618
|
-
matches = /Revision\:\s+(\d+)/.match(`svn info #{mesa_dir}`)
|
|
619
|
-
unless matches.nil?
|
|
620
|
-
return matches[1].to_i
|
|
621
|
-
end
|
|
622
|
-
return 0
|
|
623
|
-
rescue Errno::ENOENT
|
|
624
|
-
return 0
|
|
523
|
+
def git_sha
|
|
524
|
+
bashticks("git -C #{mesa_dir} rev-parse HEAD")
|
|
625
525
|
end
|
|
626
526
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
contents = ''
|
|
630
|
-
File.open(File.join(mesa_dir, 'data', 'version_number'), 'r') do |f|
|
|
631
|
-
contents = f.read
|
|
632
|
-
end
|
|
633
|
-
contents.strip.to_i
|
|
527
|
+
def sha
|
|
528
|
+
git_sha
|
|
634
529
|
end
|
|
635
530
|
|
|
636
531
|
def clean
|
|
637
532
|
with_mesa_dir do
|
|
638
533
|
visit_and_check mesa_dir, MesaDirError, 'E\countered a problem in ' \
|
|
639
534
|
"running `clean` in #{mesa_dir}." do
|
|
640
|
-
|
|
641
|
-
|
|
535
|
+
shell.say('MESA_DIR = ' + ENV['MESA_DIR'])
|
|
536
|
+
shell.say './clean'
|
|
642
537
|
bash_execute('./clean')
|
|
643
538
|
end
|
|
644
539
|
end
|
|
@@ -649,8 +544,8 @@ class Mesa
|
|
|
649
544
|
with_mesa_dir do
|
|
650
545
|
visit_and_check mesa_dir, MesaDirError, 'Encountered a problem in ' \
|
|
651
546
|
"running `install` in #{mesa_dir}." do
|
|
652
|
-
|
|
653
|
-
|
|
547
|
+
shell.say('MESA_DIR = ' + ENV['MESA_DIR'])
|
|
548
|
+
shell.say './install'
|
|
654
549
|
bash_execute('./install')
|
|
655
550
|
end
|
|
656
551
|
end
|
|
@@ -661,20 +556,34 @@ class Mesa
|
|
|
661
556
|
|
|
662
557
|
# throw an error unless it seems like it's properly compiled
|
|
663
558
|
def check_installation
|
|
664
|
-
|
|
665
|
-
raise MesaDirError, 'Installation check failed (no .mod files found ' \
|
|
666
|
-
'in the last compiled module).'
|
|
667
|
-
end
|
|
668
|
-
end
|
|
559
|
+
return if installed?
|
|
669
560
|
|
|
670
|
-
|
|
671
|
-
|
|
561
|
+
raise MesaDirError, 'Installation check failed (build.log doesn\'t '\
|
|
562
|
+
'show a successful installation).'
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# sourced from $MESA_DIR/testhub.yml, which should be created after
|
|
566
|
+
# installation
|
|
567
|
+
def compiler_hash
|
|
568
|
+
data_file = File.join(mesa_dir, 'testhub.yml')
|
|
569
|
+
res = {
|
|
570
|
+
compiler: 'Unknown',
|
|
571
|
+
sdk_version: 'Unknown',
|
|
572
|
+
math_backend: 'Unknown'
|
|
573
|
+
}
|
|
574
|
+
if File.exist? data_file
|
|
575
|
+
res = res.merge(YAML.safe_load(File.read(data_file)) || {})
|
|
576
|
+
# currently version_number is reported, but we don't need that in Git land
|
|
577
|
+
res.delete('version_number') # returns the value, not the updated hash
|
|
578
|
+
res
|
|
579
|
+
end
|
|
672
580
|
end
|
|
673
581
|
|
|
674
582
|
## TEST SUITE METHODS
|
|
675
583
|
|
|
676
584
|
def check_mod(mod)
|
|
677
585
|
return if MesaTestCase.modules.include? mod
|
|
586
|
+
|
|
678
587
|
raise TestCaseDirError, "Invalid module: #{mod}. Must be one of: " +
|
|
679
588
|
MesaTestCase.modules.join(', ')
|
|
680
589
|
end
|
|
@@ -694,53 +603,32 @@ class Mesa
|
|
|
694
603
|
end
|
|
695
604
|
else
|
|
696
605
|
check_mod mod
|
|
697
|
-
# load data from the source file
|
|
698
|
-
source_lines = IO.readlines(
|
|
699
|
-
File.join(test_suite_dir(mod: mod), 'do1_test_source')
|
|
700
|
-
)
|
|
701
606
|
|
|
702
|
-
#
|
|
703
|
-
|
|
704
|
-
@
|
|
607
|
+
# convert output of +list_tests+ to a dictionary that maps
|
|
608
|
+
# names to numbers since +each_test_run+ only knows about numbers
|
|
609
|
+
@names_to_numbers[mod] = {}
|
|
610
|
+
@test_case_names[mod] = []
|
|
705
611
|
@test_cases[mod] = {}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
photo: $4}
|
|
718
|
-
elsif line =~ one_skip
|
|
719
|
-
found_test = true
|
|
720
|
-
@test_data[mod][$1] = { success_string: $2, final_model: $3,
|
|
721
|
-
photo: nil }
|
|
722
|
-
elsif line =~ two_skip
|
|
723
|
-
found_test = true
|
|
724
|
-
@test_data[mod][$1] = { success_string: $2, final_model: nil,
|
|
725
|
-
photo: nil }
|
|
726
|
-
end
|
|
727
|
-
|
|
728
|
-
if found_test
|
|
729
|
-
@test_names[mod] << $1 unless @test_names[mod].include? $1
|
|
612
|
+
visit_dir(test_suite_dir(mod: mod), quiet: true) do
|
|
613
|
+
bashticks('./list_tests').split("\n").each do |line|
|
|
614
|
+
num, tc_name = line.strip.split
|
|
615
|
+
@names_to_numbers[mod][tc_name.strip] = num.to_i
|
|
616
|
+
@test_case_names[mod] << tc_name.strip
|
|
617
|
+
@test_cases[mod][tc_name.strip] = MesaTestCase.new(
|
|
618
|
+
test: tc_name.strip,
|
|
619
|
+
mod: mod,
|
|
620
|
+
position: num.to_i,
|
|
621
|
+
mesa: self
|
|
622
|
+
)
|
|
730
623
|
end
|
|
731
624
|
end
|
|
732
|
-
|
|
733
|
-
# make MesaTestCase objects accessible by name
|
|
734
|
-
@test_names[mod].each do |test_name|
|
|
735
|
-
data = @test_data[mod][test_name]
|
|
736
|
-
@test_cases[mod][test_name] = MesaTestCase.new(
|
|
737
|
-
test: test_name, mesa: self, success_string: data[:success_string],
|
|
738
|
-
mod: mod, final_model: data[:final_model], photo: data[:photo]
|
|
739
|
-
)
|
|
740
|
-
end
|
|
741
625
|
end
|
|
742
626
|
end
|
|
743
627
|
|
|
628
|
+
def test_case_count(mod: :all)
|
|
629
|
+
all_names_ordered(mod: mod).count
|
|
630
|
+
end
|
|
631
|
+
|
|
744
632
|
# can accept a number (in string form) as a name for indexed access
|
|
745
633
|
def find_test_case(test_case_name: nil, mod: :all)
|
|
746
634
|
if /\A[0-9]+\z/ =~ test_case_name
|
|
@@ -750,44 +638,16 @@ class Mesa
|
|
|
750
638
|
end
|
|
751
639
|
end
|
|
752
640
|
|
|
753
|
-
|
|
754
|
-
# revision 10000
|
|
755
|
-
def each_test_clean(mod: :all)
|
|
756
|
-
if mod == :all
|
|
757
|
-
MesaTestCase.modules.each { |this_mod| each_test_clean mod: this_mod }
|
|
758
|
-
else
|
|
759
|
-
check_mod mod
|
|
760
|
-
test_names[mod].each do |test_name|
|
|
761
|
-
test_cases[mod][test_name].clean
|
|
762
|
-
end
|
|
763
|
-
end
|
|
764
|
-
end
|
|
765
|
-
|
|
766
|
-
def each_test_run_and_diff(mod: :all, log_results: false)
|
|
641
|
+
def each_test_run(mod: :all)
|
|
767
642
|
check_installation
|
|
768
|
-
each_test_clean(mod: mod)
|
|
769
643
|
|
|
770
644
|
if mod == :all
|
|
771
645
|
MesaTestCase.modules.each do |this_mod|
|
|
772
|
-
|
|
646
|
+
each_test_run(mod: this_mod)
|
|
773
647
|
end
|
|
774
648
|
else
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
test_cases[mod][test_name].log_results if log_results
|
|
778
|
-
end
|
|
779
|
-
log_summary(mod: mod) if log_results
|
|
780
|
-
end
|
|
781
|
-
end
|
|
782
|
-
|
|
783
|
-
def each_test_load_results(mod: :all)
|
|
784
|
-
if mod == :all
|
|
785
|
-
MesaTestCase.modules.each do |this_mod|
|
|
786
|
-
each_test_load_results(mod: this_mod)
|
|
787
|
-
end
|
|
788
|
-
else
|
|
789
|
-
test_names[mod].each do |test_name|
|
|
790
|
-
test_cases[mod][test_name].load_results
|
|
649
|
+
visit_dir(test_suite_dir(mod: mod)) do
|
|
650
|
+
bash_execute('./each_test_run')
|
|
791
651
|
end
|
|
792
652
|
end
|
|
793
653
|
end
|
|
@@ -797,34 +657,26 @@ class Mesa
|
|
|
797
657
|
end
|
|
798
658
|
|
|
799
659
|
def installed?
|
|
800
|
-
#
|
|
801
|
-
#
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
# that is the last module to be compiled by ./install.
|
|
807
|
-
IO.readlines(install_file).select do |line|
|
|
808
|
-
line =~ /^\s*do_one\w*\s+\w+/
|
|
809
|
-
end.last =~ /^\s*do_one\w*\s+(\w+)/
|
|
810
|
-
# module is "installed" if there is a nonzero number of files in the
|
|
811
|
-
# module's make directory of the form SOMETHING.mod
|
|
812
|
-
!Dir.entries(File.join(mesa_dir, $1, 'make')).select do |file|
|
|
813
|
-
File.extname(file) == '.mod'
|
|
814
|
-
end.empty?
|
|
660
|
+
# assume build log reflects installation status; does not account for
|
|
661
|
+
# mucking with modules after the fact
|
|
662
|
+
build_log = File.join(mesa_dir, 'build.log')
|
|
663
|
+
downloaded? && File.exist?(build_log) && File.read(build_log).include?(
|
|
664
|
+
'MESA installation was successful'
|
|
665
|
+
)
|
|
815
666
|
end
|
|
816
667
|
|
|
668
|
+
def install_attempted?
|
|
669
|
+
File.exist? File.join(mesa_dir, 'testhub.yml')
|
|
670
|
+
end
|
|
817
671
|
|
|
818
672
|
private
|
|
819
673
|
|
|
820
|
-
# verify that mesa_dir is valid by checking for
|
|
821
|
-
# directory
|
|
674
|
+
# verify that mesa_dir is valid by checking for existence of test_suite
|
|
675
|
+
# directory for each module (somewhat arbitrary)
|
|
822
676
|
def check_mesa_dir
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
res &&= dir_or_symlink_exists?(test_suite_dir(mod: mod))
|
|
677
|
+
MesaTestCase.modules.inject(true) do |res, mod|
|
|
678
|
+
res && dir_or_symlink_exists?(test_suite_dir(mod: mod))
|
|
826
679
|
end
|
|
827
|
-
res
|
|
828
680
|
end
|
|
829
681
|
|
|
830
682
|
# change MESA_DIR for the execution of the block and then revert to the
|
|
@@ -845,49 +697,64 @@ class Mesa
|
|
|
845
697
|
end
|
|
846
698
|
end
|
|
847
699
|
|
|
848
|
-
def
|
|
700
|
+
def all_names_ordered(mod: :all)
|
|
701
|
+
load_test_source_data unless @names_to_numbers
|
|
849
702
|
if mod == :all
|
|
850
|
-
|
|
851
|
-
|
|
703
|
+
# build up list by first constructing each modules list and then
|
|
704
|
+
# concatenating them
|
|
705
|
+
MesaTestCase.modules.inject([]) do |res, this_mod|
|
|
706
|
+
res += all_names_ordered(mod: this_mod)
|
|
852
707
|
end
|
|
853
708
|
else
|
|
854
709
|
check_mod mod
|
|
855
|
-
res = []
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
'failure_type' => test_case.failure_type,
|
|
862
|
-
'success_type' => test_case.success_type,
|
|
863
|
-
'runtime_seconds' => test_case.runtime_seconds,
|
|
864
|
-
'omp_num_threads' => test_case.test_omp_num_threads,
|
|
865
|
-
'mesa_version' => test_case.mesa_version
|
|
866
|
-
}
|
|
867
|
-
end
|
|
868
|
-
summary_file = File.join(test_suite_dir(mod: mod), 'test_summary.yml')
|
|
869
|
-
File.open(summary_file, 'w') do |f|
|
|
870
|
-
f.write(YAML.dump(res))
|
|
710
|
+
res = Array.new(@names_to_numbers[mod].length, '')
|
|
711
|
+
|
|
712
|
+
# values of the hash give their order, keys are the names, so
|
|
713
|
+
# we assign keys to positions in the array according to their value
|
|
714
|
+
@names_to_numbers[mod].each_pair do |key, val|
|
|
715
|
+
res[val - 1] = key # +list_tests+ gives 1-indexed positions
|
|
871
716
|
end
|
|
717
|
+
res
|
|
872
718
|
end
|
|
873
719
|
end
|
|
874
720
|
|
|
875
721
|
def find_test_case_by_name(test_case_name: nil, mod: :all)
|
|
722
|
+
load_test_source_data unless @names_to_numbers
|
|
876
723
|
if mod == :all
|
|
877
|
-
# look through all loaded modules for desired test case name,
|
|
878
|
-
#
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
724
|
+
# look through all loaded modules for desired test case name, only
|
|
725
|
+
# return a test case if a single case is found with that name
|
|
726
|
+
case all_names_ordered.count(test_case_name)
|
|
727
|
+
when 1
|
|
728
|
+
# it exists in exactly one module, but we need to find the module
|
|
729
|
+
# and then return the +MesaTestCase+ object
|
|
730
|
+
MesaTestCase.modules.each do |this_mod|
|
|
731
|
+
if @test_case_names[this_mod].include? test_case_name
|
|
732
|
+
# found it, return the appropriate object
|
|
733
|
+
return @test_cases[this_mod][test_case_name]
|
|
734
|
+
end
|
|
882
735
|
end
|
|
736
|
+
raise 'Weird problem: found test case in overall names, but '\
|
|
737
|
+
"not in any particular module. This shouldn't happen."
|
|
738
|
+
when 0
|
|
739
|
+
raise(TestCaseDirError, "Could not find test case #{test_case_name} "\
|
|
740
|
+
'in any module.')
|
|
741
|
+
else
|
|
742
|
+
raise(TestCaseDirError, 'Found multiple test cases named '\
|
|
743
|
+
"#{test_case_name} in multiple modules. Indicate the module you "\
|
|
744
|
+
'want to search.')
|
|
883
745
|
end
|
|
884
|
-
#
|
|
885
|
-
nil
|
|
746
|
+
# append this array to the end of the exisitng one
|
|
886
747
|
else
|
|
887
748
|
# module specified; check it and return the proper test case (may be nil
|
|
888
749
|
# if the test case doesn't exist)
|
|
889
750
|
check_mod mod
|
|
890
|
-
@
|
|
751
|
+
if @test_case_names[mod].include? test_case_name
|
|
752
|
+
# happy path: test case exists in the specified module
|
|
753
|
+
return @test_cases[mod][test_case_name]
|
|
754
|
+
else
|
|
755
|
+
raise TestCaseDirError.new('Could not find test case ' \
|
|
756
|
+
"#{test_case_name} in the #{mod} module.")
|
|
757
|
+
end
|
|
891
758
|
end
|
|
892
759
|
end
|
|
893
760
|
|
|
@@ -895,149 +762,69 @@ class Mesa
|
|
|
895
762
|
# this will be the index in the name array of the proper module of
|
|
896
763
|
# the desired test case
|
|
897
764
|
# input numbers are 1-indexed, but we'll fix that later
|
|
898
|
-
|
|
899
|
-
|
|
765
|
+
if test_number < 1 || test_number > test_case_count(mod: mod)
|
|
766
|
+
raise TestCaseDirError.new('Invalid test case number for searching '\
|
|
767
|
+
"in module #{mod}. Must be between 1 and #{test_case_count(mod: mod)}.")
|
|
768
|
+
end
|
|
900
769
|
|
|
901
770
|
if mod == :all
|
|
902
|
-
#
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
test_case_name: @test_names[this_mod][i - 1],
|
|
913
|
-
mod: this_mod
|
|
771
|
+
# can get the name easily, now need to find the module
|
|
772
|
+
test_case_name = all_names_ordered[test_number - 1]
|
|
773
|
+
MesaTestCase.modules.each do |mod|
|
|
774
|
+
if test_number <= test_case_count(mod: mod)
|
|
775
|
+
# test must live in this module; we have everything
|
|
776
|
+
return MesaTestCase.new(
|
|
777
|
+
test: test_case_name,
|
|
778
|
+
mod: mod,
|
|
779
|
+
mesa: self,
|
|
780
|
+
position: @names_to_numbers[mod][test_case_name]
|
|
914
781
|
)
|
|
782
|
+
else
|
|
783
|
+
# number was too big, so decrement by this modules case count
|
|
784
|
+
# and move on to next one
|
|
785
|
+
test_number -= test_case_count(mod: mod)
|
|
915
786
|
end
|
|
916
|
-
# index lies outside possible range for this module, move on to
|
|
917
|
-
# next module and decrement index by the number of test cases in this
|
|
918
|
-
# module
|
|
919
|
-
i -= @test_names[this_mod].length
|
|
920
787
|
end
|
|
921
|
-
# return
|
|
922
|
-
|
|
788
|
+
# should return before we get here, but fail hard if we do
|
|
789
|
+
raise TestCaseDirError.new('Unknown problem in loading test case #' +
|
|
790
|
+
test_number + '.')
|
|
923
791
|
else
|
|
924
|
-
# module was specified, so
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
792
|
+
# module was specified, so we can get at everything right away
|
|
793
|
+
check_mod mod
|
|
794
|
+
return MesaTestCase.new(
|
|
795
|
+
test: all_names_ordered(mod: mod)[test_number - 1],
|
|
796
|
+
mod: mod,
|
|
797
|
+
mesa: self,
|
|
798
|
+
position: test_number
|
|
931
799
|
)
|
|
932
800
|
end
|
|
933
801
|
end
|
|
934
802
|
end
|
|
935
803
|
|
|
936
804
|
class MesaTestCase
|
|
937
|
-
attr_reader :test_name, :
|
|
938
|
-
:failure_msg, :success_msg, :photo, :runtime_seconds,
|
|
939
|
-
:test_omp_num_threads, :mesa_version, :shell, :mod, :retries,
|
|
940
|
-
:backups, :steps, :runtime_minutes, :summary_text, :compiler,
|
|
941
|
-
:compiler_version, :diff, :checksum, :rn_mem, :re_mem,
|
|
942
|
-
:re_time, :total_runtime_seconds
|
|
943
|
-
attr_accessor :data_names, :data_types, :failure_type, :success_type,
|
|
944
|
-
:outcome
|
|
805
|
+
attr_reader :test_name, :mesa, :mod, :position, :shell
|
|
945
806
|
|
|
946
807
|
def self.modules
|
|
947
808
|
%i[star binary astero]
|
|
948
809
|
end
|
|
949
810
|
|
|
950
|
-
def initialize(test: nil, mesa: nil,
|
|
951
|
-
final_model: 'final.mod', photo: nil, mod: nil)
|
|
811
|
+
def initialize(test: nil, mesa: nil, mod: nil, position: nil)
|
|
952
812
|
@test_name = test
|
|
953
|
-
@mesa_dir = mesa.mesa_dir
|
|
954
813
|
@mesa = mesa
|
|
955
|
-
@mesa_version = mesa.version_number
|
|
956
|
-
@success_string = success_string
|
|
957
|
-
@final_model = final_model
|
|
958
|
-
@photo = photo
|
|
959
|
-
@failure_type = nil
|
|
960
|
-
@success_type = nil
|
|
961
|
-
@outcome = :not_tested
|
|
962
|
-
@runtime_seconds = 0
|
|
963
|
-
@test_omp_num_threads = 1
|
|
964
|
-
@runtime_minutes = 0
|
|
965
|
-
@total_runtime_seconds = 0
|
|
966
|
-
@retries = 0
|
|
967
|
-
@backups = 0
|
|
968
|
-
@steps = 0
|
|
969
|
-
# 2 (default) means uknown. Updated by running or loading data.
|
|
970
|
-
# 1 means did diffs (not update_checksums; like each_test_run_and_diff)
|
|
971
|
-
# 0 means no diffs (update_checksums; like each_test_run)
|
|
972
|
-
@diff = 2
|
|
973
|
-
# start with nil. Should only be updated to a non-nil value if test is
|
|
974
|
-
# completely successful
|
|
975
|
-
@checksum = nil
|
|
976
|
-
@re_time = nil # rn_time is in the form of @runtime_seconds
|
|
977
|
-
|
|
978
|
-
# these only get used with modern versions of both the sdk and the test
|
|
979
|
-
# suite
|
|
980
|
-
@rn_mem = nil
|
|
981
|
-
@re_mem = nil
|
|
982
|
-
|
|
983
|
-
# note: this gets overridden for new runs, so this is probably irrelevant
|
|
984
|
-
@summary_text = nil
|
|
985
|
-
|
|
986
|
-
# this overrides the submitters choice if it is non-nil
|
|
987
|
-
@compiler = mesa.using_sdk ? 'SDK' : nil
|
|
988
|
-
# only relevant if @compiler is SDK. Gets set during do_one
|
|
989
|
-
@compiler_version = nil
|
|
990
|
-
|
|
991
814
|
unless MesaTestCase.modules.include? mod
|
|
992
815
|
raise TestCaseDirError, "Invalid module: #{mod}. Must be one of: " +
|
|
993
816
|
MesaTestCase.modules.join(', ')
|
|
994
817
|
end
|
|
995
818
|
@mod = mod
|
|
996
|
-
@
|
|
997
|
-
run_test_string: "#{test_name} run failed: does not match test string",
|
|
998
|
-
final_model: "#{test_name} run failed: final model #{final_model} not " \
|
|
999
|
-
'made.',
|
|
1000
|
-
run_checksum: "#{test_name} run failed: checksum for #{final_model} " \
|
|
1001
|
-
'does not match after ./rn',
|
|
1002
|
-
run_diff: "#{test_name} run failed: diff #{final_model} " \
|
|
1003
|
-
'final_check.mod after ./rn',
|
|
1004
|
-
photo_file: "#{test_name} restart failed: #{photo} does not exist",
|
|
1005
|
-
photo_checksum: "#{test_name} restart failed: checksum for " \
|
|
1006
|
-
"#{final_model} does not match after ./re",
|
|
1007
|
-
photo_diff: "#{test_name} restart failed: diff #{final_model} " \
|
|
1008
|
-
'final_check.mod after ./re',
|
|
1009
|
-
compilation: "#{test_name} compilation failed"
|
|
819
|
+
@position = position
|
|
1010
820
|
|
|
1011
|
-
|
|
1012
|
-
@
|
|
1013
|
-
run_test_string: "#{test_name} run: found test string: " \
|
|
1014
|
-
"'#{success_string}'",
|
|
1015
|
-
run_checksum: "#{test_name} run: checksum for #{final_model} matches " \
|
|
1016
|
-
'after ./rn',
|
|
1017
|
-
photo_checksum: "#{test_name} restart: checksum for #{final_model} " \
|
|
1018
|
-
"matches after ./re #{photo}"
|
|
1019
|
-
}
|
|
821
|
+
# way to output colored text to shell
|
|
822
|
+
@shell = Thor::Shell::Color.new
|
|
1020
823
|
|
|
1021
824
|
# validate stuff
|
|
1022
825
|
check_mesa_dir
|
|
1023
826
|
check_test_case
|
|
1024
827
|
|
|
1025
|
-
@data = {}
|
|
1026
|
-
@data_names = []
|
|
1027
|
-
|
|
1028
|
-
# way to output colored text to shell
|
|
1029
|
-
@shell = Thor::Shell::Color.new
|
|
1030
|
-
end
|
|
1031
|
-
|
|
1032
|
-
def passed?
|
|
1033
|
-
if @outcome == :pass
|
|
1034
|
-
true
|
|
1035
|
-
elsif @outcome == :fail
|
|
1036
|
-
false
|
|
1037
|
-
else
|
|
1038
|
-
raise TestCaseDirError, 'Cannot determine pass/fail status of ' \
|
|
1039
|
-
"#{test_name} yet."
|
|
1040
|
-
end
|
|
1041
828
|
end
|
|
1042
829
|
|
|
1043
830
|
def test_suite_dir
|
|
@@ -1048,214 +835,26 @@ class MesaTestCase
|
|
|
1048
835
|
File.join(test_suite_dir, test_name)
|
|
1049
836
|
end
|
|
1050
837
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
raise InvalidDataType, "Invalid data type: #{datum_type}. Must be one "\
|
|
1054
|
-
'of ' + data_types.join(', ') + '.'
|
|
1055
|
-
end
|
|
1056
|
-
@data[datum_name] = datum_type
|
|
1057
|
-
@data_names << datum_name
|
|
1058
|
-
end
|
|
1059
|
-
|
|
1060
|
-
def omp_num_threads
|
|
1061
|
-
ENV['OMP_NUM_THREADS'].to_i || 1
|
|
1062
|
-
end
|
|
1063
|
-
|
|
1064
|
-
# based on $MESA_DIR/star/test_suite/each_test_clean, revision 10000
|
|
1065
|
-
def clean
|
|
1066
|
-
shell.say("Cleaning #{test_name}", color = :yellow)
|
|
1067
|
-
# puts ''
|
|
1068
|
-
check_mesa_dir
|
|
1069
|
-
check_test_case
|
|
1070
|
-
in_dir do
|
|
1071
|
-
puts './clean'
|
|
1072
|
-
unless bash_execute('./clean')
|
|
1073
|
-
raise TestCaseDirError, 'Encountered an error while running ./clean ' \
|
|
1074
|
-
"in #{Dir.getwd}."
|
|
1075
|
-
end
|
|
1076
|
-
shell.say 'Removing all files from LOGS, LOGS1, LOGS2, photos, ' \
|
|
1077
|
-
'photos1, and photos2 as well as old test results', color = :blue
|
|
1078
|
-
FileUtils.rm_f Dir.glob('LOGS/*')
|
|
1079
|
-
FileUtils.rm_f Dir.glob('LOGS1/*')
|
|
1080
|
-
FileUtils.rm_f Dir.glob('LOGS2/*')
|
|
1081
|
-
FileUtils.rm_f Dir.glob('photos/*')
|
|
1082
|
-
FileUtils.rm_f Dir.glob('photos1/*')
|
|
1083
|
-
FileUtils.rm_f Dir.glob('photos2/*')
|
|
1084
|
-
|
|
1085
|
-
shell.say 'Removing files binary_history.data, out.txt, ' \
|
|
1086
|
-
'test_results.yml, and memory usage files', color = :blue
|
|
1087
|
-
FileUtils.rm_f 'binary_history.data'
|
|
1088
|
-
FileUtils.rm_f 'out.txt'
|
|
1089
|
-
FileUtils.rm_f 'test_results.yml'
|
|
1090
|
-
FileUtils.rm_f Dir.glob('mem-*.txt')
|
|
1091
|
-
if File.directory? File.join('star_history', 'history_out')
|
|
1092
|
-
shell.say 'Removing all files of the form history_out* from ' \
|
|
1093
|
-
'star_history', :blue
|
|
1094
|
-
FileUtils.rm_f Dir.glob(File.join('star_history', 'history_out', '*'))
|
|
1095
|
-
end
|
|
1096
|
-
if File.directory? File.join('star_profile', 'profiles_out')
|
|
1097
|
-
shell.say 'Removing all files of the form profiles_out* from ' \
|
|
1098
|
-
'star_profile', color = :blue
|
|
1099
|
-
FileUtils.rm_f Dir.glob(File.join('star_profile', 'profiles_out', '*'))
|
|
1100
|
-
end
|
|
1101
|
-
shell.say 'Removing .running', color = :blue
|
|
1102
|
-
FileUtils.rm_f '.running'
|
|
1103
|
-
end
|
|
1104
|
-
end
|
|
1105
|
-
|
|
1106
|
-
# based on $MESA_DIR/star/test_suite/each_test_run_and_diff, revision 10000
|
|
838
|
+
# just punt to +each_test_run+ in the test_suite directory. It's your problem
|
|
839
|
+
# now, sucker!
|
|
1107
840
|
def do_one
|
|
1108
841
|
shell.say("Testing #{test_name}", :yellow)
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
@test_omp_num_threads = omp_num_threads
|
|
1112
|
-
if mesa.using_sdk
|
|
1113
|
-
version_bin = File.join(ENV['MESASDK_ROOT'], 'bin', 'mesasdk_version')
|
|
1114
|
-
# can't use bash_execute because the return value of bash_execute is the
|
|
1115
|
-
# exit status of the commmand (true or false), whereas backticks give the
|
|
1116
|
-
# output (the version string) as the output
|
|
1117
|
-
if File.exist? version_bin
|
|
1118
|
-
# newer SDKs have a simple executable
|
|
1119
|
-
@compiler_version = `#{version_bin}`.strip
|
|
1120
|
-
else
|
|
1121
|
-
# older way, call bash on it (file is mesasdk_version.sh)
|
|
1122
|
-
@compiler_version = `bash -c #{version_bin + '.sh'}`.strip
|
|
1123
|
-
end
|
|
1124
|
-
|
|
1125
|
-
shell.say("Using version #{@compiler_version} of the SDK.", :blue)
|
|
1126
|
-
end
|
|
1127
|
-
in_dir do
|
|
1128
|
-
FileUtils.touch '.running'
|
|
1129
|
-
build_and_run
|
|
1130
|
-
# report memory usage if it is available
|
|
1131
|
-
if File.exist?('mem-rn.txt')
|
|
1132
|
-
@rn_mem = File.read('mem-rn.txt').strip.to_i
|
|
1133
|
-
end
|
|
1134
|
-
if File.exist?('mem-re.txt')
|
|
1135
|
-
@re_mem = File.read('mem-re.txt').strip.to_i
|
|
1136
|
-
end
|
|
1137
|
-
FileUtils.rm '.running'
|
|
1138
|
-
puts ''
|
|
1139
|
-
end
|
|
1140
|
-
test_finish = Time.now
|
|
1141
|
-
@total_runtime_seconds = (test_finish - test_start).to_i
|
|
1142
|
-
end
|
|
1143
|
-
|
|
1144
|
-
def log_results
|
|
1145
|
-
# gets all parameters that would be submitted as well as computer
|
|
1146
|
-
# information and dumps to a yml file in the test case directory
|
|
1147
|
-
save_file = File.join(test_case_dir, 'test_results.yml')
|
|
1148
|
-
shell.say "Logging test results to #{save_file}...", :blue
|
|
1149
|
-
res = {
|
|
1150
|
-
'test_case' => test_name,
|
|
1151
|
-
'module' => mod,
|
|
1152
|
-
'runtime_seconds' => runtime_seconds,
|
|
1153
|
-
're_time' => re_time,
|
|
1154
|
-
'total_runtime_seconds' => total_runtime_seconds,
|
|
1155
|
-
'mesa_version' => mesa_version,
|
|
1156
|
-
'outcome' => outcome,
|
|
1157
|
-
'omp_num_threads' => test_omp_num_threads,
|
|
1158
|
-
'success_type' => success_type,
|
|
1159
|
-
'failure_type' => failure_type,
|
|
1160
|
-
'runtime_minutes' => runtime_minutes,
|
|
1161
|
-
'retries' => retries,
|
|
1162
|
-
'backups' => backups,
|
|
1163
|
-
'steps' => steps,
|
|
1164
|
-
'diff' => diff,
|
|
1165
|
-
'checksum' => checksum,
|
|
1166
|
-
'rn_mem' => rn_mem,
|
|
1167
|
-
're_mem' => re_mem,
|
|
1168
|
-
'summary_text' => summary_text
|
|
1169
|
-
}
|
|
1170
|
-
if compiler == 'SDK'
|
|
1171
|
-
res['compiler'] = 'SDK'
|
|
1172
|
-
res['compiler_version'] = compiler_version
|
|
842
|
+
visit_dir(test_suite_dir) do
|
|
843
|
+
bash_execute("./each_test_run #{position}")
|
|
1173
844
|
end
|
|
1174
|
-
File.open(save_file, 'w') { |f| f.write(YAML.dump(res)) }
|
|
1175
|
-
shell.say "Successfully saved results to file #{save_file}.\n", :green
|
|
1176
845
|
end
|
|
1177
846
|
|
|
1178
|
-
def
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
unless File.exist? load_file
|
|
1184
|
-
shell.say "No such file: #{load_file}. No data loaded.", :red
|
|
1185
|
-
return
|
|
847
|
+
def results_hash
|
|
848
|
+
testhub_file = File.join(test_case_dir, 'testhub.yml')
|
|
849
|
+
unless File.exist?(testhub_file)
|
|
850
|
+
raise TestCaseDirError.new('No results found for test case '\
|
|
851
|
+
"#{test_name}.")
|
|
1186
852
|
end
|
|
1187
|
-
|
|
1188
|
-
@runtime_seconds = data['runtime_seconds'] || @runtime_seconds
|
|
1189
|
-
@re_time = data['re_time'] || @re_time
|
|
1190
|
-
@total_runtime_seconds = data['total_runtime_seconds'] || @total_runtime_seconds
|
|
1191
|
-
@mod = data['module'] || @mod
|
|
1192
|
-
@mesa_version = data['mesa_version'] || @mesa_version
|
|
1193
|
-
@outcome = data['outcome'] || @outcome
|
|
1194
|
-
@test_omp_num_threads = data['omp_num_threads'] || @test_omp_num_threads
|
|
1195
|
-
@success_type = data['success_type'] || @success_type
|
|
1196
|
-
@failure_type = data['failure_type'] || @failure_type
|
|
1197
|
-
@runtime_minutes = data['runtime_minutes'] || @runtime_minutes
|
|
1198
|
-
@retries = data['retries'] || @retries
|
|
1199
|
-
@backups = data['backups'] || @backups
|
|
1200
|
-
@steps = data['steps'] || @steps
|
|
1201
|
-
@diff = data['diff'] || @diff
|
|
1202
|
-
@checksum = data['checksum'] || @checksum
|
|
1203
|
-
@rn_mem = data['rn_mem'] || @rn_mem
|
|
1204
|
-
@re_mem = data['re_mem'] || @re_mem
|
|
1205
|
-
@summary_text = data['summary_text'] || @summary_text
|
|
1206
|
-
@compiler = data['compiler'] || @compiler
|
|
1207
|
-
|
|
1208
|
-
@compiler_version = data['compiler_version'] || @compiler_version
|
|
1209
|
-
|
|
1210
|
-
# convert select data to symbols since that is how they are used
|
|
1211
|
-
@outcome = @outcome.to_sym if @outcome
|
|
1212
|
-
@success_type = @success_type.to_sym if @success_type
|
|
1213
|
-
@failure_type = @failure_type.to_sym if @failure_type
|
|
1214
|
-
|
|
1215
|
-
shell.say "Done loading data from #{load_file}.\n", :green
|
|
1216
|
-
end
|
|
1217
|
-
|
|
1218
|
-
def load_summary_data
|
|
1219
|
-
begin
|
|
1220
|
-
out_data = parse_out
|
|
1221
|
-
@summary_text = get_summary_text
|
|
1222
|
-
rescue Errno::ENOENT
|
|
1223
|
-
shell.say "\nError loading data from #{out_file}. No summary data "\
|
|
1224
|
-
'loaded. Proceeding anyway.', :red
|
|
1225
|
-
else
|
|
1226
|
-
@runtime_minutes = out_data[:runtime_minutes]
|
|
1227
|
-
@retries = out_data[:retries]
|
|
1228
|
-
@backups = out_data[:backups]
|
|
1229
|
-
@steps = out_data[:steps]
|
|
1230
|
-
end
|
|
1231
|
-
end
|
|
1232
|
-
|
|
1233
|
-
def parse_out
|
|
1234
|
-
runtime_minutes = 0
|
|
1235
|
-
retries = 0
|
|
1236
|
-
backups = 0
|
|
1237
|
-
steps = 0
|
|
1238
|
-
run_summaries.each do |summary|
|
|
1239
|
-
summary =~ /^\s*runtime\s*\(minutes\),\s+retries,\s+backups,\ssteps\s+(\d+\.?\d*)\s+(\d+)\s+(\d+)\s+(\d+)\s*$/
|
|
1240
|
-
runtime_minutes += $1.to_f
|
|
1241
|
-
retries += $2.to_i
|
|
1242
|
-
backups += $3.to_i
|
|
1243
|
-
steps += $4.to_i
|
|
1244
|
-
end
|
|
1245
|
-
{runtime_minutes: runtime_minutes, retries: retries, backups: backups,
|
|
1246
|
-
steps: steps}
|
|
853
|
+
YAML.safe_load(File.read(testhub_file), [Symbol])
|
|
1247
854
|
end
|
|
1248
855
|
|
|
1249
856
|
private
|
|
1250
857
|
|
|
1251
|
-
def data_types
|
|
1252
|
-
%i[float integer string boolean]
|
|
1253
|
-
end
|
|
1254
|
-
|
|
1255
|
-
def rn_time
|
|
1256
|
-
runtime_seconds
|
|
1257
|
-
end
|
|
1258
|
-
|
|
1259
858
|
# cd into the test case directory, do something in a block, then cd back
|
|
1260
859
|
# to original directory
|
|
1261
860
|
def in_dir(&block)
|
|
@@ -1269,328 +868,10 @@ class MesaTestCase
|
|
|
1269
868
|
raise TestCaseDirError, "No such test case: #{test_case_dir}."
|
|
1270
869
|
end
|
|
1271
870
|
|
|
1272
|
-
# verify that mesa_dir is valid by checking for
|
|
1273
|
-
# directory
|
|
871
|
+
# "verify" that mesa_dir is valid by checking for test_suite directory
|
|
1274
872
|
def check_mesa_dir
|
|
1275
|
-
is_valid =
|
|
1276
|
-
|
|
1277
|
-
raise MesaDirError, "Invalid MESA dir: #{mesa_dir}" unless is_valid
|
|
1278
|
-
end
|
|
1279
|
-
|
|
1280
|
-
# append message to log file
|
|
1281
|
-
def log_message(msg, color = nil, log_file = 'out.txt')
|
|
1282
|
-
if color.nil?
|
|
1283
|
-
shell.say(msg)
|
|
1284
|
-
else
|
|
1285
|
-
shell.say(msg, color)
|
|
1286
|
-
end
|
|
1287
|
-
File.open(log_file, 'a') { |f| f.puts(msg) }
|
|
1288
|
-
end
|
|
1289
|
-
|
|
1290
|
-
# write failure message to log file
|
|
1291
|
-
def write_failure_message
|
|
1292
|
-
msg = "******************** #{failure_msg[@failure_type]} " \
|
|
1293
|
-
'********************'
|
|
1294
|
-
log_message(msg, :red)
|
|
1295
|
-
end
|
|
1296
|
-
|
|
1297
|
-
# write success message to log file
|
|
1298
|
-
def write_success_msg(success_type)
|
|
1299
|
-
msg = 'PASS ' + success_msg[success_type]
|
|
1300
|
-
log_message(msg, :green)
|
|
1301
|
-
end
|
|
1302
|
-
|
|
1303
|
-
# used as return value for run or photo test. Logs failure to text file, and
|
|
1304
|
-
# sets internal status to failing
|
|
1305
|
-
def fail_test(failure_type)
|
|
1306
|
-
@failure_type = failure_type
|
|
1307
|
-
@outcome = :fail
|
|
1308
|
-
write_failure_message
|
|
1309
|
-
false
|
|
1310
|
-
end
|
|
1311
|
-
|
|
1312
|
-
# used as return value for run or photo test. Logs data to text file, and
|
|
1313
|
-
# sets internal status to passing
|
|
1314
|
-
def succeed(success_type)
|
|
1315
|
-
@success_type = success_type
|
|
1316
|
-
@outcome = :pass
|
|
1317
|
-
# this should ONLY be read after we are certain we've passed AND that we
|
|
1318
|
-
# even have a newly-made checksum
|
|
1319
|
-
if File.exist?('checks.md5') && @mesa.update_checksums
|
|
1320
|
-
# sometimes we want to ignore the final checksum value
|
|
1321
|
-
# we still want to check that restarts on individual machines are bit-for-bit
|
|
1322
|
-
# but we don't require bit-for-bit globally, so we report a bogus checksum
|
|
1323
|
-
if File.exist?('.ignore_checksum') then
|
|
1324
|
-
@checksum ='00000000000000000000000000000000'
|
|
1325
|
-
else
|
|
1326
|
-
@checksum = File.read('checks.md5').split.first
|
|
1327
|
-
end
|
|
1328
|
-
end
|
|
1329
|
-
write_success_msg(success_type)
|
|
1330
|
-
true
|
|
1331
|
-
end
|
|
1332
|
-
|
|
1333
|
-
def check_run
|
|
1334
|
-
# assumes we are in the directory already, called from something else
|
|
1335
|
-
run_start = Time.now
|
|
1336
|
-
|
|
1337
|
-
# do the run
|
|
1338
|
-
rn_command = if ENV['MESASDK_ROOT'] && File.exist?(File.join(ENV['MESASDK_ROOT'], 'bin', 'time'))
|
|
1339
|
-
%q(command time -f '%M' -o mem-rn.txt ./rn > out.txt 2> ) +
|
|
1340
|
-
'err.txt'
|
|
1341
|
-
else
|
|
1342
|
-
'./rn >> out.txt 2> err.txt'
|
|
1343
|
-
end
|
|
1344
|
-
puts rn_command
|
|
1345
|
-
bash_execute(rn_command)
|
|
1346
|
-
|
|
1347
|
-
# report runtime and clean up
|
|
1348
|
-
run_finish = Time.now
|
|
1349
|
-
@runtime_seconds = (run_finish - run_start).to_i
|
|
1350
|
-
@rn_time = (run_finish - run_start).to_i
|
|
1351
|
-
shell.say("Finished with ./rn; runtime = #{@runtime_seconds} seconds.",
|
|
1352
|
-
:blue)
|
|
1353
|
-
append_and_rm_err
|
|
1354
|
-
|
|
1355
|
-
# look for success text
|
|
1356
|
-
success = true
|
|
1357
|
-
File.open('out.txt', 'r') do |f|
|
|
1358
|
-
success = !f.read.downcase.scan(success_string.downcase).empty?
|
|
1359
|
-
end
|
|
1360
|
-
# bail if there was no test string found
|
|
1361
|
-
return fail_test(:run_test_string) unless success
|
|
1362
|
-
|
|
1363
|
-
# no final model to check, and we already found the test string, so pass
|
|
1364
|
-
return succeed(:run_test_string) unless final_model
|
|
1365
|
-
|
|
1366
|
-
# display runtime message
|
|
1367
|
-
puts IO.readlines('out.txt').select { |line| line.scan(/runtime/i) }[-1]
|
|
1368
|
-
|
|
1369
|
-
# there's supposed to be a final model; check that it exists first
|
|
1370
|
-
return fail_test(:final_model) unless File.exist?(final_model)
|
|
1371
|
-
|
|
1372
|
-
# update checksums
|
|
1373
|
-
#
|
|
1374
|
-
# if this is true, behave like each_test_run. update the checksum
|
|
1375
|
-
# after rn and then check it matches after re
|
|
1376
|
-
#
|
|
1377
|
-
# if this is false, behave like each_test_run_and_diff. assume
|
|
1378
|
-
# the checksum is up-to-date and check it matches after rn and re.
|
|
1379
|
-
if @mesa.update_checksums
|
|
1380
|
-
@diff = 0 # this means no diffs run
|
|
1381
|
-
puts "md5sum \"#{final_model}\" > checks.md5"
|
|
1382
|
-
bash_execute("md5sum \"#{final_model}\" > checks.md5")
|
|
1383
|
-
FileUtils.cp final_model, 'final_check.mod'
|
|
1384
|
-
|
|
1385
|
-
# if there's no photo, we won't check the checksum, so we've succeeded
|
|
1386
|
-
return succeed(:run_test_string) unless photo
|
|
1387
|
-
# if there is a photo, we'll have to wait and see
|
|
1388
|
-
return true
|
|
1389
|
-
end
|
|
1390
|
-
|
|
1391
|
-
# check that final model matches
|
|
1392
|
-
@diff = 1 # this means diffs were run
|
|
1393
|
-
puts './ck >& final_check_diff.txt'
|
|
1394
|
-
return fail_test(:run_checksum) unless
|
|
1395
|
-
bash_execute('./ck >& final_check_diff.txt')
|
|
1396
|
-
return fail_test(:run_diff) if File.exist?('final_check_diff.txt') &&
|
|
1397
|
-
!File.read('final_check_diff.txt').empty?
|
|
1398
|
-
return succeed(:run_checksum) if File.exist? final_model
|
|
1399
|
-
end
|
|
1400
|
-
|
|
1401
|
-
# prepare for and do restart, check results, and return pass/fail status
|
|
1402
|
-
def check_restart
|
|
1403
|
-
# abort if there is not photo specified
|
|
1404
|
-
return unless photo
|
|
1405
|
-
|
|
1406
|
-
# get antepenultimate photo
|
|
1407
|
-
if photo == "auto" then
|
|
1408
|
-
# get all photos [single-star (x100) or binary (b_x100); exclude binary stars (1_x100, 2_x100)]
|
|
1409
|
-
photo_files = Dir["photos/*"].select{|p| p =~ /^photos\/(b_)?x?\d+$/}
|
|
1410
|
-
# sort by filesystem modification time
|
|
1411
|
-
sorted_photo_files = photo_files.sort_by { |file_name| File.stat(file_name).mtime }
|
|
1412
|
-
# if you can, pull out 3rd most recent one; otherwise take the oldest
|
|
1413
|
-
if sorted_photo_files.length >= 3 then
|
|
1414
|
-
re_photo = File.basename(sorted_photo_files[-3])
|
|
1415
|
-
else
|
|
1416
|
-
re_photo = File.basename(sorted_photo_files[0])
|
|
1417
|
-
end
|
|
1418
|
-
# if binary, trim off prefix
|
|
1419
|
-
re_photo = re_photo.sub("b_", "")
|
|
1420
|
-
else
|
|
1421
|
-
re_photo = photo
|
|
1422
|
-
end
|
|
1423
|
-
|
|
1424
|
-
# check that photo file actually exists
|
|
1425
|
-
unless File.exist?(File.join('photos', re_photo)) ||
|
|
1426
|
-
File.exist?(File.join('photos1', re_photo)) ||
|
|
1427
|
-
File.exist?(File.join('photos', "b_#{re_photo}"))
|
|
1428
|
-
return fail_test(:photo_file)
|
|
1429
|
-
end
|
|
1430
|
-
|
|
1431
|
-
# remove final model since it will be remade by restart
|
|
1432
|
-
FileUtils.rm_f final_model
|
|
1433
|
-
|
|
1434
|
-
# do restart and consolidate output. Command depends on if we have access
|
|
1435
|
-
# to SDK version of gnu time.
|
|
1436
|
-
re_command = if ENV['MESASDK_ROOT'] && File.exist?(File.join(ENV['MESASDK_ROOT'], 'bin', 'time'))
|
|
1437
|
-
%q(command time -f '%M' -o mem-re.txt ./re ) + "#{re_photo}" \
|
|
1438
|
-
' >> out.txt 2> err.txt'
|
|
1439
|
-
else
|
|
1440
|
-
"./re #{re_photo} >> out.txt 2> err.txt"
|
|
1441
|
-
end
|
|
1442
|
-
|
|
1443
|
-
puts re_command
|
|
1444
|
-
# puts "./re #{re_photo} >> out.txt 2> err.txt"
|
|
1445
|
-
re_start = Time.now
|
|
1446
|
-
# bash_execute("./re #{re_photo} >> out.txt 2> err.txt")
|
|
1447
|
-
# bash_execute(%Q{command time -f '%M' -o mem-re.txt ./re #{re_photo} >> out.txt 2> err.txt})
|
|
1448
|
-
bash_execute(re_command)
|
|
1449
|
-
re_finish = Time.now
|
|
1450
|
-
@re_time = (re_finish - re_start).to_i
|
|
1451
|
-
append_and_rm_err
|
|
1452
|
-
|
|
1453
|
-
# check that final model matches
|
|
1454
|
-
puts './ck >& final_check_diff.txt'
|
|
1455
|
-
return fail_test(:photo_checksum) unless
|
|
1456
|
-
bash_execute('./ck >& final_check_diff.txt')
|
|
1457
|
-
return fail_test(:photo_diff) if
|
|
1458
|
-
File.exist?('final_check_diff.txt') &&
|
|
1459
|
-
!File.read('final_check_diff.txt').empty?
|
|
1460
|
-
succeed(:photo_checksum)
|
|
1461
|
-
end
|
|
1462
|
-
|
|
1463
|
-
def build_and_run
|
|
1464
|
-
# assumes we are in the test case directory. Should only be called
|
|
1465
|
-
# in the context of an `in_dir` block.
|
|
1466
|
-
|
|
1467
|
-
# first clean and make... Should be compatible with any shell since
|
|
1468
|
-
# redirection is always wrapped in 'bash -c "{STUFF}"'
|
|
1469
|
-
simple_clean
|
|
1470
|
-
begin
|
|
1471
|
-
mk
|
|
1472
|
-
rescue TestCaseDirError
|
|
1473
|
-
return fail_test(:compilation)
|
|
1474
|
-
end
|
|
1475
|
-
|
|
1476
|
-
# remove old final model if it exists
|
|
1477
|
-
remove_final_model
|
|
1478
|
-
|
|
1479
|
-
# only check restart/photo if we get through run successfully
|
|
1480
|
-
check_restart if check_run
|
|
1481
|
-
|
|
1482
|
-
# get reported runtime, retries, backups, and steps
|
|
1483
|
-
load_summary_data if File.exist?(out_file)
|
|
1484
|
-
end
|
|
1485
|
-
|
|
1486
|
-
# append contents of err.txt to end of out.txt, then delete err.txt
|
|
1487
|
-
def append_and_rm_err(outfile = 'out.txt', errfile = 'err.txt')
|
|
1488
|
-
err_contents = File.read(errfile)
|
|
1489
|
-
display_errors(err_contents)
|
|
1490
|
-
log_errors(err_contents, outfile)
|
|
1491
|
-
FileUtils.rm errfile
|
|
1492
|
-
end
|
|
1493
|
-
|
|
1494
|
-
def display_errors(err_contents)
|
|
1495
|
-
return if err_contents.strip.empty?
|
|
1496
|
-
shell.say("\nERRORS", :red)
|
|
1497
|
-
puts err_contents
|
|
1498
|
-
shell.say('END OF ERRORS', :red)
|
|
1499
|
-
end
|
|
1500
|
-
|
|
1501
|
-
def log_errors(err_contents, outfile)
|
|
1502
|
-
return if err_contents.strip.empty?
|
|
1503
|
-
File.open(outfile, 'a') { |f_out| f_out.write(err_contents) }
|
|
1504
|
-
shell.say("appended to #{outfile}\n", :red)
|
|
1505
|
-
end
|
|
1506
|
-
|
|
1507
|
-
def simple_clean
|
|
1508
|
-
puts './clean'
|
|
1509
|
-
return if bash_execute('./clean')
|
|
1510
|
-
raise TestCaseDirError, 'Encountered an error when running `clean` in ' \
|
|
1511
|
-
"#{Dir.getwd} for test case #{test_name}."
|
|
1512
|
-
end
|
|
1513
|
-
|
|
1514
|
-
def mk
|
|
1515
|
-
puts './mk > mk.txt'
|
|
1516
|
-
unless bash_execute('./mk > mk.txt')
|
|
1517
|
-
raise TestCaseDirError, 'Encountered an error when running `mk` in ' \
|
|
1518
|
-
"#{Dir.getwd} for test case #{test_name}."
|
|
1519
|
-
end
|
|
1520
|
-
FileUtils.rm 'mk.txt'
|
|
1521
|
-
end
|
|
1522
|
-
|
|
1523
|
-
def remove_final_model
|
|
1524
|
-
# remove final model if it already exists
|
|
1525
|
-
return unless final_model
|
|
1526
|
-
return unless File.exist?(final_model)
|
|
1527
|
-
FileUtils.rm(final_model)
|
|
1528
|
-
end
|
|
1529
|
-
|
|
1530
|
-
def out_file
|
|
1531
|
-
File.join(test_case_dir, 'out.txt')
|
|
1532
|
-
end
|
|
1533
|
-
|
|
1534
|
-
# helpers for getting run summaries
|
|
1535
|
-
def run_summaries
|
|
1536
|
-
# look at all lines in out.txt
|
|
1537
|
-
lines = IO.readlines(out_file)
|
|
1538
|
-
|
|
1539
|
-
# find lines with summary information
|
|
1540
|
-
summary_line_numbers = []
|
|
1541
|
-
lines.each_with_index do |line, i|
|
|
1542
|
-
if line =~ /^\s*runtime \(minutes\),\s+retries,\s+backups,\ssteps/
|
|
1543
|
-
summary_line_numbers << i
|
|
1544
|
-
end
|
|
1545
|
-
end
|
|
1546
|
-
|
|
1547
|
-
# find lines indicating passage or failure of runs and restarts
|
|
1548
|
-
run_finish_line_numbers = []
|
|
1549
|
-
restart_finish_line_numbers = []
|
|
1550
|
-
lines.each_with_index do |line, i|
|
|
1551
|
-
if line =~ /^\s*((?:PASS)|(?:FAIL))\s+#{test_name}\s+restart/
|
|
1552
|
-
restart_finish_line_numbers << i
|
|
1553
|
-
elsif line =~ /^\s*((?:PASS)|(?:FAIL))\s+#{test_name}\s+run/
|
|
1554
|
-
run_finish_line_numbers << i
|
|
1555
|
-
end
|
|
1556
|
-
end
|
|
1557
|
-
|
|
1558
|
-
# only keep summaries that correspond to runs rather than restart
|
|
1559
|
-
summary_line_numbers.select do |i|
|
|
1560
|
-
run_summary?(i, run_finish_line_numbers, restart_finish_line_numbers)
|
|
1561
|
-
end.map { |line_number| lines[line_number] }
|
|
1562
|
-
end
|
|
1563
|
-
|
|
1564
|
-
def get_summary_text
|
|
1565
|
-
# original plan was to include diff data in summary text... now it's just
|
|
1566
|
-
# part of the test_instance object and is submitted as an integer
|
|
1567
|
-
# res = case diff
|
|
1568
|
-
# when 0
|
|
1569
|
-
# "No Diff\n"
|
|
1570
|
-
# when 1
|
|
1571
|
-
# "Diff\n"
|
|
1572
|
-
# else
|
|
1573
|
-
# "Ambiguous Diff\n"
|
|
1574
|
-
# end
|
|
1575
|
-
# res +
|
|
1576
|
-
IO.readlines(out_file).select do |line|
|
|
1577
|
-
line =~ /^\s*runtime/
|
|
1578
|
-
end.join
|
|
1579
|
-
end
|
|
1580
|
-
|
|
1581
|
-
def run_summary?(i, run_finish_line_numbers, restart_finish_line_numbers)
|
|
1582
|
-
# iterate from starting line (a summary line) up to largest PASS/FAIL
|
|
1583
|
-
# line, bail out if summary line is beyond any PASS/FAIL line
|
|
1584
|
-
max_line = run_finish_line_numbers.max || 0
|
|
1585
|
-
max_line = [max_line, (restart_finish_line_numbers.max || 0)].max
|
|
1586
|
-
return false if i > max_line
|
|
1587
|
-
# return true if next PASS/FAIL line is for a run and fail if it is for a
|
|
1588
|
-
# restart
|
|
1589
|
-
i.upto(max_line) do |j|
|
|
1590
|
-
return true if run_finish_line_numbers.include?(j)
|
|
1591
|
-
return false if restart_finish_line_numbers.include?(j)
|
|
1592
|
-
end
|
|
1593
|
-
false
|
|
873
|
+
is_valid = dir_or_symlink_exists? test_suite_dir
|
|
874
|
+
raise MesaDirError, "Invalid MESA dir: #{mesa.mesa_dir}" unless is_valid
|
|
1594
875
|
end
|
|
1595
876
|
|
|
1596
877
|
end
|
|
@@ -1605,13 +886,11 @@ end
|
|
|
1605
886
|
def visit_and_check(new_dir, exception, message)
|
|
1606
887
|
cwd = Dir.getwd
|
|
1607
888
|
shell.say "Leaving #{cwd}", :blue
|
|
1608
|
-
|
|
1609
|
-
shell.say "Entering #{new_dir}.", :blue
|
|
889
|
+
shell.say "\nEntering #{new_dir}.", :blue
|
|
1610
890
|
Dir.chdir(new_dir)
|
|
1611
891
|
success = yield if block_given?
|
|
1612
892
|
shell.say "Leaving #{new_dir}", :blue
|
|
1613
|
-
|
|
1614
|
-
shell.say "Entering #{cwd}.", :blue
|
|
893
|
+
shell.say "\nEntering #{cwd}.", :blue
|
|
1615
894
|
Dir.chdir(cwd)
|
|
1616
895
|
return if success
|
|
1617
896
|
raise exception, message
|
|
@@ -1619,18 +898,18 @@ end
|
|
|
1619
898
|
|
|
1620
899
|
# cd into a new directory, execute a block, then cd back into original
|
|
1621
900
|
# directory
|
|
1622
|
-
def visit_dir(new_dir)
|
|
901
|
+
def visit_dir(new_dir, quiet: false)
|
|
1623
902
|
cwd = Dir.getwd
|
|
1624
|
-
shell.say "Leaving #{cwd}\n", :blue
|
|
1625
|
-
shell.say "
|
|
903
|
+
shell.say "Leaving #{cwd}\n", :blue unless quiet
|
|
904
|
+
shell.say "\nEntering #{new_dir}.", :blue unless quiet
|
|
1626
905
|
Dir.chdir(new_dir)
|
|
1627
906
|
yield if block_given?
|
|
1628
|
-
shell.say "Leaving #{new_dir}\n", :blue
|
|
1629
|
-
shell.say "
|
|
1630
|
-
puts ""
|
|
907
|
+
shell.say "Leaving #{new_dir}\n", :blue unless quiet
|
|
908
|
+
shell.say "\nRe-entering #{cwd}.", :blue unless quiet
|
|
1631
909
|
Dir.chdir(cwd)
|
|
1632
910
|
end
|
|
1633
911
|
|
|
912
|
+
# the next function probalby doesn't belong here, but keep it anyway, please
|
|
1634
913
|
# create seed data for test cases for MesaTestHub of a given mesa version
|
|
1635
914
|
def generate_seeds_rb(mesa_dir, outfile)
|
|
1636
915
|
m = Mesa.new(mesa_dir: mesa_dir)
|
|
@@ -1641,7 +920,6 @@ def generate_seeds_rb(mesa_dir, outfile)
|
|
|
1641
920
|
m.test_names.each do |test_case_name|
|
|
1642
921
|
f.puts ' {'
|
|
1643
922
|
f.puts " name: '#{test_case_name}',"
|
|
1644
|
-
f.puts " version_added: #{m.version_number},"
|
|
1645
923
|
# no comma on last one
|
|
1646
924
|
if test_case_name == m.test_names[-1]
|
|
1647
925
|
f.puts(' }')
|
|
@@ -1654,12 +932,22 @@ def generate_seeds_rb(mesa_dir, outfile)
|
|
|
1654
932
|
end
|
|
1655
933
|
end
|
|
1656
934
|
|
|
1657
|
-
# Check if path is directory or symlink
|
|
1658
935
|
def dir_or_symlink_exists?(path)
|
|
1659
|
-
|
|
936
|
+
File.directory?(path) || File.symlink?(path)
|
|
1660
937
|
end
|
|
1661
938
|
|
|
1662
939
|
# force the execution to happen with bash
|
|
1663
|
-
def bash_execute(command)
|
|
1664
|
-
system('bash -c "' + command + '"')
|
|
940
|
+
def bash_execute(command, throw_exception=false)
|
|
941
|
+
res = system('bash -c "' + command + '"')
|
|
942
|
+
if !res && throw_exception
|
|
943
|
+
raise BashError('Encountered an error when executing the following '\
|
|
944
|
+
"command in bash: #{command}.")
|
|
945
|
+
end
|
|
946
|
+
res
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
# force execution to happen with bash, but return result rather than exit
|
|
950
|
+
# status (like backticks)
|
|
951
|
+
def bashticks(command)
|
|
952
|
+
`bash -c "#{command}"`.chomp
|
|
1665
953
|
end
|