doing 2.1.24 → 2.1.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +11 -10
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -1
- data/bin/doing +81 -37
- data/docs/doc/Array.html +70 -7
- data/docs/doc/BooleanTermParser/Clause.html +5 -5
- data/docs/doc/BooleanTermParser/Operator.html +4 -4
- data/docs/doc/BooleanTermParser/Query.html +8 -8
- data/docs/doc/BooleanTermParser/QueryParser.html +2 -2
- data/docs/doc/BooleanTermParser/QueryTransformer.html +2 -2
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +4 -4
- data/docs/doc/Doing/Completion.html +2 -2
- data/docs/doc/Doing/Configuration.html +14 -16
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +2 -2
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +2 -2
- data/docs/doc/Doing/Errors/DoingStandardError.html +2 -2
- data/docs/doc/Doing/Errors/EmptyInput.html +2 -2
- data/docs/doc/Doing/Errors/NoResults.html +2 -2
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +2 -2
- data/docs/doc/Doing/Errors/WrongCommand.html +2 -2
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +6 -6
- data/docs/doc/Doing/Item.html +50 -16
- data/docs/doc/Doing/Items.html +10 -10
- data/docs/doc/Doing/LogAdapter.html +24 -24
- data/docs/doc/Doing/Note.html +7 -7
- data/docs/doc/Doing/Pager.html +4 -4
- data/docs/doc/Doing/Plugins.html +7 -7
- data/docs/doc/Doing/Prompt.html +14 -14
- data/docs/doc/Doing/Section.html +6 -6
- data/docs/doc/Doing/TemplateString.html +8 -8
- data/docs/doc/Doing/Types.html +6 -1
- data/docs/doc/Doing/Util/Backup.html +10 -10
- data/docs/doc/Doing/Util.html +15 -15
- data/docs/doc/Doing/WWID.html +65 -53
- data/docs/doc/Doing.html +3 -3
- data/docs/doc/GLI/Commands/Help.html +3 -3
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +17 -17
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +45 -11
- data/docs/doc/Numeric.html +5 -5
- data/docs/doc/PhraseParser/Operator.html +4 -4
- data/docs/doc/PhraseParser/PhraseClause.html +5 -5
- data/docs/doc/PhraseParser/Query.html +10 -10
- data/docs/doc/PhraseParser/QueryParser.html +2 -2
- data/docs/doc/PhraseParser/QueryTransformer.html +2 -2
- data/docs/doc/PhraseParser/TermClause.html +5 -5
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +7 -7
- data/docs/doc/String.html +107 -44
- data/docs/doc/Symbol.html +8 -8
- data/docs/doc/Time.html +6 -6
- data/docs/doc/_index.html +41 -18
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +369 -313
- data/docs/doc/top-level-namespace.html +2 -2
- data/doing.rdoc +19 -11
- data/example_plugin.rb +2 -2
- data/lib/completion/_doing.zsh +12 -12
- data/lib/completion/doing.bash +2 -2
- data/lib/completion/doing.fish +9 -8
- data/lib/doing/changelog/changes.rb +1 -1
- data/lib/doing/configuration.rb +4 -6
- data/lib/doing/good.rb +64 -0
- data/lib/doing/hash.rb +4 -0
- data/lib/doing/hooks.rb +3 -3
- data/lib/doing/item.rb +14 -10
- data/lib/doing/plugins/import/calendar_import.rb +1 -1
- data/lib/doing/plugins/import/doing_import.rb +1 -1
- data/lib/doing/plugins/import/timing_import.rb +1 -1
- data/lib/doing/string.rb +1 -1
- data/lib/doing/template_string.rb +2 -2
- data/lib/doing/types.rb +1 -0
- data/lib/doing/util.rb +10 -10
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +40 -18
- data/lib/doing.rb +1 -0
- data/lib/helpers/threaded_tests.rb +35 -64
- data/lib/helpers/threaded_tests_string.rb +50 -0
- metadata +4 -2
@@ -131,7 +131,7 @@ module Doing
|
|
131
131
|
|
132
132
|
after = m['after']
|
133
133
|
|
134
|
-
if value.
|
134
|
+
if !value.good?
|
135
135
|
after
|
136
136
|
else
|
137
137
|
pad = m['width'].to_i
|
@@ -183,7 +183,7 @@ module Doing
|
|
183
183
|
' '
|
184
184
|
else
|
185
185
|
line = l.gsub(/%/, '\%').strip.wrap(width, pad: pad, indent: indent, offset: 0, prefix: prefix, color: last_color, after: after, reset: reset, pad_first: true)
|
186
|
-
line.highlight_tags!(tags_color, last_color: last_color) unless !tags_color || tags_color.
|
186
|
+
line.highlight_tags!(tags_color, last_color: last_color) unless !tags_color || !tags_color.good?
|
187
187
|
"#{line} "
|
188
188
|
end
|
189
189
|
end.join("\n")
|
data/lib/doing/types.rb
CHANGED
data/lib/doing/util.rb
CHANGED
@@ -19,7 +19,7 @@ module Doing
|
|
19
19
|
## @param cli [String] The name or path of the cli
|
20
20
|
##
|
21
21
|
def exec_available(cli)
|
22
|
-
return false
|
22
|
+
return false unless cli.good?
|
23
23
|
|
24
24
|
!TTY::Which.which(cli).nil?
|
25
25
|
end
|
@@ -32,7 +32,7 @@ module Doing
|
|
32
32
|
##
|
33
33
|
def first_available_exec(*commands)
|
34
34
|
commands.compact.map(&:strip).reject(&:empty?).uniq
|
35
|
-
|
35
|
+
.find { |cmd| exec_available(cmd.split.first) }
|
36
36
|
end
|
37
37
|
|
38
38
|
def merge_default_proc(target, overwrite)
|
@@ -56,7 +56,7 @@ module Doing
|
|
56
56
|
# @param [Hash] other_hash The other hash
|
57
57
|
#
|
58
58
|
def deep_merge_hashes(master_hash, other_hash)
|
59
|
-
deep_merge_hashes!(master_hash.
|
59
|
+
deep_merge_hashes!(master_hash.clone, other_hash)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Merges a master hash with another hash, recursively.
|
@@ -157,32 +157,32 @@ module Doing
|
|
157
157
|
def find_default_editor(editor_for = 'default')
|
158
158
|
# return nil unless $stdout.isatty || ENV['DOING_EDITOR_TEST']
|
159
159
|
|
160
|
-
if ENV['DOING_EDITOR_TEST']
|
161
|
-
return ENV['EDITOR']
|
162
|
-
end
|
160
|
+
return ENV['EDITOR'] if ENV['DOING_EDITOR_TEST']
|
163
161
|
|
164
162
|
editor_config = Doing.config.settings['editors']
|
165
163
|
|
166
164
|
if editor_config.is_a?(String)
|
167
|
-
|
165
|
+
msg = "Please update your configuration, 'editors' should be a mapping."
|
166
|
+
msg << ' Delete the key and run `doing config --update`.'
|
167
|
+
Doing.logger.warn('Deprecated:', msg)
|
168
168
|
return editor_config
|
169
169
|
end
|
170
170
|
|
171
171
|
if editor_config[editor_for]
|
172
172
|
editor = editor_config[editor_for]
|
173
173
|
# Doing.logger.debug('Editor:', "Using #{editor} from config 'editors->#{editor_for}'")
|
174
|
-
return editor unless editor.
|
174
|
+
return editor unless editor.good?
|
175
175
|
end
|
176
176
|
|
177
177
|
if editor_for != 'editor' && editor_config['default']
|
178
178
|
editor = editor_config['default']
|
179
179
|
# Doing.logger.debug('Editor:', "Using #{editor} from config: 'editors->default'")
|
180
|
-
return editor unless editor.
|
180
|
+
return editor unless editor.good?
|
181
181
|
end
|
182
182
|
|
183
183
|
editor ||= ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR']
|
184
184
|
|
185
|
-
unless editor.
|
185
|
+
unless editor.good?
|
186
186
|
# Doing.logger.debug('Editor:', "Found editor in environment variables: #{editor}")
|
187
187
|
return editor
|
188
188
|
end
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -338,6 +338,7 @@ module Doing
|
|
338
338
|
## @option opt :note [Array] item note (will be converted if value is String)
|
339
339
|
## @option opt :back [Date] backdate
|
340
340
|
## @option opt :timed [Boolean] new item is timed entry, marks previous entry as @done
|
341
|
+
## @option opt :done [Date] If set, adds a @done tag to new entry
|
341
342
|
##
|
342
343
|
def add_item(title, section = nil, opt)
|
343
344
|
opt ||= {}
|
@@ -360,9 +361,18 @@ module Doing
|
|
360
361
|
|
361
362
|
title.compress!
|
362
363
|
entry = Item.new(opt[:back], title.strip, section)
|
364
|
+
|
365
|
+
if opt[:done] && entry.should_finish?
|
366
|
+
if entry.should_time?
|
367
|
+
entry.tag('done', value: opt[:done])
|
368
|
+
else
|
369
|
+
entry.tag('done')
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
363
373
|
entry.note = note
|
364
374
|
|
365
|
-
items = @content.
|
375
|
+
items = @content.clone
|
366
376
|
if opt[:timed]
|
367
377
|
items.reverse!
|
368
378
|
items.each_with_index do |i, x|
|
@@ -380,7 +390,8 @@ module Doing
|
|
380
390
|
# logger.count(:added, level: :debug)
|
381
391
|
logger.info('New entry:', %(added "#{entry.date.relative_date}: #{entry.title}" to #{section}))
|
382
392
|
|
383
|
-
Hooks.trigger :post_entry_added, self, entry
|
393
|
+
Hooks.trigger :post_entry_added, self, entry
|
394
|
+
entry
|
384
395
|
end
|
385
396
|
|
386
397
|
##
|
@@ -474,6 +485,7 @@ module Doing
|
|
474
485
|
#
|
475
486
|
def repeat_item(item, opt)
|
476
487
|
opt ||= {}
|
488
|
+
old_item = item.clone
|
477
489
|
if item.should_finish?
|
478
490
|
if item.should_time?
|
479
491
|
finish_date = verify_duration(item.date, Time.now, title: item.title)
|
@@ -481,7 +493,7 @@ module Doing
|
|
481
493
|
else
|
482
494
|
item.title.tag!('done')
|
483
495
|
end
|
484
|
-
Hooks.trigger :post_entry_updated, self, item
|
496
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
485
497
|
end
|
486
498
|
|
487
499
|
# Remove @done tag
|
@@ -651,7 +663,7 @@ module Doing
|
|
651
663
|
|
652
664
|
if items.nil? || items.empty?
|
653
665
|
section = opt[:section] ? guess_section(opt[:section]) : 'All'
|
654
|
-
items = section =~ /^all$/i ? @content.
|
666
|
+
items = section =~ /^all$/i ? @content.clone : @content.in_section(section)
|
655
667
|
end
|
656
668
|
|
657
669
|
opt[:time_filter] = [nil, nil]
|
@@ -847,7 +859,7 @@ module Doing
|
|
847
859
|
note.map!(&:strip)
|
848
860
|
note.delete_if(&:ignore?)
|
849
861
|
item = items[i]
|
850
|
-
old_item = item.
|
862
|
+
old_item = item.clone
|
851
863
|
item.date = date || items[i].date
|
852
864
|
item.title = title
|
853
865
|
item.note = note
|
@@ -855,7 +867,7 @@ module Doing
|
|
855
867
|
Doing.logger.count(:skipped, level: :debug)
|
856
868
|
else
|
857
869
|
Doing.logger.count(:updated)
|
858
|
-
Hooks.trigger :post_entry_updated, self, item
|
870
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
859
871
|
end
|
860
872
|
end
|
861
873
|
end
|
@@ -1045,9 +1057,10 @@ module Doing
|
|
1045
1057
|
else
|
1046
1058
|
opt[:resume]
|
1047
1059
|
end
|
1060
|
+
old_item = item.clone
|
1048
1061
|
new_entry = reset_item(item, date: date, resume: res)
|
1049
1062
|
@content.update_item(item, new_entry)
|
1050
|
-
Hooks.trigger :post_entry_updated, self, new_entry
|
1063
|
+
Hooks.trigger :post_entry_updated, self, new_entry, old_item
|
1051
1064
|
end
|
1052
1065
|
write(@doing_file)
|
1053
1066
|
|
@@ -1062,8 +1075,9 @@ module Doing
|
|
1062
1075
|
if opt[:flag]
|
1063
1076
|
tag = @config['marker_tag'] || 'flagged'
|
1064
1077
|
items.map! do |i|
|
1078
|
+
old_item = i.clone
|
1065
1079
|
i.tag(tag, date: false, remove: opt[:remove], single: single)
|
1066
|
-
Hooks.trigger :post_entry_updated, self, i
|
1080
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
1067
1081
|
end
|
1068
1082
|
end
|
1069
1083
|
|
@@ -1071,9 +1085,10 @@ module Doing
|
|
1071
1085
|
tag = 'done'
|
1072
1086
|
items.map! do |i|
|
1073
1087
|
if i.should_finish?
|
1088
|
+
old_item = i.clone
|
1074
1089
|
should_date = !opt[:cancel] && i.should_time?
|
1075
1090
|
i.tag(tag, date: should_date, remove: opt[:remove], single: single)
|
1076
|
-
Hooks.trigger :post_entry_updated, self, i
|
1091
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
1077
1092
|
end
|
1078
1093
|
end
|
1079
1094
|
end
|
@@ -1087,8 +1102,9 @@ module Doing
|
|
1087
1102
|
else
|
1088
1103
|
logger.count(:added_tags)
|
1089
1104
|
logger.write(items.count == 1 ? :info : :debug, 'Tagged:', new_title)
|
1105
|
+
old_item = i.clone
|
1090
1106
|
i.title = new_title
|
1091
|
-
Hooks.trigger :post_entry_updated, self, i
|
1107
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
1092
1108
|
end
|
1093
1109
|
end
|
1094
1110
|
end
|
@@ -1096,17 +1112,19 @@ module Doing
|
|
1096
1112
|
if opt[:tag]
|
1097
1113
|
tag = opt[:tag]
|
1098
1114
|
items.map! do |i|
|
1115
|
+
old_item = i.clone
|
1099
1116
|
i.tag(tag, date: false, remove: opt[:remove], single: single)
|
1100
1117
|
i.expand_date_tags(@config['date_tags'])
|
1101
|
-
Hooks.trigger :post_entry_updated, self, i
|
1118
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
1102
1119
|
end
|
1103
1120
|
end
|
1104
1121
|
|
1105
1122
|
if opt[:archive] || opt[:move]
|
1106
1123
|
section = opt[:archive] ? 'Archive' : guess_section(opt[:move])
|
1107
1124
|
items.map! do |i|
|
1125
|
+
old_item = i.clone
|
1108
1126
|
i.move_to(section, label: true)
|
1109
|
-
Hooks.trigger :post_entry_updated, self, i
|
1127
|
+
Hooks.trigger :post_entry_updated, self, i, old_item
|
1110
1128
|
end
|
1111
1129
|
end
|
1112
1130
|
|
@@ -1228,6 +1246,7 @@ module Doing
|
|
1228
1246
|
end
|
1229
1247
|
|
1230
1248
|
items.each do |item|
|
1249
|
+
old_item = item.clone
|
1231
1250
|
added = []
|
1232
1251
|
removed = []
|
1233
1252
|
|
@@ -1322,7 +1341,7 @@ module Doing
|
|
1322
1341
|
end
|
1323
1342
|
|
1324
1343
|
item.expand_date_tags(@config['date_tags'])
|
1325
|
-
Hooks.trigger :post_entry_updated, self, item
|
1344
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
1326
1345
|
end
|
1327
1346
|
|
1328
1347
|
write(@doing_file)
|
@@ -1361,6 +1380,7 @@ module Doing
|
|
1361
1380
|
return
|
1362
1381
|
end
|
1363
1382
|
|
1383
|
+
old_item = item.clone
|
1364
1384
|
content = ["#{item.date.strftime('%F %R')} | #{item.title.dup}"]
|
1365
1385
|
content << item.note.strip_lines.join("\n") unless item.note.empty?
|
1366
1386
|
new_item = fork_editor(content.join("\n"))
|
@@ -1376,7 +1396,7 @@ module Doing
|
|
1376
1396
|
item.title = title
|
1377
1397
|
item.note.add(note, replace: true)
|
1378
1398
|
logger.info('Edited:', item.title)
|
1379
|
-
Hooks.trigger :post_entry_updated, self, item
|
1399
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
1380
1400
|
|
1381
1401
|
write(@doing_file)
|
1382
1402
|
end
|
@@ -1413,6 +1433,7 @@ module Doing
|
|
1413
1433
|
found_items = 0
|
1414
1434
|
|
1415
1435
|
@content.each_with_index do |item, i|
|
1436
|
+
old_item = i.clone
|
1416
1437
|
next unless item.section == opt[:section] || opt[:section] =~ /all/i
|
1417
1438
|
|
1418
1439
|
next unless item.title =~ /@#{tag}/
|
@@ -1431,7 +1452,7 @@ module Doing
|
|
1431
1452
|
logger.count(:completed)
|
1432
1453
|
logger.info('Completed:', item.title)
|
1433
1454
|
end
|
1434
|
-
Hooks.trigger :post_entry_updated, self, item
|
1455
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
1435
1456
|
end
|
1436
1457
|
|
1437
1458
|
|
@@ -1492,7 +1513,7 @@ module Doing
|
|
1492
1513
|
|
1493
1514
|
unless ((!tags.empty? && !item.tags?(tags, bool)) || (opt[:search] && !item.search(opt[:search].to_s)) || (opt[:before] && item.date >= cutoff))
|
1494
1515
|
new_item = @content.delete(item)
|
1495
|
-
Hooks.trigger :post_entry_removed, self, item.
|
1516
|
+
Hooks.trigger :post_entry_removed, self, item.clone
|
1496
1517
|
raise DoingRuntimeError, "Error deleting item: #{item}" if new_item.nil?
|
1497
1518
|
|
1498
1519
|
new_content.add_section(new_item.section, log: false)
|
@@ -2202,7 +2223,7 @@ EOS
|
|
2202
2223
|
|
2203
2224
|
filename ||= @config['doing_file']
|
2204
2225
|
init_doing_file(filename)
|
2205
|
-
current_content = @content.
|
2226
|
+
current_content = @content.clone
|
2206
2227
|
backup_file = Util::Backup.last_backup(filename, count: 1)
|
2207
2228
|
raise DoingRuntimeError, 'No undo history to diff' if backup_file.nil?
|
2208
2229
|
|
@@ -2321,8 +2342,9 @@ EOS
|
|
2321
2342
|
item
|
2322
2343
|
else
|
2323
2344
|
counter += 1
|
2345
|
+
old_item = item.clone
|
2324
2346
|
item.move_to(destination, label: label, log: false)
|
2325
|
-
Hooks.trigger :post_entry_updated, self, item
|
2347
|
+
Hooks.trigger :post_entry_updated, self, item, old_item
|
2326
2348
|
item
|
2327
2349
|
end
|
2328
2350
|
end
|
data/lib/doing.rb
CHANGED
@@ -31,6 +31,7 @@ require_relative 'doing/template_string'
|
|
31
31
|
require_relative 'doing/string'
|
32
32
|
require_relative 'doing/time'
|
33
33
|
require_relative 'doing/array'
|
34
|
+
require_relative 'doing/good'
|
34
35
|
require_relative 'doing/symbol'
|
35
36
|
require_relative 'doing/util'
|
36
37
|
require_relative 'doing/util_backup'
|
@@ -2,64 +2,21 @@
|
|
2
2
|
|
3
3
|
require 'tty-spinner'
|
4
4
|
require 'tty-progressbar'
|
5
|
-
require './lib/doing'
|
6
5
|
require 'open3'
|
7
6
|
require 'shellwords'
|
7
|
+
require 'fileutils'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def highlight_errors
|
13
|
-
cols = `tput cols`.strip.to_i
|
14
|
-
|
15
|
-
string = dup
|
16
|
-
|
17
|
-
errs = string.scan(/(?<==\n)(?:Failure|Error):.*?(?=\n=+)/m)
|
18
|
-
|
19
|
-
errs.map! do |error|
|
20
|
-
err = error.dup
|
21
|
-
|
22
|
-
err.gsub!(%r{^(/.*?/)([^/:]+):(\d+):in (.*?)$}) do
|
23
|
-
m = Regexp.last_match
|
24
|
-
"#{m[1].white}#{m[2].bold.white}:#{m[3].yellow}:in #{m[4].cyan}"
|
25
|
-
end
|
26
|
-
err.gsub!(/(Failure|Error): (.*?)\((.*?)\):\n (.*?)(?=\n)/m) do
|
27
|
-
m = Regexp.last_match
|
28
|
-
[
|
29
|
-
m[1].bold.boldbgred.white,
|
30
|
-
m[3].bold.boldbgcyan.white,
|
31
|
-
m[2].bold.boldbgyellow.black,
|
32
|
-
" #{m[4]} ".bold.boldbgwhite.black.reset
|
33
|
-
].join(':'.boldblack.boldbgblack.reset)
|
34
|
-
end
|
35
|
-
err.gsub!(/(<.*?>) (was expected to) (.*?)\n( *<.*?>)./m) do
|
36
|
-
m = Regexp.last_match
|
37
|
-
"#{m[1].bold.green} #{m[2].white} #{m[3].boldwhite.boldbgred.reset}\n#{m[4].bold.white}"
|
38
|
-
end
|
39
|
-
err.gsub!(/(Finished in) ([\d.]+) (seconds)/) do
|
40
|
-
m = Regexp.last_match
|
41
|
-
"#{m[1].green} #{m[2].bold.white} #{m[3].green}"
|
42
|
-
end
|
43
|
-
err.gsub!(/(\d+) (failures)/) do
|
44
|
-
m = Regexp.last_match
|
45
|
-
"#{m[1].bold.red} #{m[2].red}"
|
46
|
-
end
|
47
|
-
err.gsub!(/100% passed/) do |m|
|
48
|
-
m.bold.green
|
49
|
-
end
|
50
|
-
|
51
|
-
err
|
52
|
-
end
|
53
|
-
|
54
|
-
errs.join("\n#{('=' * cols).blue}\n")
|
55
|
-
end
|
56
|
-
end
|
9
|
+
$LOAD_PATH.unshift File.join(__dir__, '..')
|
10
|
+
require 'doing'
|
11
|
+
require 'helpers/threaded_tests_string'
|
57
12
|
|
58
13
|
class ThreadedTests
|
59
14
|
include Doing::Color
|
15
|
+
include ThreadedTestString
|
60
16
|
|
61
17
|
def run(pattern: '*', max_threads: 8, max_tests: 0)
|
62
18
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
19
|
+
@results = File.expand_path('results.log')
|
63
20
|
|
64
21
|
max_threads = 1000 if max_threads == 0
|
65
22
|
|
@@ -90,13 +47,21 @@ class ThreadedTests
|
|
90
47
|
].join('')
|
91
48
|
progress = TTY::ProgressBar::Multi.new(banner,
|
92
49
|
width: 12,
|
50
|
+
clear: true,
|
93
51
|
hide_cursor: true)
|
94
52
|
@children = []
|
95
53
|
tests.each do |t|
|
96
54
|
test_name = File.basename(t, '.rb').sub(/doing_(.*?)_test/, '\1')
|
97
55
|
new_sp = progress.register("[#{':bar'.cyan}] #{test_name.bold.white}:status",
|
98
|
-
total:
|
99
|
-
|
56
|
+
total: tests.count + 8,
|
57
|
+
width: 1,
|
58
|
+
head: ' ',
|
59
|
+
unknown: ' ',
|
60
|
+
hide_cursor: true,
|
61
|
+
clear: true)
|
62
|
+
status = ': waiting'.dark.yellow.reset
|
63
|
+
@children.push([test_name, new_sp, status])
|
64
|
+
# new_sp.advance(status: ': waiting'.dark.yellow.reset)
|
100
65
|
end
|
101
66
|
|
102
67
|
@elapsed = 0.0
|
@@ -140,30 +105,36 @@ class ThreadedTests
|
|
140
105
|
rescue
|
141
106
|
progress.stop
|
142
107
|
end
|
108
|
+
ensure
|
109
|
+
FileUtils.rm(@results)
|
143
110
|
end
|
144
111
|
|
145
112
|
def run_test(s)
|
146
|
-
|
147
113
|
bar = s[1]
|
148
|
-
|
114
|
+
s[2] = ": #{'running'.green}"
|
115
|
+
bar.advance(status: s[2])
|
149
116
|
|
150
117
|
if @running_tests.count.positive?
|
151
|
-
|
152
|
-
|
153
|
-
prev_bar.
|
154
|
-
|
118
|
+
@running_tests.each do |b|
|
119
|
+
prev_bar = b[1]
|
120
|
+
if prev_bar.complete?
|
121
|
+
prev_bar.reset
|
122
|
+
prev_bar.advance(status: b[2])
|
123
|
+
prev_bar.finish
|
124
|
+
else
|
125
|
+
prev_bar.update(head: ' ', unfinished: ' ')
|
126
|
+
prev_bar.advance(status: b[2])
|
127
|
+
end
|
155
128
|
end
|
156
129
|
end
|
157
130
|
|
158
131
|
@running_tests.push(s)
|
159
|
-
|
160
|
-
|
161
|
-
out, _err, status = Open3.capture3(ENV, 'rake', "test:#{s[0]}", stdin_data: nil)
|
132
|
+
out, _err, status = Open3.capture3(ENV, "rake test:#{s[0]} | tee #{@results}", stdin_data: nil)
|
162
133
|
unless status.success?
|
163
134
|
m = out.match(/(?<fail>\d+) failures, (?<err>\d+) errors/)
|
164
|
-
|
135
|
+
s[2] = ": #{m['fail'].bold.red} #{'failures'.red}, #{m['err'].bold.red} #{'errors'.red}"
|
165
136
|
bar.update(head: '✖'.boldred)
|
166
|
-
bar.advance(head: '✖'.boldred, status:
|
137
|
+
bar.advance(head: '✖'.boldred, status: s[2])
|
167
138
|
|
168
139
|
# errs = out.scan(/(?:Failure|Error): [\w_]+\((?:.*?)\):(?:.*?)(?=\n=======)/m)
|
169
140
|
@error_out.push(out.highlight_errors)
|
@@ -175,7 +146,7 @@ class ThreadedTests
|
|
175
146
|
|
176
147
|
time = out.match(/^Finished in (?<time>\d+\.\d+) seconds\./)
|
177
148
|
count = out.match(/^(?<tests>\d+) tests, (?<assrt>\d+) assertions, (?<fails>\d+) failures, (?<errs>\d+) errors/)
|
178
|
-
|
149
|
+
s[2] = [
|
179
150
|
': ',
|
180
151
|
count['tests'].green,
|
181
152
|
'/',
|
@@ -190,7 +161,7 @@ class ThreadedTests
|
|
190
161
|
's'
|
191
162
|
].join('')
|
192
163
|
bar.update(head: '✔'.boldgreen)
|
193
|
-
bar.advance(head: '✔'.boldgreen, status:
|
164
|
+
bar.advance(head: '✔'.boldgreen, status: s[2])
|
194
165
|
@test_total += count['tests'].to_i
|
195
166
|
@assrt_total += count['assrt'].to_i
|
196
167
|
@elapsed += time['time'].to_f
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ThreadedTestString
|
2
|
+
class ::String
|
3
|
+
include Doing::Color
|
4
|
+
|
5
|
+
def highlight_errors
|
6
|
+
cols = `tput cols`.strip.to_i
|
7
|
+
|
8
|
+
string = dup
|
9
|
+
|
10
|
+
errs = string.scan(/(?<==\n)(?:Failure|Error):.*?(?=\n=+)/m)
|
11
|
+
|
12
|
+
errs.map! do |error|
|
13
|
+
err = error.dup
|
14
|
+
|
15
|
+
err.gsub!(%r{^(/.*?/)([^/:]+):(\d+):in (.*?)$}) do
|
16
|
+
m = Regexp.last_match
|
17
|
+
"#{m[1].white}#{m[2].bold.white}:#{m[3].yellow}:in #{m[4].cyan}"
|
18
|
+
end
|
19
|
+
err.gsub!(/(Failure|Error): (.*?)\((.*?)\):\n (.*?)(?=\n)/m) do
|
20
|
+
m = Regexp.last_match
|
21
|
+
[
|
22
|
+
m[1].bold.boldbgred.white,
|
23
|
+
m[3].bold.boldbgcyan.white,
|
24
|
+
m[2].bold.boldbgyellow.black,
|
25
|
+
" #{m[4]} ".bold.boldbgwhite.black.reset
|
26
|
+
].join(':'.boldblack.boldbgblack.reset)
|
27
|
+
end
|
28
|
+
err.gsub!(/(<.*?>) (was expected to) (.*?)\n( *<.*?>)./m) do
|
29
|
+
m = Regexp.last_match
|
30
|
+
"#{m[1].bold.green} #{m[2].white} #{m[3].boldwhite.boldbgred.reset}\n#{m[4].bold.white}"
|
31
|
+
end
|
32
|
+
err.gsub!(/(Finished in) ([\d.]+) (seconds)/) do
|
33
|
+
m = Regexp.last_match
|
34
|
+
"#{m[1].green} #{m[2].bold.white} #{m[3].green}"
|
35
|
+
end
|
36
|
+
err.gsub!(/(\d+) (failures)/) do
|
37
|
+
m = Regexp.last_match
|
38
|
+
"#{m[1].bold.red} #{m[2].red}"
|
39
|
+
end
|
40
|
+
err.gsub!(/100% passed/) do |m|
|
41
|
+
m.bold.green
|
42
|
+
end
|
43
|
+
|
44
|
+
err
|
45
|
+
end
|
46
|
+
|
47
|
+
errs.join("\n#{('=' * cols).blue}\n")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.25
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: safe_yaml
|
@@ -555,6 +555,7 @@ files:
|
|
555
555
|
- lib/doing/completion/zsh_completion.rb
|
556
556
|
- lib/doing/configuration.rb
|
557
557
|
- lib/doing/errors.rb
|
558
|
+
- lib/doing/good.rb
|
558
559
|
- lib/doing/hash.rb
|
559
560
|
- lib/doing/help_monkey_patch.rb
|
560
561
|
- lib/doing/hooks.rb
|
@@ -687,6 +688,7 @@ files:
|
|
687
688
|
- lib/helpers/fzf/test/test_go.rb
|
688
689
|
- lib/helpers/fzf/uninstall
|
689
690
|
- lib/helpers/threaded_tests.rb
|
691
|
+
- lib/helpers/threaded_tests_string.rb
|
690
692
|
- lib/templates/doing-dayone-entry.erb
|
691
693
|
- lib/templates/doing-dayone.erb
|
692
694
|
- lib/templates/doing-markdown.erb
|