doing 2.1.24 → 2.1.25
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/.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
|