diff --git a/.gitignore b/.gitignore index a859a2e..99e7d84 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ tags ## BUNDLER .bundle Gemfile.lock + +## YARD +.yardoc diff --git a/Gemfile b/Gemfile index 392ea17..5fcd95e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,11 +3,13 @@ source 'https://rubygems.org' gem 'yard' gem 'puppet', '~> 3.6.2' gem 'rgen' +gem 'redcarpet' group :test do gem 'rspec' gem 'mocha' gem 'puppetlabs_spec_helper' + gem 'rspec-html-matchers' end group :development do diff --git a/spec/lib/strings_spec/parsing.rb b/spec/lib/strings_spec/parsing.rb new file mode 100644 index 0000000..2570e28 --- /dev/null +++ b/spec/lib/strings_spec/parsing.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +module StringsSpec + module Parsing + + def parse(string, parser = :ruby) + Registry.clear + YARD::Parser::SourceParser.parse_string(string, parser) + end + + RSpec::Matchers.define :document_a do |arguments| + match do |actual| + compare_values(actual).empty? + end + + failure_message do |actual| + mismatches = compare_values(actual) + mismatches.collect do |key, value| + "Expected #{key} to be <#{value[1]}>, but got <#{value[0]}>." + end.join("\n") + end + + def compare_values(actual) + mismatched_arguments = {} + expected.each do |key, value| + actual_value = actual.send(key) + if actual_value != value + mismatched_arguments[key] = [actual_value, value] + end + end + mismatched_arguments + end + end + end +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 311484f..7e24a2f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,10 +13,3 @@ RSpec.configure do |config| config.mock_with :mocha end -# Borrowed from YARD spec helper -def parse_file(file, thisfile = __FILE__, log_level = log.level, ext = '.pp') - Registry.clear - path = File.join(File.dirname(thisfile), 'examples', file.to_s + ext) - YARD::Parser::SourceParser.parse(path, [], log_level) -end - diff --git a/spec/unit/puppet/examples/test/lib/puppet/functions/4x_function.rb b/spec/unit/puppet/examples/test/lib/puppet/functions/4x_function.rb new file mode 100644 index 0000000..5825496 --- /dev/null +++ b/spec/unit/puppet/examples/test/lib/puppet/functions/4x_function.rb @@ -0,0 +1,5 @@ +# function 4x +# +# This is a function which is used to test puppet strings +Puppet::Functions.create_function(:function4x) do +end diff --git a/spec/unit/puppet/examples/test/lib/puppet/parser/functions/function3x.rb b/spec/unit/puppet/examples/test/lib/puppet/parser/functions/function3x.rb new file mode 100644 index 0000000..e3083ea --- /dev/null +++ b/spec/unit/puppet/examples/test/lib/puppet/parser/functions/function3x.rb @@ -0,0 +1,3 @@ +Puppet::Parser::Functions.newfunction(:function3x, :doc => "This is the +function documentation for `function3x`") do |args| +end diff --git a/spec/unit/puppet/examples/test/manifests/init.pp b/spec/unit/puppet/examples/test/manifests/init.pp index 487cf30..8ebd451 100644 --- a/spec/unit/puppet/examples/test/manifests/init.pp +++ b/spec/unit/puppet/examples/test/manifests/init.pp @@ -1,6 +1,16 @@ +# Class: test +# +# This class exists to serve as fixture data for testing the puppet strings face +# +# @example +# class { "test": } +# +# @param package_name The name of the package +# @param service_name The name of the service class test ( $package_name = $test::params::package_name, $service_name = $test::params::service_name, + ) inherits test::params { # validate parameters here @@ -9,4 +19,9 @@ class test ( class { 'test::config': } ~> class { 'test::service': } -> Class['test'] + + File { + owner => 'user', + path => 'some/file/path', + } } diff --git a/spec/unit/puppet/face_spec.rb b/spec/unit/puppet/face_spec.rb index b60b6ef..3eaf42a 100644 --- a/spec/unit/puppet/face_spec.rb +++ b/spec/unit/puppet/face_spec.rb @@ -1,5 +1,8 @@ require 'spec_helper' require 'puppet/face/yardoc' +require 'rspec-html-matchers' +require 'tmpdir' +require 'stringio' describe Puppet::Face do @@ -27,6 +30,41 @@ describe Puppet::Face do YARD::CLI::Yardoc.expects(:run).with('--debug', 'some_file.rb') Puppet::Face[:yardoc, :current].yardoc('--debug', 'some_file.rb') end + + describe "when generating HTML for documentation" do + it "should properly generate HTML for manifest comments" do + + YARD::Logger.instance.io = StringIO.new + + using_module('test') do |tmp| + Dir.chdir('test') + + Puppet::Face[:yardoc, :current].yardoc + + expect(read_html(tmp, 'test', 'test.html')).to have_tag('.docstring .discussion', :text => /This class/) + end + end + + it "should properly generate HTML for 3x function comments" do + using_module('test') do |tmp| + Dir.chdir('test') + + Puppet::Face[:yardoc, :current].yardoc + + expect(read_html(tmp, 'test', 'ParserFunctions.html')).to have_tag('.docstring .discussion', :text => /documentation for `function3x`/) + end + end + + it "should properly generate HTML for 4x function comments" do + using_module('test') do |tmp| + Dir.chdir('test') + + Puppet::Face[:yardoc, :current].yardoc + + expect(read_html(tmp, 'test', 'test.html')).to have_tag('.docstring .discussion', :text => /This class/) + end + end + end end describe "modules action" do @@ -60,4 +98,23 @@ describe Puppet::Face do expect{Puppet::Face[:yardoc, :current].server}.to raise_error(RuntimeError, "This face requires Ruby 1.9 or greater.") end end + + # Helper methods to handle file operations around generating and loading HTML + def using_module(modulename, &block) + Dir.mktmpdir do |tmp| + module_location = File.join(File.dirname(__FILE__), "examples", modulename) + FileUtils.cp_r(module_location, tmp) + old_dir = Dir.pwd + begin + Dir.chdir(tmp) + yield(tmp) + ensure + Dir.chdir(old_dir) + end + end + end + + def read_html(dir, modulename, file) + File.read(File.join(dir, modulename, 'doc', file)) + end end diff --git a/spec/unit/puppetx/yardoc/yard/defined_type_handler_spec.rb b/spec/unit/puppetx/yardoc/yard/defined_type_handler_spec.rb new file mode 100644 index 0000000..7d347f2 --- /dev/null +++ b/spec/unit/puppetx/yardoc/yard/defined_type_handler_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' +require 'puppetx/yardoc/yard/handlers/defined_type_handler' +require 'strings_spec/parsing' + + +describe "DefinedTypeHanlder" do + include StringsSpec::Parsing + + def the_definedtype() + Registry.at("foo::bar") + end + + it "should parse single-line documentation strings before a given defined type" do + comment = "Definition: foo::bar" + puppet_code = <<-PUPPET + # #{comment} + define foo::bar ($baz) { } + PUPPET + + parse(puppet_code, :puppet) + + expect(the_definedtype).to document_a(:type => :definedtype, :docstring => comment) + end + + it "should parse multi-line documentation strings before a given defined type" do + puppet_code = <<-PUPPET + # Definition: foo::bar + # + # This class does some stuff + define foo::bar ($baz) { } + PUPPET + + parse(puppet_code, :puppet) + + comment = "Definition: foo::bar\nThis class does some stuff" + expect(the_definedtype).to document_a(:type => :definedtype, :docstring => comment) + end + + it "should not parse documentation before a function if it is followed by a new line" do + puppet_code = <<-PUPPET + # Definition: foo::bar + + define foo::bar ($baz) { } + PUPPET + + parse(puppet_code, :puppet) + + expect(the_definedtype).to document_a(:type => :definedtype, :docstring => "") + end + + it "should not add anything to the Registry if incorrect puppet code is present" do + puppet_code = <<-PUPPET + # Definition: foo::bar + This is not puppet code + PUPPET + + parse(puppet_code, :puppet) + + expect(Registry.all).to be_empty + end +end diff --git a/spec/unit/puppetx/yardoc/yard/examples/class.pp b/spec/unit/puppetx/yardoc/yard/examples/class.pp deleted file mode 100644 index 512b39f..0000000 --- a/spec/unit/puppetx/yardoc/yard/examples/class.pp +++ /dev/null @@ -1,5 +0,0 @@ -class foo::bar { - file { '/test/file/path': - owner => 'baz', - } -} diff --git a/spec/unit/puppetx/yardoc/yard/examples/defined_type.pp b/spec/unit/puppetx/yardoc/yard/examples/defined_type.pp deleted file mode 100644 index 6021a41..0000000 --- a/spec/unit/puppetx/yardoc/yard/examples/defined_type.pp +++ /dev/null @@ -1,7 +0,0 @@ -define wibbly::wobbly ($wimey) { - Notify ($wimey) -} - -wibbly::wobbly{ - 'timey': wimey => stuff - } diff --git a/spec/unit/puppetx/yardoc/yard/examples/puppet3_function.rb b/spec/unit/puppetx/yardoc/yard/examples/puppet3_function.rb deleted file mode 100644 index a9e352f..0000000 --- a/spec/unit/puppetx/yardoc/yard/examples/puppet3_function.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'puppet' - -module Puppet::Parser::Functions - newfunction(:puppet3_function, :type => rvalue) do |args| - puts 'Hello World!' - end -end diff --git a/spec/unit/puppetx/yardoc/yard/examples/puppet4_function.rb b/spec/unit/puppetx/yardoc/yard/examples/puppet4_function.rb deleted file mode 100644 index fc65f89..0000000 --- a/spec/unit/puppetx/yardoc/yard/examples/puppet4_function.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'puppet' - -Puppet::Functions.create_function(:puppet4_function) do - def puppet4_function(x,y) - x >= y ? x : y - end -end diff --git a/spec/unit/puppetx/yardoc/yard/future_parser_function_handler_spec.rb b/spec/unit/puppetx/yardoc/yard/future_parser_function_handler_spec.rb new file mode 100644 index 0000000..c62bee7 --- /dev/null +++ b/spec/unit/puppetx/yardoc/yard/future_parser_function_handler_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' +require 'puppetx/yardoc/yard/handlers/future_parser_function_handler' +require 'strings_spec/parsing' + +describe "FutureParserDispatchHandler" do + include StringsSpec::Parsing + + def the_method() + Registry.at("FutureParserFunctions#the_function") + end + + def the_namespace() + Registry.at("FutureParserFunctions") + end + + it "should parse single-line documentation strings before a given function" do + comment = "The summary" + parse <<-RUBY + # #{comment} + Puppet::Functions.create_function(:the_function) do + end + RUBY + + expect(the_method).to document_a(:type => :method, :docstring => comment) + expect(the_namespace).to document_a(:type => :puppetnamespace) + end + + it "should parse multi-line documentation strings before a given function" do + parse <<-RUBY + # The summary + # + # The longer description + Puppet::Functions.create_function(:the_function) do + end + RUBY + + comment = "The summary\n\nThe longer description" + expect(the_method).to document_a(:type => :method, :docstring => comment) + expect(the_namespace).to document_a(:type => :puppetnamespace) + end + + it "should not parse documentation before a function if it is followed by two new lines" do + parse <<-RUBY + # The summary + # + # The longer description + + + Puppet::Functions.create_function(:the_function) do + end + RUBY + + expect(the_method).to document_a(:type => :method, :docstring => "") + expect(the_namespace).to document_a(:type => :puppetnamespace) + end + + it "should not add anything to the Registry if incorrect ruby code is present" do + parse <<-RUBY + # The summary + This is not ruby code + RUBY + + expect(Registry.all).to be_empty + end +end diff --git a/spec/unit/puppetx/yardoc/yard/handlers_spec.rb b/spec/unit/puppetx/yardoc/yard/handlers_spec.rb deleted file mode 100644 index adbbeb6..0000000 --- a/spec/unit/puppetx/yardoc/yard/handlers_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'spec_helper' -require 'puppetx/yardoc/yard/handlers' - -describe Puppetx::Yardoc::YARD::Handlers do - describe "DefinedTypeHanlder" do - it "should add a defined type object in the Registry" do - parse_file :defined_type, __FILE__, log.level, '.pp' - obj = Registry.at("wibbly::wobbly") - expect(obj.type).to be(:definedtype) - end - end - - describe "FutureParserDispatchHandler" do - before(:each) {parse_file :puppet4_function, __FILE__, log.level, '.rb'} - - it "should add a puppet namespace object to the Registry" do - namespace = Registry.at("FutureParserFunctions") - expect(namespace.type).to be(:puppetnamespace) - end - - it "should add a future parser function object to the Registry" do - function = Registry.at("FutureParserFunctions#puppet4_function") - expect(function.type).to be(:method) - end - - it "should add a method object to the Registry" do - method = Registry.at("#puppet4_function") - expect(method.type).to be(:method) - end - end - - describe "ParserFunctionHanlder" do - before(:each) {parse_file :puppet3_function, __FILE__, log.level, '.rb'} - - it "should add a module object to the Registry" do - puppet_module = Registry.at("Puppet::Parser::Functions") - expect(puppet_module.type).to be(:module) - end - - it "should add a puppet namespace object to the Registry" do - namespace = Registry.at("ParserFunctions") - expect(namespace.type).to be(:puppetnamespace) - end - - it "should add a method object to the Registry" do - method = Registry.at("ParserFunctions#puppet3_function") - expect(method.type).to be(:method) - end - end - - describe "HostClassDefintion" do - before(:each) {parse_file :class, __FILE__, log.level, '.pp'} - it "should add a host class object to the Registry" do - hostclass = Registry.at("foo::bar") - expect(hostclass.type).to be(:hostclass) - end - end -end diff --git a/spec/unit/puppetx/yardoc/yard/host_class_handler_spec.rb b/spec/unit/puppetx/yardoc/yard/host_class_handler_spec.rb new file mode 100644 index 0000000..e594373 --- /dev/null +++ b/spec/unit/puppetx/yardoc/yard/host_class_handler_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' +require 'puppetx/yardoc/yard/handlers/host_class_handler' +require 'strings_spec/parsing' + +describe "HostClassDefintion" do + include StringsSpec::Parsing + + def the_hostclass() + Registry.at("foo::bar") + end + + it "should parse single-line documentation strings before a given class" do + comment = "Class: foo::bar" + puppet_code = <<-PUPPET + # #{comment} + class foo::bar { } + PUPPET + + parse(puppet_code, :puppet) + + expect(the_hostclass).to document_a(:type => :hostclass, :docstring => comment) + end + + it "should parse multi-line documentation strings before a given class" do + puppet_code = <<-PUPPET + # Class: foo::bar + # + # This class does some stuff + class foo::bar { } + PUPPET + + parse(puppet_code, :puppet) + + comment = "Class: foo::bar\nThis class does some stuff" + expect(the_hostclass).to document_a(:type => :hostclass, :docstring => comment) + end + + it "should not parse documentation before a class if it is followed by a new line" do + puppet_code = <<-PUPPET + # Class: foo::bar + + class foo::bar { } + PUPPET + + parse(puppet_code, :puppet) + + expect(the_hostclass).to document_a(:type => :hostclass, :docstring => "") + end +end diff --git a/spec/unit/puppetx/yardoc/yard/parser_function_handler_spec.rb b/spec/unit/puppetx/yardoc/yard/parser_function_handler_spec.rb new file mode 100644 index 0000000..b84141e --- /dev/null +++ b/spec/unit/puppetx/yardoc/yard/parser_function_handler_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' +require 'puppetx/yardoc/yard/handlers/parser_function_handler' +require 'strings_spec/parsing' + +describe "ParserFunctionHanlder" do + include StringsSpec::Parsing + + def the_method() + Registry.at("ParserFunctions#the_function") + end + + def the_namespace() + Registry.at("ParserFunctions") + end + + it "should parse single-line documentation strings before a given function" do + comment = "The summary" + parse <<-RUBY + # #{comment} + newfunction(:the_function, :type => rvalue) do |args| + end + RUBY + + expect(the_method).to document_a(:type => :method, :docstring => comment) + expect(the_namespace).to document_a(:type => :puppetnamespace) + end + + it "should parse multi-line documentation strings before a given function" do + parse <<-RUBY + # The summary + # + # The longer description + newfunction(:the_function, :type => rvalue) do |args| + end + RUBY + + comment = "The summary\n\nThe longer description" + expect(the_method).to document_a(:type => :method, :docstring => comment) + expect(the_namespace).to document_a(:type => :puppetnamespace) + end + + it "should not parse documentation before a function if it is followed by two new lines" do + parse <<-RUBY + # The summary + + + newfunction(:the_function, :type => rvalue) do |args| + end + RUBY + + expect(the_method).to document_a(:type => :method, :docstring => "") + expect(the_namespace).to document_a(:type => :puppetnamespace) + end +end