tap 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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