pairing with luke, nagios_command provider skeleton
[vinpup.git] / test / util / fileparsing.rb
blobadccfab65561037d2744cd305640c1d8f237abf9
1 #!/usr/bin/env ruby
3 require File.dirname(__FILE__) + '/../lib/puppettest'
5 require 'puppettest'
6 require 'puppettest/fileparsing'
7 require 'puppet'
8 require 'puppet/util/fileparsing'
10 class TestUtilFileParsing < Test::Unit::TestCase
11         include PuppetTest
12         include PuppetTest::FileParsing
14     class FParser
15         include Puppet::Util::FileParsing
16     end
18     def setup
19         super
20         @parser = FParser.new
21     end
23     def test_lines
24         assert_equal("\n", @parser.line_separator,
25             "Default separator was incorrect")
27         {"\n" => ["one two\nthree four", "one two\nthree four\n"],
28          "\t" => ["one two\tthree four", "one two\tthree four\t"],
29         }.each do |sep, tests|
30             assert_nothing_raised do
31                 @parser.line_separator = sep
32             end
33             assert_equal(sep, @parser.line_separator,
34                 "Did not set separator")
36             tests.each do |test|
37                 assert_equal(["one two", "three four"], @parser.lines(test),
38                     "Incorrectly parsed %s" % test.inspect)
39             end
40         end
41     end
43     # Make sure parse calls the appropriate methods or errors out
44     def test_parse
45         @parser.meta_def(:parse_line) do |line|
46             line.split(/\s+/)
47         end
49         text = "one line\ntwo line"
50         should = [%w{one line}, %w{two line}]
51         ret = nil
52         assert_nothing_raised do
53             ret = @parser.parse(text)
54         end
56         assert_equal(should, ret)
57     end
59     # Make sure we correctly handle different kinds of text lines.
60     def test_text_line
61         comment = "# this is a comment"
63         # Make sure it fails if no regex is passed
64         assert_raise(ArgumentError) do
65             @parser.text_line :comment
66         end
68         # define a text matching comment record
69         assert_nothing_raised do
70             @parser.text_line :comment, :match => /^#/
71         end
73         # Make sure it matches
74         assert_nothing_raised do
75             assert_equal({:record_type => :comment, :line => comment}, 
76                  @parser.parse_line(comment))
77         end
79         # But not something else
80         assert_nothing_raised do
81             assert_nil(@parser.parse_line("some other text"))
82         end
84         # Now define another type and make sure we get the right one back
85         assert_nothing_raised do
86             @parser.text_line :blank, :match => /^\s*$/
87         end
89         # The comment should still match
90         assert_nothing_raised do
91             assert_equal({:record_type => :comment, :line => comment}, 
92                  @parser.parse_line(comment))
93         end
95         # As should our new line type
96         assert_nothing_raised do
97             assert_equal({:record_type => :blank, :line => ""}, 
98                  @parser.parse_line(""))
99         end
101     end
103     def test_parse_line
104         Puppet[:trace] = false
106         comment = "# this is a comment"
108         # Make sure it fails if we don't have any record types defined
109         assert_raise(Puppet::DevError) do
110             @parser.parse_line(comment)
111         end
113         # Now define a text matching comment record
114         assert_nothing_raised do
115             @parser.text_line :comment, :match => /^#/
116         end
118         # And make sure we can't define another one with the same name
119         assert_raise(ArgumentError) do
120             @parser.text_line :comment, :match => /^"/
121         end
123         result = nil
124         assert_nothing_raised("Did not parse text line") do
125             result = @parser.parse_line comment
126         end
128         assert_equal({:record_type => :comment, :line => comment}, result)
130         # Make sure we just return nil on unmatched lines.
131         assert_nothing_raised("Did not parse text line") do
132             result = @parser.parse_line "No match for this"
133         end
135         assert_nil(result, "Somehow matched an empty line")
137         # Now define another type of comment, and make sure both types get
138         # correctly returned as comments
139         assert_nothing_raised do
140             @parser.text_line :comment2, :match => /^"/
141         end
143         assert_nothing_raised("Did not parse old comment") do
144             assert_equal({:record_type => :comment, :line => comment}, 
145                  @parser.parse_line(comment))
146         end
147         comment = '" another type of comment'
148         assert_nothing_raised("Did not parse new comment") do
149             assert_equal({:record_type => :comment2, :line => comment}, 
150                  @parser.parse_line(comment))
151         end
153         # Now define two overlapping record types and make sure we keep the
154         # correct order.  We do first match, not longest match.
155         assert_nothing_raised do
156             @parser.text_line :one, :match => /^y/
157             @parser.text_line :two, :match => /^yay/
158         end
160         assert_nothing_raised do
161             assert_equal({:record_type => :one, :line => "yayness"},
162                 @parser.parse_line("yayness"))
163         end
165     end
167     def test_record_line
168         tabrecord = "tab        separated       content"
169         spacerecord = "space separated content"
171         # Make sure we always require an appropriate set of options
172         [{:separator => "\t"}, {}, {:fields => %w{record_type}}].each do |opts|
173             assert_raise(ArgumentError, "Accepted %s" % opts.inspect) do
174                 @parser.record_line :record, opts
175             end
176         end
178         # Verify that our default separator is tabs
179         tabs = nil
180         assert_nothing_raised do
181             tabs = @parser.record_line :tabs, :fields => [:name, :first, :second]
182         end
184         # Make sure out tab line gets matched
185         tabshould = {:record_type => :tabs, :name => "tab", :first => "separated",
186                             :second => "content"}
187         assert_nothing_raised do
188             assert_equal(tabshould, @parser.handle_record_line(tabrecord, tabs))
189         end
191         # Now add our space-separated record type
192         spaces = nil
193         assert_nothing_raised do
194             spaces = @parser.record_line :spaces, :fields => [:name, :first, :second]
195         end
197         # Now make sure both lines parse correctly
198         spaceshould = {:record_type => :spaces, :name => "space",
199             :first => "separated", :second => "content"}
201         assert_nothing_raised do
202             assert_equal(tabshould, @parser.handle_record_line(tabrecord, tabs))
203             assert_equal(spaceshould, @parser.handle_record_line(spacerecord, spaces))
204         end
205     end
207     def test_to_line
208         @parser.text_line :comment, :match => /^#/
209         @parser.text_line :blank, :match => /^\s*$/
210         @parser.record_line :record, :fields => %w{name one two}, :joiner => "\t"
212         johnny = {:record_type => :record, :name => "johnny", :one => "home",
213             :two => "yay"}
214         bill = {:record_type => :record, :name => "bill", :one => "work",
215             :two => "boo"}
217         records = {
218             :comment => {:record_type => :comment, :line => "# This is a file"},
219             :blank => {:record_type => :blank, :line => ""},
220             :johnny => johnny,
221             :bill => bill
222         }
224         lines = {
225             :comment => "# This is a file",
226             :blank => "",
227             :johnny => "johnny  home    yay",
228             :bill => "bill      work    boo"
229         }
231         records.each do |name, details|
232             result = nil
233             assert_nothing_raised do
234                 result = @parser.to_line(details)
235             end
237             assert_equal(lines[name], result)
238         end
239         order = [:comment, :blank, :johnny, :bill]
241         file = order.collect { |name| lines[name] }.join("\n")
243         ordered_records = order.collect { |name| records[name] }
245         # Make sure we default to a trailing separator
246         assert_equal(true, @parser.trailing_separator,
247             "Did not default to a trailing separtor")
249         # Start without a trailing separator
250         @parser.trailing_separator = false
251         assert_nothing_raised do
252             assert_equal(file, @parser.to_file(ordered_records))
253         end
255         # Now with a trailing separator
256         file += "\n"
257         @parser.trailing_separator = true
258         assert_nothing_raised do
259             assert_equal(file, @parser.to_file(ordered_records))
260         end
262         # Now try it with a different separator, so we're not just catching
263         # defaults
264         file.gsub!("\n", "\t")
265         @parser.line_separator = "\t"
266         assert_nothing_raised do
267             assert_equal(file, @parser.to_file(ordered_records))
268         end
269     end
271     # Make sure fields that are marked absent get replaced with the appropriate
272     # string.
273     def test_absent_fields
274         record = nil
275         assert_nothing_raised do
276             record = @parser.record_line :record, :fields => %w{one two three},
277                 :optional => %w{two three}
278         end
279         assert_equal("", record.absent, "Did not set a default absent string")
281         result = nil
282         assert_nothing_raised do
283             result = @parser.to_line(:record_type => :record,
284                 :one => "a", :two => :absent, :three => "b")
285         end
287         assert_equal("a  b", result, "Absent was not correctly replaced")
289         # Now try using a different replacement character
290         record.absent = "*" # Because cron is a pain in my ass
291         assert_nothing_raised do
292             result = @parser.to_line(:record_type => :record,
293                 :one => "a", :two => :absent, :three => "b")
294         end
296         assert_equal("a * b", result, "Absent was not correctly replaced")
298         # Make sure we deal correctly with the string 'absent'
299         assert_nothing_raised do
300             result = @parser.to_line(:record_type => :record,
301                 :one => "a", :two => "b", :three => 'absent')
302         end
304         assert_equal("a b absent", result, "Replaced string 'absent'")
306         # And, of course, make sure we can swap things around.
307         assert_nothing_raised do
308             result = @parser.to_line(:record_type => :record,
309                 :one => "a", :two => "b", :three => :absent)
310         end
312         assert_equal("a b *", result, "Absent was not correctly replaced")
313     end
315     # Make sure we can specify a different join character than split character
316     def test_split_join_record_line
317         check = proc do |start, record, final|
318             # Check parsing first
319             result = @parser.parse_line(start)
320             [:one, :two].each do |param|
321                 assert_equal(record[param], result[param], 
322                     "Did not correctly parse %s" % start.inspect)
323             end
325             # And generating
326             assert_equal(final, @parser.to_line(result),
327                 "Did not correctly generate %s from %s" %
328                 [final.inspect, record.inspect])
329         end
331         # First try it with symmetric characters
332         @parser.record_line :symmetric, :fields => %w{one two},
333             :separator => " "
335         check.call "a b", {:one => "a", :two => "b"}, "a b"
336         @parser.clear_records
338         # Now assymetric but both strings
339         @parser.record_line :asymmetric, :fields => %w{one two},
340             :separator => "\t", :joiner => " "
342         check.call "a\tb", {:one => "a", :two => "b"}, "a b"
343         @parser.clear_records
345         # And assymmetric with a regex
346         @parser.record_line :asymmetric2, :fields => %w{one two},
347             :separator => /\s+/, :joiner => " "
349         check.call "a\tb", {:one => "a", :two => "b"}, "a b"
350         check.call "a b", {:one => "a", :two => "b"}, "a b"
351     end
353     # Make sure we correctly regenerate files.
354     def test_to_file
355         @parser.text_line :comment, :match => /^#/
356         @parser.text_line :blank, :match => /^\s*$/
357         @parser.record_line :record, :fields => %w{name one two}
359         text = "# This is a comment
361 johnny one two
362 billy three four\n"
364         # Just parse and generate, to make sure it's isomorphic.
365         assert_nothing_raised do
366             assert_equal(text, @parser.to_file(@parser.parse(text)),
367                 "parsing was not isomorphic")
368         end
369     end
371     def test_valid_attrs
372         @parser.record_line :record, :fields => %w{one two three}
374         assert(@parser.valid_attr?(:record, :one),
375             "one was considered invalid")
377         assert(@parser.valid_attr?(:record, :ensure),
378             "ensure was considered invalid")
380         assert(! @parser.valid_attr?(:record, :four),
381             "four was considered valid")
382     end
384     def test_record_blocks
385         options = nil
386         assert_nothing_raised do
387             # Just do a simple test
388             options = @parser.record_line :record,
389                 :fields => %w{name alias info} do |line|
390                 line = line.dup
391                 ret = {}
392                 if line.sub!(/(\w+)\s*/, '')
393                     ret[:name] = $1
394                 else
395                     return nil
396                 end
398                 if line.sub!(/(#.+)/, '')
399                     desc = $1.sub(/^#\s*/, '')
400                     ret[:description] = desc unless desc == ""
401                 end
403                 if line != ""
404                     ret[:alias] = line.split(/\s+/)
405                 end
407                 return ret
408             end
409         end
411         values = {
412             :name => "tcpmux",
413             :description => "TCP port service multiplexer",
414             :alias => ["sink"]
415         }
417         {
419             "tcpmux      " => [:name],
420             "tcpmux" => [:name],
421             "tcpmux      sink" => [:name, :port, :protocols, :alias],
422             "tcpmux      # TCP port service multiplexer" =>
423                 [:name, :description, :port, :protocols],
424             "tcpmux      sink         # TCP port service multiplexer" =>
425                 [:name, :description, :port, :alias, :protocols],
426             "tcpmux      sink null    # TCP port service multiplexer" =>
427                 [:name, :description, :port, :alias, :protocols],
428         }.each do |line, should|
429             result = nil
430             assert_nothing_raised do
431                 result = @parser.handle_record_line(line, options)
432             end
433             assert(result, "Did not get a result back for '%s'" % line)
434             should.each do |field|
435                 if field == :alias and line =~ /null/
436                     assert_equal(%w{sink null}, result[field],
437                         "Field %s was not right in '%s'" % [field, line])
438                 else
439                     assert_equal(values[field], result[field],
440                         "Field %s was not right in '%s'" % [field, line])
441                 end
442             end
443         end
446     end
448     # Make sure we correctly handle optional fields.  We'll skip this
449     # functionality until we really know we need it.
450     def test_optional_fields
451         assert_nothing_raised do
452             @parser.record_line :record,
453                 :fields => %w{one two three four},
454                 :optional => %w{three four},
455                 :absent => "*",
456                 :separator => " " # A single space
457         end
459         { "a b c d" => [],
460           "a b * d" => [:three],
461           "a b * *" => [:three, :four],
462           "a b c *" => [:four]
463         }.each do |line, absentees|
464             record = nil
465             assert_nothing_raised do
466                 record = @parser.parse_line(line)
467             end
469             # Absent field is :absent, not "*" inside the record
470             absentees.each do |absentee|
471                 assert_equal(:absent, record[absentee])
472             end
474             # Now regenerate the line
475             newline = nil
476             assert_nothing_raised do
477                 newline = @parser.to_line(record)
478             end
480             # And make sure they're equal
481             assert_equal(line, newline)
482         end
484         # Now make sure it pukes if we don't provide the required fields
485         assert_raise(ArgumentError) do
486             @parser.to_line(:record_type => :record, :one => "yay")
487         end
488     end
490     def test_record_rts
491         # Start with the default
492         assert_nothing_raised do
493             @parser.record_line :record,
494                 :fields => %w{one two three four},
495                 :optional => %w{three four}
496         end
498         assert_equal("a b  ",
499             @parser.to_line(:record_type => :record, :one => "a", :two => "b")
500         )
502         # Now say yes to removing
503         @parser.clear_records
504         assert_nothing_raised do
505             @parser.record_line :record,
506                 :fields => %w{one two three four},
507                 :optional => %w{three four},
508                 :rts => true
509         end
511         assert_equal("a b",
512             @parser.to_line(:record_type => :record, :one => "a", :two => "b")
513         )
515         # Lastly, try a regex
516         @parser.clear_records
517         assert_nothing_raised do
518             @parser.record_line :record,
519                 :fields => %w{one two three four},
520                 :optional => %w{three four},
521                 :absent => "*",
522                 :rts => /[ *]+$/
523         end
525         assert_equal("a b",
526             @parser.to_line(:record_type => :record, :one => "a", :two => "b")
527         )
528     end
530     # Make sure the last field can contain the separator, as crontabs do, and
531     # that we roll them all up by default.
532     def test_field_rollups
533         @parser.record_line :yes, :fields => %w{name one two}
535         result = nil
536         assert_nothing_raised do
537             result = @parser.send(:parse_line, "Name One Two Three")
538         end
539         assert_equal("Two Three", result[:two],
540             "Did not roll up last fields by default")
542         @parser = FParser.new
543         assert_nothing_raised("Could not create record that rolls up fields") do
544             @parser.record_line :no, :fields => %w{name one two}, :rollup => false
545         end
547         result = nil
548         assert_nothing_raised do
549             result = @parser.send(:parse_line, "Name One Two Three")
550         end
551         assert_equal("Two", result[:two],
552             "Rolled up last fields when rollup => false")
553     end
555     def test_text_blocks
556         record = nil
557         assert_nothing_raised do
558             record = @parser.text_line :name, :match => %r{^#} do |line|
559                 {:line => line.upcase}
560             end
561         end
563         assert(record.respond_to?(:process),
564             "Block was not used with text line")
566         assert_equal("YAYNESS", record.process("yayness")[:line],
567             "Did not call process method")
568     end
570     def test_hooks
571         record = nil
572         # First try it with a normal record
573         assert_nothing_raised("Could not set hooks") do
574             record = @parser.record_line :yay, :fields => %w{one two},
575                 :post_parse => proc { |hash| hash[:posted] = true },
576                 :pre_gen => proc { |hash| hash[:one] = hash[:one].upcase },
577                 :to_line => proc { |hash| "# Line\n" + join(hash) }
578         end
580         assert(record.respond_to?(:post_parse), "did not create method for post-hook")
581         assert(record.respond_to?(:pre_gen), "did not create method for pre-hook")
583         result = nil
584         assert_nothing_raised("Could not process line with hooks") do
585             result = @parser.parse_line("one two")
586         end
588         assert(result[:posted], "Did not run post-hook")
589         old_result = result
591         # Now make sure our pre-gen hook is called
592         assert_nothing_raised("Could not generate line with hooks") do
593             result = @parser.to_line(result)
594         end
595         assert_equal("# Line\nONE two", result, "did not call pre-gen hook")
596         assert_equal("one", old_result[:one], "passed original hash to pre hook")
597     end
600 class TestUtilFileRecord < Test::Unit::TestCase
601         include PuppetTest
602         include PuppetTest::FileParsing
604     Record = Puppet::Util::FileParsing::FileRecord
605     def test_new_filerecord
606         [   [:fake, {}],
607             [nil, ]
608         ].each do |args|
609             assert_raise(ArgumentError, "Did not fail on %s" % args.inspect) do
610                 Record.new(*args)
611             end
612         end
614         # Make sure the fields get turned into symbols
615         record = nil
616         assert_nothing_raised do
617             record = Record.new(:record, :fields => %w{one two})
618         end
619         assert_equal([:one, :two], record.fields,
620             "Did not symbolize fields")
622         # Make sure we fail on invalid fields
623         [:record_type, :target, :on_disk].each do |field|
624             assert_raise(ArgumentError, "Did not fail on invalid field %s" % field) {
625                 Record.new(:record, :fields => [field])
626             }
627         end
628     end
630     def test_defaults
631         record = Record.new(:text, :match => %r{^#})
632         [:absent, :separator, :joiner, :optional].each do |field|
633             assert_nil(record.send(field), "%s was not nil" % field)
634         end
636         record = Record.new(:record, :fields => %w{fields})
637         {:absent => "", :separator => /\s+/, :joiner => " ",
638             :optional => []}.each do |field, default|
639                 assert_equal(default, record.send(field), "%s was not default" % field)
640         end
641     end
643     def test_block
644         record = nil
645         assert_nothing_raised("Could not pass a block when creating record") do
646             record = Record.new(:record, :fields => %w{one}) do |line|
647                 return line.upcase
648             end
649         end
651         line = "This is a line"
653         assert(record.respond_to?(:process),
654             "Record did not define :process method")
656         assert_equal(line.upcase, record.process(line),
657             "Record did not process line correctly")
658     end
660     # Make sure we can declare that we want the block to be instance-eval'ed instead of
661     # defining the 'process' method.
662     def test_instance_block
663         record = nil
664         assert_nothing_raised("Could not pass a block when creating record") do
665             record = Record.new(:record, :block_eval => :instance, :fields => %w{one}) do
666                 def process(line)
667                     line.upcase
668                 end
670                 def to_line(details)
671                     details.upcase
672                 end
673             end
674         end
676         assert(record.respond_to?(:process), "Block was not instance-eval'ed and process was not defined")
677         assert(record.respond_to?(:to_line), "Block was not instance-eval'ed and to_line was not defined")
679         line = "some text"
680         assert_equal(line.upcase, record.process(line), "Instance-eval'ed record did not call :process correctly")
681         assert_equal(line.upcase, record.to_line(line), "Instance-eval'ed record did not call :to_line correctly")
682     end