asautotest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,124 @@
1
+ # -*- coding: utf-8 -*-
2
+ # logging.rb --- mixins for logging and various other utilities
3
+ # Copyright (C) 2010 Go Interactive
4
+
5
+ # This file is part of ASAutotest.
6
+
7
+ # ASAutotest is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # ASAutotest is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with ASAutotest. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module ASAutotest
21
+ module Bracketable
22
+ def [] *arguments ; new(*arguments) end
23
+ end
24
+
25
+ module Logging
26
+ PREFIX = "asautotest: "
27
+
28
+ def self.verbose= value
29
+ @verbose = value
30
+ end
31
+
32
+ def self.verbose?
33
+ @verbose == true
34
+ end
35
+
36
+ def verbose?
37
+ Logging.verbose?
38
+ end
39
+
40
+ # ------------------------------------------------------
41
+
42
+ def say(*arguments, &block)
43
+ if block_given?
44
+ say_with_block(*arguments, &block)
45
+ else
46
+ say_without_block(*arguments)
47
+ end
48
+ end
49
+
50
+ def shout(message)
51
+ say "\e[1;31m!!\e[0m #{message}"
52
+ end
53
+
54
+ def hint(message)
55
+ say "\e[34m#{message}\e[0m"
56
+ end
57
+
58
+ def whisper(*arguments, &block)
59
+ if block_given?
60
+ whisper_with_block(*arguments, &block)
61
+ else
62
+ whisper_without_block(*arguments)
63
+ end
64
+ end
65
+
66
+ def barf(message)
67
+ puts "\e[1;31m??\e[0m #{message}"
68
+ end
69
+
70
+ # ------------------------------------------------------
71
+
72
+ def say_without_block(message)
73
+ puts "#{PREFIX}#{message}"
74
+ end
75
+
76
+ def say_with_block(message, ok_message = "ok", error_message = "failed")
77
+ start_saying(message)
78
+ status = ""
79
+ yield(status)
80
+ end_saying(status.empty? ? ok_message : status)
81
+ ended = true
82
+ ensure
83
+ end_saying(error_message) unless ended
84
+ end
85
+
86
+ def start_saying(message)
87
+ STDOUT.print "#{PREFIX}#{message}..."
88
+ STDOUT.flush
89
+ end
90
+
91
+ def end_saying(message)
92
+ STDOUT.puts " #{message}."
93
+ end
94
+
95
+ # ------------------------------------------------------
96
+
97
+ def whisper_without_block(message)
98
+ say message if verbose?
99
+ end
100
+
101
+ def whisper_with_block(message, ok_message = "ok", error_message = "failed")
102
+ start_whisper(message)
103
+ yield
104
+ end_whisper(ok_message)
105
+ ended = true
106
+ ensure
107
+ end_whisper(error_message) unless ended
108
+ end
109
+
110
+ def start_whisper(message)
111
+ start_saying(message) if verbose?
112
+ end
113
+
114
+ def end_whisper(message)
115
+ end_saying(message) if verbose?
116
+ end
117
+
118
+ # ------------------------------------------------------
119
+
120
+ def new_logging_section
121
+ say("—" * 60)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,526 @@
1
+ # -*- coding: utf-8 -*-
2
+ # problematic-file.rb --- parsing of compiler errors and warnings
3
+ # Copyright (C) 2010 Go Interactive
4
+
5
+ # This file is part of ASAutotest.
6
+
7
+ # ASAutotest is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # ASAutotest is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with ASAutotest. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module ASAutotest
21
+ class ProblematicFile
22
+ include Logging
23
+
24
+ def initialize(file_name, source_directories)
25
+ @file_name = file_name
26
+ @source_directories = source_directories
27
+ @problems = []
28
+ end
29
+
30
+ def << problem
31
+ @problems << problem
32
+ problem.file = self
33
+ @problems = @problems.sort_by { |x| x.sort_key }
34
+ end
35
+
36
+ def n_problems
37
+ @problems.size
38
+ end
39
+
40
+ def type
41
+ Type[dirname.gsub(/\/|\\/, "."), basename.sub(/\..*$/, "")]
42
+ end
43
+
44
+ def any_type_warnings?
45
+ @problems.any? &:type_warning?
46
+ end
47
+
48
+ def problem_before(next_problem)
49
+ returning nil do
50
+ previous_problem = nil
51
+ for problem in @problems do
52
+ if problem == next_problem
53
+ return previous_problem
54
+ else
55
+ previous_problem = problem
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def print_report
62
+ puts
63
+ print ljust("\e[1m#{basename}\e[0m", 40)
64
+ print " (in #{dirname})" unless dirname == "." if has_file_name?
65
+ puts
66
+
67
+ @problems.each &:print_report
68
+ end
69
+
70
+ def has_file_name?
71
+ @file_name != nil
72
+ end
73
+
74
+ def basename
75
+ if @file_name == nil
76
+ "(compiler error)"
77
+ else
78
+ File.basename(@file_name)
79
+ end
80
+ end
81
+
82
+ def dirname
83
+ File.dirname(stripped_file_name) if has_file_name?
84
+ end
85
+
86
+ def stripped_file_name
87
+ if source_directory
88
+ @file_name[source_directory.size .. -1]
89
+ else
90
+ @file_name
91
+ end
92
+ end
93
+
94
+ def source_directory
95
+ @source_directories.find do |directory|
96
+ @file_name.start_with? directory
97
+ end
98
+ end
99
+ end
100
+
101
+ class Location
102
+ extend Bracketable
103
+ attr_reader :line_number, :column_number, :source_line
104
+
105
+ def initialize(line_number, column_number, source_line)
106
+ @line_number = line_number
107
+ @column_number = column_number
108
+ @source_line = source_line
109
+ end
110
+
111
+ def sort_key
112
+ [line_number, column_number]
113
+ end
114
+ end
115
+
116
+ class Type
117
+ extend Bracketable
118
+ attr_reader :package, :name
119
+ def initialize(package, name)
120
+ @package = package
121
+ @name = name
122
+ end
123
+
124
+ def self.parse(input)
125
+ case input
126
+ when /^(.*):(.*)$/
127
+ Type[$1, $2]
128
+ else
129
+ Type[nil, input]
130
+ end
131
+ end
132
+
133
+ def to_s
134
+ name ? name : package
135
+ end
136
+
137
+ def full_name
138
+ "#{package}.#{name}"
139
+ end
140
+
141
+ def == other
142
+ package == other.package and name == other.name
143
+ end
144
+ end
145
+
146
+ class Member
147
+ extend Bracketable
148
+ attr_reader :type, :name
149
+
150
+ def initialize(type, name)
151
+ @type = type
152
+ @name = name
153
+ end
154
+
155
+ def to_s
156
+ @type ? "#{@type.name}.#@name" : "#@name"
157
+ end
158
+ end
159
+
160
+ class Problem
161
+ attr_accessor :location
162
+ attr_accessor :file
163
+
164
+ def details ; nil end
165
+ def extra_details ; nil end
166
+ def type_warning? ; false end
167
+
168
+ def source_line
169
+ if location
170
+ build_string do |result|
171
+ result << location.source_line.trim
172
+ result << " ..." unless location.source_line =~ /[;{}]\s*$/
173
+ end
174
+ end
175
+ end
176
+
177
+ def sort_key
178
+ location ? location.sort_key : [0]
179
+ end
180
+
181
+ def line_number
182
+ location.line_number rescue "?"
183
+ end
184
+
185
+ def column_number
186
+ location.column_number - indentation_width rescue "?"
187
+ end
188
+
189
+ def indentation_width
190
+ location.source_line =~ /^(\s*)/ ; $1.size
191
+ end
192
+
193
+ def self.[] message, location
194
+ returning parse(message) do |problem|
195
+ problem.location = location
196
+ end
197
+ end
198
+
199
+ def self.parse(message)
200
+ case message.sub(/^(Error|Warning):\s+/, "")
201
+ when /^Definition (\S+) could not be found.$/i
202
+ UndefinedImport.new(Type.parse($1))
203
+ when /^Call to a possibly undefined method (\S+) .* type (\S+).$/i
204
+ UndefinedMethod.new(Member[Type.parse($2), $1])
205
+ when /^Call to a possibly undefined method (\S+).$/i
206
+ UndefinedMethod.new(Member[nil, $1])
207
+ when /^Access of undefined property (\S+).$/i
208
+ UndefinedProperty.new(Member[nil, $1])
209
+ when /^Access of possibly undefined property (\S+)/i
210
+ UndefinedProperty.new(Member[nil, $1])
211
+ when /^Attempted access of inaccessible property (\S+)/i
212
+ InaccessibleProperty.new(Member[nil, $1])
213
+ when /^A file found in a source-path must have .*? '(\S+?)'/i
214
+ WrongPackage.new(Type[$1, nil])
215
+ when /^A file found in a source-path '(\S+?)' must .* as the class/i
216
+ WrongClassName.new(Type[nil, $1])
217
+ when /^Type was not found or was not a compile-time constant: (\S+).$/i
218
+ UndefinedType.new(Type.parse($1))
219
+ when /^The definition of base class (\S+) was not found.$/i
220
+ UndefinedType.new(Type.parse($1))
221
+ when /^Illegal assignment to a variable specified as constant.$/i
222
+ ConstantAssignment.new
223
+ when /^Method marked override must override another method.$/i
224
+ BogusOverride.new
225
+ when /^Incorrect number of arguments.\s* Expected no more than (\d+).$/i
226
+ TooManyArguments.new($1.to_i)
227
+ when /^Incorrect number of arguments.\s* Expected (0).$/i
228
+ TooManyArguments.new($1.to_i)
229
+ when /^Incorrect number of arguments.\s* Expected (\d+).$/i
230
+ TooFewArguments.new($1.to_i)
231
+ when /^return value for function '(\S+)' has no type declaration.$/i
232
+ MissingReturnType.new(Member[nil, $1])
233
+ when /^(?:variable|parameter) '(\S+)' has no type declaration.$/i
234
+ MissingTypeDeclaration.new
235
+ when /^Interface (\S+) was not found.$/i
236
+ InterfaceNotFound.new(Type.parse($1))
237
+ when /^Implicit coercion of a value of type (\S+) to an unrelated type (\S+).$/i
238
+ TypeMismatch.new(Type.parse($2), Type.parse($1))
239
+ when /^Comparison between a value with static type (\S+) and a possibly unrelated type Null.$/i
240
+ InvalidNullComparison.new
241
+ when /^Interface method ((?:get |set )?\S+) in namespace (\S+) not implemented by class (\S+).$/i
242
+ MissingImplementation.new(Member[Type.parse($2), $1], Type.parse($3))
243
+ when /^Interface method ((?:get |set )?\S+) in namespace (\S+) is implemented with an incompatible signature in class (\S+).$/i
244
+ WrongImplementation.new(Member[Type.parse($2), $1], Type.parse($3))
245
+ when /^A file (.*) must have an externally visible definition./
246
+ MissingPublicDefinition.new
247
+ else
248
+ Unknown.new(message)
249
+ end
250
+ end
251
+
252
+ def detail ; nil end
253
+ def plain_message
254
+ if detail
255
+ "#{message} #{detail}"
256
+ else
257
+ message.sub(/:$/, ".")
258
+ end
259
+ end
260
+
261
+ class ConstantAssignment < Problem
262
+ def message ; "Attempt to modify constant:" end
263
+ def details ; identifier_source_line_details end
264
+ end
265
+
266
+ class BogusOverride < Problem
267
+ def message ; "Bogus override:" end
268
+ def details ; identifier_source_line_details end
269
+ end
270
+
271
+ class UndefinedImport < Problem
272
+ def initialize(type) @type = type end
273
+ def message ; "Import not found:" end
274
+ def details ; bullet_details(detail) end
275
+ def detail ; @type.full_name end
276
+ end
277
+
278
+ class UndefinedMethod < Problem
279
+ def initialize(member) @member = member end
280
+ def message ; "Undefined method:" end
281
+ def details ; identifier_source_line_details end
282
+ def detail ; @member end
283
+ end
284
+
285
+ class UndefinedProperty < Problem
286
+ def initialize(member) @member = member end
287
+ def message ; "Undefined property:" end
288
+ def details ; identifier_source_line_details end
289
+ def detail ; @member end
290
+ end
291
+
292
+ class InaccessibleProperty < Problem
293
+ def initialize(member) @member = member end
294
+ def message ; "Property access not allowed:" end
295
+ def details ; identifier_source_line_details end
296
+ def detail ; @member end
297
+ end
298
+
299
+ class WrongPackage < Problem
300
+ def initialize(type) @type = type end
301
+ def message ; "Package should be #@type." end
302
+ end
303
+
304
+ class WrongClassName < Problem
305
+ def initialize(type) @type = type end
306
+ def message ; "Class name should be #@type." end
307
+ end
308
+
309
+ class UndefinedType < Problem
310
+ def initialize(type) @type = type end
311
+ def message ; "Undefined type:" end
312
+ def details ; identifier_source_line_details end
313
+ def detail ; @type.full_name end
314
+ def extra_details
315
+ if problematic_identifier == "Sprite"
316
+ "Hint: It’s flash.display.Sprite."
317
+ end
318
+ end
319
+ end
320
+
321
+ class TooManyArguments < Problem
322
+ def initialize(max) @max = max end
323
+ def message
324
+ if @max == 0
325
+ "No arguments allowed:"
326
+ else
327
+ "Too many arguments (only #@max allowed):"
328
+ end
329
+ end
330
+ def details ; source_line_details end
331
+ end
332
+
333
+ class TooFewArguments < Problem
334
+ def initialize(min) @min = min end
335
+ def message ; "Too few arguments (expected #@min):" end
336
+ def details ; source_line_details end
337
+ end
338
+
339
+ class MissingReturnType < Problem
340
+ def initialize(member) @member = member end
341
+ def message ; "Missing return type:" end
342
+ def detail ; @member end
343
+ def details ; member_details end
344
+ def type_warning? ; true end
345
+ end
346
+
347
+ class MissingTypeDeclaration < Problem
348
+ def message ; "Missing type:" end
349
+ def details ; identifier_source_line_details end
350
+ def type_warning? ; true end
351
+ end
352
+
353
+ class InterfaceNotFound < Problem
354
+ def initialize(member) @member = member end
355
+ def message ; "Interface not found:" end
356
+ def detail ; @member end
357
+ def details ; member_details end
358
+ end
359
+
360
+ class InvalidNullComparison < Problem
361
+ def message ; "Invalid comparison to null." end
362
+ def details ; source_line_details end
363
+ end
364
+
365
+ class TypeMismatch < Problem
366
+ def initialize(expected_type, actual_type)
367
+ @expected_type = expected_type
368
+ @actual_type = actual_type
369
+ end
370
+
371
+ def message
372
+ "Expected \e[4m#@expected_type\e[0m " +
373
+ "but was \e[4m#@actual_type\e[0m:"
374
+ end
375
+
376
+ def details
377
+ identifier_source_line_details
378
+ end
379
+ end
380
+
381
+ class ImplementationProblem < Problem
382
+ def initialize(member, implementing_type)
383
+ @member = member
384
+ @implementing_type = implementing_type
385
+ end
386
+
387
+ def message
388
+ if file.type == @implementing_type
389
+ "#{core_message}:"
390
+ else
391
+ "#{core_message} in #@implementing_type:"
392
+ end
393
+ end
394
+
395
+ def detail ; @member end
396
+
397
+ def details
398
+ "\e[0m * \e[1m#{@member.name}\e[0m (#{@member.type})\e[0m"
399
+ end
400
+ end
401
+
402
+ class MissingImplementation < ImplementationProblem
403
+ def core_message
404
+ "Missing implementation"
405
+ end
406
+ end
407
+
408
+ class WrongImplementation < ImplementationProblem
409
+ def core_message
410
+ "Wrong implementation"
411
+ end
412
+ end
413
+
414
+ class MissingPublicDefinition < Problem
415
+ def message ; "Missing public class or function definition." end
416
+ end
417
+
418
+ class Unknown < Problem
419
+ def initialize(message) @message = message end
420
+
421
+ def message
422
+ @message.sub(/^Error:\s+/, "")
423
+ end
424
+
425
+ def details
426
+ if source_line
427
+ source_line_details
428
+ end
429
+ end
430
+ end
431
+
432
+ def print_report
433
+ unless message == last_message
434
+ print message_column
435
+ print dummy_line_number_column unless message_column_overflowed?
436
+ puts
437
+ end
438
+
439
+ if details and details != last_details
440
+ print details_column
441
+ print line_number_column
442
+ puts
443
+ end
444
+
445
+ if extra_details and extra_details != last_extra_details
446
+ for line in extra_details
447
+ puts "\e[34m #{line.chomp}\e[0m"
448
+ end
449
+ end
450
+ end
451
+
452
+ MESSAGE_COLUMN_WIDTH = 56
453
+ LINE_NUMBER_COLUMN_WIDTH = 4
454
+
455
+ def message_column_overflowed?
456
+ message_column.size > MESSAGE_COLUMN_WIDTH
457
+ end
458
+
459
+ def message_column
460
+ ljust(" #{message}", MESSAGE_COLUMN_WIDTH)
461
+ end
462
+
463
+ def details_column
464
+ ljust(" #{details} ", MESSAGE_COLUMN_WIDTH)
465
+ end
466
+
467
+ def line_number_column
468
+ rjust("\e[1m#{line_number}\e[0m", LINE_NUMBER_COLUMN_WIDTH)
469
+ end
470
+
471
+ def dummy_line_number_column
472
+ rjust("\e[0m|\e[0m", LINE_NUMBER_COLUMN_WIDTH)
473
+ end
474
+
475
+ def last_message
476
+ previous.message if previous
477
+ end
478
+
479
+ def last_details
480
+ previous.details if previous
481
+ end
482
+
483
+ def last_extra_details
484
+ previous.extra_details if previous
485
+ end
486
+
487
+ def previous
488
+ file.problem_before(self)
489
+ end
490
+
491
+ def source_line_details
492
+ "\e[0m ... #{source_line}\e[0m"
493
+ end
494
+
495
+ def identifier_source_line_details
496
+ "\e[0m ... #{problematic_identifier_pre}" +
497
+ "\e[1;4m#{problematic_identifier}\e[0m" +
498
+ "#{problematic_identifier_post}"
499
+ end
500
+
501
+ def problematic_identifier_pre
502
+ identifier_source_line_parts[0]
503
+ end
504
+
505
+ def problematic_identifier
506
+ identifier_source_line_parts[1]
507
+ end
508
+
509
+ def problematic_identifier_post
510
+ identifier_source_line_parts[2]
511
+ end
512
+
513
+ def identifier_source_line_parts
514
+ source_line =~ /^(.{#{column_number}})([\w$]+)(.*)$/
515
+ [$1, $2, $3]
516
+ end
517
+
518
+ def member_details
519
+ bullet_details(@member)
520
+ end
521
+
522
+ def bullet_details(content)
523
+ "\e[0m * #{content}\e[0m"
524
+ end
525
+ end
526
+ end