tap 0.10.0 → 0.10.1

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.
@@ -1,88 +1,151 @@
1
+ require 'tap/root'
2
+
1
3
  module Tap
2
4
  module Support
3
- class Manifest
4
-
5
+ class Manifest
5
6
  class << self
6
- def glob_method(name)
7
- "manifest_glob_#{name}".to_sym
8
- end
9
-
10
- def map_method(name)
11
- "manifest_map_#{name}".to_sym
7
+ def normalize(key)
8
+ key.to_s.downcase.gsub(/\s/, "_").delete(":")
12
9
  end
13
10
  end
14
11
 
15
- DEFAULT_MAP_METHOD = :manifest_map
16
-
12
+ include Enumerable
13
+
14
+ # An array of (key, value) entries in self.
17
15
  attr_reader :entries
18
- attr_reader :map_method
19
- attr_reader :paths
20
- attr_reader :path_index
21
16
 
22
- def initialize(name, source)
23
- @entries = []
24
-
25
- @map_method = Manifest.map_method(name)
26
- @map_method = DEFAULT_MAP_METHOD if !source.respond_to?(@map_method)
27
-
28
- @paths = source.send(Manifest.glob_method(name)).uniq
29
- @path_index = 0
17
+ # An array of search_paths to identify entries.
18
+ attr_reader :search_paths
19
+
20
+ # The index of the search_path that will be searched
21
+ # next when building the manifest.
22
+ attr_reader :search_path_index
23
+
24
+ def initialize(search_paths)
25
+ self.search_paths = search_paths
26
+ end
27
+
28
+ # Returns an array of the entries keys.
29
+ def keys
30
+ entries.collect {|(key, value)| key }
30
31
  end
31
32
 
32
- def complete?
33
- @path_index == paths.length
33
+ # Returns an array of the entries values.
34
+ def values
35
+ entries.collect {|(key, value)| value }
34
36
  end
35
37
 
36
- def each_path
37
- return(false) if complete?
38
-
39
- n_to_skip = @path_index
40
- paths.each do |context, path|
41
- if n_to_skip > 0
42
- n_to_skip -= 1
43
- next
44
- end
45
-
46
- @path_index += 1
47
- yield(context, path)
48
- end
49
-
50
- true
38
+ # True if entries are empty.
39
+ def empty?
40
+ entries.empty?
51
41
  end
52
42
 
53
- # Checks that the manifest does not already assign key a conflicting path,
54
- # then adds the (key, path) pair to manifest.
55
- def store(entry)
56
- existing_key, existing_path = entries.find {|(key, path)| key == entry[0] }
57
-
58
- if existing_key && existing_path != entry[1]
59
- raise ManifestConflict, "multiple paths for key '#{existing_key}': ['#{existing_path}', '#{entry[1]}']"
60
- end
43
+ def search_paths=(search_paths)
44
+ @entries = []
45
+ @search_paths = search_paths
46
+ @search_path_index = 0
47
+ end
61
48
 
62
- entries << entry
49
+ # Clears entries and sets the search_path_index to zero.
50
+ def reset
51
+ @entries.clear
52
+ @search_path_index = 0
63
53
  end
64
54
 
65
- def keys
66
- entries.collect {|(key, value)| key }
55
+ # Builds the manifest, identifying all entries from search_paths.
56
+ # Returns self.
57
+ def build
58
+ each {|k, v|} unless built?
59
+ self
67
60
  end
68
61
 
69
- def values
70
- entries.collect {|(key, value)| value }
62
+ # True if all search paths have been checked for entries
63
+ # (ie search_path_index == search_paths.length).
64
+ def built?
65
+ @search_path_index == search_paths.length
66
+ end
67
+
68
+ # Abstract method which should return each (key, value) entry
69
+ # for a given search path. Raises a NotImplementedError
70
+ # if left not implemented.
71
+ def entries_for(search_path)
72
+ [[search_path, search_path]]
73
+ end
74
+
75
+ # Adds the (key, value) pair to entries and returns the new entry.
76
+ # Checks that entries does not already assign key a conflicting value;
77
+ # raises an error if this is the case, or returns the existing entry.
78
+ #
79
+ # Keys are normalized using Manifest.normalize before storing.
80
+ def store(key, value)
81
+ key = Manifest.normalize(key)
82
+ existing = entries.find {|(k, v)| key == k }
83
+
84
+ if existing
85
+ if existing[1] != value
86
+ raise ManifestConflict.new(key, value, existing[1])
87
+ else
88
+ return existing
89
+ end
90
+ end
91
+
92
+ new_entry = [key, value]
93
+ entries << new_entry
94
+ new_entry
71
95
  end
72
96
 
73
- def mini_map
74
- return [] if entries.empty?
97
+ # Iterates over each (key, value) entry in self, dynamically identifying entries
98
+ # from search_paths if necessary. New entries are identifed using the each_for
99
+ # method.
100
+ def each
101
+ entries.each do |key, path|
102
+ yield(key, path)
103
+ end
75
104
 
105
+ unless built?
106
+ n_to_skip = @search_path_index
107
+ search_paths.each do |search_path|
108
+ # advance to the current search path
109
+ if n_to_skip > 0
110
+ n_to_skip -= 1
111
+ next
112
+ end
113
+ @search_path_index += 1
114
+
115
+ # collect new entries and yield afterwards to ensure
116
+ # that all entries for the search_path get stored
117
+ new_entries = entries_for(*search_path)
118
+ next if new_entries == nil
119
+
120
+ new_entries.each {|(key, value)| store(key, value) }
121
+ new_entries.each {|(key, value)| yield(key, value) }
122
+ end
123
+ end
124
+ end
125
+
126
+ # Returns an array of (mini_key, value) pairs, matching
127
+ # entries by index.
128
+ def minimize
76
129
  hash = {}
77
- Root.minimize(keys) do |path, mini_path|
130
+ Tap::Root.minimize(keys) do |path, mini_path|
78
131
  hash[path] = mini_path
79
132
  end
80
133
 
81
134
  entries.collect {|path, value| [hash[path], value] }
82
135
  end
83
136
 
137
+ protected
138
+
84
139
  # Raised when multiple paths are assigned to the same manifest key.
85
140
  class ManifestConflict < StandardError
141
+ attr_reader :key, :value, :existing
142
+
143
+ def initialize(key, value, existing)
144
+ @key = key
145
+ @value = value
146
+ @existing = existing
147
+ super("attempted to store '%s': %s\nbut already was\n%s" % [key, value, existing])
148
+ end
86
149
  end
87
150
  end
88
151
  end
@@ -1,4 +1,5 @@
1
1
  require 'rake'
2
+ require 'tap/support/gems/rake'
2
3
 
3
4
  module Tap
4
5
  module Tasks
@@ -1,7 +1,7 @@
1
1
  require 'test/unit'
2
2
  require 'tap/test/file_methods'
3
3
  require 'tap/test/subset_methods'
4
- #require 'tap/support/shell_utils'
4
+ require 'tap/test/script_methods/script_test'
5
5
 
6
6
  module Test # :nodoc:
7
7
  module Unit # :nodoc:
@@ -25,36 +25,15 @@ end
25
25
 
26
26
  module Tap
27
27
  module Test
28
-
29
28
  module ScriptMethods
30
- class CommandTest
31
- attr_accessor :command_path
32
- attr_reader :commands
33
-
34
- def initialize
35
- @command_path = nil
36
- @commands = []
37
- end
38
-
39
- def check(argstr, msg=nil, expected=nil, &block)
40
- commands << ["#{command_path}#{argstr}", msg, expected, block]
41
- end
42
-
43
- def check_cmd(cmd, msg=nil, expected=nil, &block)
44
- commands << [cmd, msg, expected, block]
45
- end
46
- end
47
-
48
- include Tap::Support::ShellUtils
49
-
50
- def assert_output_equal(a, b, cmd, msg)
29
+
30
+ def assert_output_equal(a, b, msg)
51
31
  a = a[1..-1] if a[0] == ?\n
52
32
  if a == b
53
33
  assert true
54
34
  else
55
35
  flunk %Q{
56
36
  #{msg}
57
- % #{cmd}
58
37
  ==================== expected output ====================
59
38
  #{a.gsub(/\t/, "\\t").gsub(/\r\n/, "\\r\\n\n").gsub(/\n/, "\\n\n")}
60
39
  ======================== but was ========================
@@ -64,13 +43,12 @@ module Tap
64
43
  end
65
44
  end
66
45
 
67
- def assert_alike(a, b, cmd, msg)
46
+ def assert_alike(a, b, msg)
68
47
  if b =~ a
69
48
  assert true
70
49
  else
71
50
  flunk %Q{
72
51
  #{msg}
73
- % #{cmd}
74
52
  ================= expected output like ==================
75
53
  #{a}
76
54
  ======================== but was ========================
@@ -80,58 +58,43 @@ module Tap
80
58
  end
81
59
  end
82
60
 
83
- def script_test(test_dir=method_dir(:output))
61
+ def with_argv(argv=[])
62
+ current_argv = ARGV.dup
63
+ begin
64
+ ARGV.clear
65
+ ARGV.concat(argv)
66
+
67
+ yield
68
+
69
+ ensure
70
+ ARGV.clear
71
+ ARGV.concat(current_argv)
72
+ end
73
+ end
74
+
75
+ def default_command_path
76
+ nil
77
+ end
78
+
79
+ def script_test(test_dir=method_root)
84
80
  subset_test("SCRIPT", "s") do
85
- test = CommandTest.new
86
- yield(test)
81
+ cmd = ScriptTest.new(default_command_path)
82
+ yield(cmd)
87
83
 
88
- current_dir = Dir.pwd
89
- current_argv = ARGV.dup
90
- begin
91
- ARGV.clear
92
- make_test_directories
93
- Dir.chdir(test_dir)
94
-
95
- puts "\n# == #{method_name}"
96
-
97
- test.commands.each do |cmd, msg, expected, block|
98
- start = Time.now
99
- result = capture_sh(cmd) {|ok, status, tempfile_path| }
100
- elapsed = Time.now - start
84
+ Tap::Root.indir(test_dir, true) do
85
+ with_argv do
86
+ puts "\n# == #{method_name}"
101
87
 
102
- case expected
103
- when String
104
- assert_output_equal(expected, result, cmd, msg)
105
- when Regexp
106
- assert_alike(expected, result, cmd, msg)
107
- end
108
-
109
- if block
110
- block.call(result)
111
- end
112
-
113
- if env('stepwise') || (expected == nil && block == nil)
114
- print %Q{
115
- ------------------------------------
116
- %s
117
- > %s
118
- %s
119
- Time Elapsed: %.3fs} % [msg, cmd, result, elapsed]
120
-
121
- if env('stepwise')
122
- print "\nContinue? (y/n): "
123
- break if gets.strip =~ /^no?$/i
88
+ cmd.run(env('stepwise')) do |expected, result, msg|
89
+ case expected
90
+ when String
91
+ assert_output_equal(expected, result, msg)
92
+ when Regexp
93
+ assert_alike(expected, result, msg)
124
94
  end
125
- else
126
- puts "%.3fs : %s" % [elapsed, msg]
127
95
  end
128
96
  end
129
- ensure
130
- Dir.chdir(current_dir)
131
- ARGV.clear
132
- ARGV.concat(current_argv)
133
97
  end
134
-
135
98
  end
136
99
  end
137
100
 
@@ -0,0 +1,98 @@
1
+ require 'tap/support/shell_utils'
2
+
3
+ module Tap
4
+ module Test
5
+ module ScriptMethods
6
+ class ScriptTest
7
+ include Tap::Support::ShellUtils
8
+
9
+ # The command path for self, returned by to_s
10
+ attr_accessor :command_path
11
+
12
+ # An array of (command, message, expected, validation)
13
+ # entries, representing the accumulated test commands.
14
+ attr_reader :commands
15
+
16
+ def initialize(command_path=nil)
17
+ @command_path = command_path
18
+ @commands = []
19
+ end
20
+
21
+ # Splits the input string, collecting single-line commands
22
+ # and expected results. Nil will be used as the expected
23
+ # result if the result is whitespace, or not present.
24
+ #
25
+ # cmd = ScriptTest.new
26
+ # cmd.split %Q{
27
+ # % command one
28
+ # expected text for command one
29
+ # % command two
30
+ # % command three
31
+ # expected text for command three
32
+ # }
33
+ # # => [
34
+ # # ["command one", "expected text for command one\n"],
35
+ # # ["command two", nil],
36
+ # # ["command three", "expected text for command three\n"]]
37
+ #
38
+ def split(str)
39
+ str.split(/^%\s*/).collect do |s|
40
+ next(nil) if s.strip.empty?
41
+ command, expected = s.split(/\n/, 2)
42
+ expected = nil if expected && expected.strip.empty?
43
+ [command.strip, expected]
44
+ end.compact
45
+ end
46
+
47
+ def time(msg, command)
48
+ commands << [command, msg, nil, nil]
49
+ end
50
+
51
+ def check(msg, command, &validation)
52
+ new_commands = split(command)
53
+ new_commands.each_with_index do |(cmd, expected), i|
54
+ commands << [cmd, (new_commands.length > 1 ? "#{msg} (#{i})" : msg), expected, validation]
55
+ end
56
+ end
57
+
58
+ def match(msg, command, regexp=nil, &validation)
59
+ new_commands = split(command)
60
+ new_commands.each_with_index do |(cmd, expected), i|
61
+ raise "expected text specified in match command" unless expected == nil
62
+ commands << [cmd, (new_commands.length > 1 ? "#{msg} (#{i})" : msg), regexp, validation]
63
+ end
64
+ end
65
+
66
+ def run(stepwise=false)
67
+ commands.each do |cmd, msg, expected, validation|
68
+ start = Time.now
69
+ result = capture_sh(cmd) {|ok, status, tempfile_path| }
70
+ elapsed = Time.now - start
71
+
72
+ yield(expected, result, %Q{#{msg}\n% #{cmd}}) if expected
73
+ validation.call(result) if validation
74
+
75
+ if stepwise
76
+ print %Q{
77
+ ------------------------------------
78
+ %s
79
+ > %s
80
+ %s
81
+ Time Elapsed: %.3fs} % [msg, cmd, result, elapsed]
82
+
83
+ print "\nContinue? (y/n): "
84
+ break if gets.strip =~ /^no?$/i
85
+ else
86
+ puts "%.3fs : %s" % [elapsed, msg]
87
+ end
88
+ end
89
+ end
90
+
91
+ # Returns the command path.
92
+ def to_s
93
+ command_path
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end