(PDOC-63) Add specs to test the new implementation.

This commit adds specs to cover parts of the new implementation of Puppet
Strings.
This commit is contained in:
Peter Huene 2016-09-14 12:20:26 -07:00
parent f2aa7d00c5
commit cf77ef1379
No known key found for this signature in database
GPG Key ID: 6B585C1742BE3C3C
20 changed files with 2045 additions and 49 deletions

View File

@ -6,17 +6,14 @@ require 'puppet-lint/tasks/puppet-lint'
require 'puppet-strings/tasks'
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
PuppetLint.configuration.ignore_paths = %w(acceptance/**/*.pp spec/**/*.pp pkg/**/*.pp)
desc "Validate manifests, templates, and ruby files"
desc 'Validate Ruby source files and ERB templates.'
task :validate do
Dir['manifests/**/*.pp'].each do |manifest|
sh "puppet parser validate --noop #{manifest}"
end
Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file|
sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/
end
Dir['templates/**/*.erb'].each do |template|
Dir['lib/puppet-strings/yard/templates/**/*.erb'].each do |template|
sh "erb -P -x -T '-' #{template} | ruby -c"
end
end
@ -32,16 +29,16 @@ task :acceptance do
end
cli = BeakerHostGenerator::CLI.new([target])
nodeset_dir = "spec/acceptance/nodesets"
nodeset_dir = 'acceptance/nodesets'
nodeset = "#{nodeset_dir}/#{target}.yml"
FileUtils.mkdir_p(nodeset_dir)
File.open(nodeset, 'w') do |fh|
fh.print(cli.execute)
end
puts nodeset
sh "gem build puppet-strings.gemspec"
sh "puppet module build spec/unit/puppet/examples/test"
sh "BEAKER_set=#{ENV["platform"]} rspec spec/acceptance/*.rb"
sh 'gem build puppet-strings.gemspec'
sh 'puppet module build acceptance/fixtures/modules/test'
sh "BEAKER_set=#{ENV['platform']} rspec acceptance/*.rb"
end
task(:rubocop) do

View File

@ -0,0 +1,5 @@
# function 4x
#
# This is a function which is used to test puppet strings
Puppet::Functions.create_function(:function4x) do
end

View File

@ -0,0 +1,3 @@
Puppet::Parser::Functions.newfunction(:function3x, :doc => "This is the
function documentation for `function3x`") do |args|
end

View File

@ -0,0 +1,27 @@
# 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
class { 'test::install': } ->
class { 'test::config': } ~>
class { 'test::service': } ->
Class['test']
File {
owner => 'user',
path => 'some/file/path',
}
}

View File

@ -0,0 +1,27 @@
# Testing tested classes
# docs stuff
# @param nameservers [String] Don't ask me what this does!
# @param default_lease_time [Integer[1024, 8192]] text goes here
# @param max_lease_time does stuff
class outer (
$dnsdomain,
$nameservers,
$default_lease_time = 3600,
$max_lease_time = 86400
) {
# @param options [String[5,7]] gives user choices
# @param multicast [Boolean] foobar
# @param servers yep, that's right
class middle (
$options = "iburst",
$servers,
$multicast = false
) {
class inner (
$choices = "uburst",
$secenekler = "weallburst",
$boxen,
$manyspell = true
) {}
}
}

View File

@ -0,0 +1,6 @@
{
"name": "username-test",
"version": "0.0.1",
"author": "username",
"license": "Apache 2.0"
}

View File

@ -0,0 +1,28 @@
require 'spec_helper_acceptance'
require 'json'
describe 'Generating module documentation using generate action' do
def read_file_on(host, filename)
on(host, "cat #{filename}").stdout
end
before :all do
modules = JSON.parse(on(master, puppet('module', 'list', '--render-as', 'json')).stdout)
test_module_info = modules['modules_by_path'].values.flatten.find { |mod_info| mod_info =~ /Module test/ }
test_module_path = test_module_info.match(/\(([^)]*)\)/)[1]
on master, puppet('strings', 'generate', "#{test_module_path}/**/*.{rb,pp}")
end
it 'should generate documentation for manifests' do
expect(read_file_on(master, '/root/doc/puppet_classes/test.html')).to include('Class: test')
end
it 'should generate documentation for 3x functions' do
expect(read_file_on(master, '/root/doc/puppet_functions_ruby3x/function3x.html')).to include('This is the function documentation for function3x')
end
it 'should generate documentation for 4x functions' do
expect(read_file_on(master, '/root/doc/puppet_functions_ruby4x/function4x.html')).to include('This is a function which is used to test puppet strings')
end
end

View File

@ -23,13 +23,11 @@ class PuppetStrings::Yard::Handlers::Puppet::Base < YARD::Handlers::Base
# Assign the types for the parameter
statement.parameters.each do |parameter|
tag = tags.find { |t| t.name == parameter.name }
unless tag
log.warn "Missing @param tag for parameter '#{parameter.name}' near #{statement.file}:#{statement.line}." unless object.docstring.empty?
# Add a tag with an empty docstring
object.add_tag YARD::Tags::Tag.new(:param, '', [parameter.type || 'Any'], parameter.name)
object.parameters << [parameter.name, parameter.value]
next
end

View File

@ -1,28 +0,0 @@
require 'spec_helper_acceptance'
require 'json'
describe 'Genearting module documation using yardoc action' do
def read_file_on(host, filename)
on(host, "cat #{filename}").stdout
end
before :all do
modules = JSON.parse(on(master, puppet("module", "list", "--render-as", "json")).stdout)
test_module_info = modules["modules_by_path"].values.flatten.find { |mod_info| mod_info =~ /Module test/ }
test_module_path = test_module_info.match(/\(([^)]*)\)/)[1]
on master, puppet("strings", "#{test_module_path}/**/*.{rb,pp}")
end
it "should generate documentation for manifests" do
expect(read_file_on(master, '/root/doc/test.html')).to include("Class: test")
end
it "should generate documenation for 3x functions" do
expect(read_file_on(master, '/root/doc/Puppet3xFunctions.html')).to include("This is the function documentation for `function3x`")
end
it "should generate documenation for 4x functions" do
expect(read_file_on(master, '/root/doc/Puppet4xFunctions.html')).to include("This is a function which is used to test puppet strings")
end
end

348
spec/fixtures/unit/json/output.json vendored Normal file
View File

@ -0,0 +1,348 @@
{
"puppet_classes": [
{
"name": "klass",
"file": "(stdin)",
"line": 5,
"inherits": "foo::bar",
"docstring": {
"text": "A simple class.",
"tags": [
{
"tag_name": "param",
"text": "First param.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "Second param.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "Third param.",
"types": [
"String"
],
"name": "param3"
}
]
},
"defaults": {
"param3": "hi"
},
"source": "class klass(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {\n}"
}
],
"defined_types": [
{
"name": "dt",
"file": "(stdin)",
"line": 12,
"docstring": {
"text": "A simple defined type.",
"tags": [
{
"tag_name": "param",
"text": "First param.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "Second param.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "Third param.",
"types": [
"String"
],
"name": "param3"
}
]
},
"defaults": {
"param3": "hi"
},
"source": "define dt(Integer $param1, $param2, String $param3 = hi) {\n}"
}
],
"resource_types": [
{
"name": "database",
"file": "(stdin)",
"line": 43,
"docstring": {
"text": "An example database server resource type."
},
"properties": [
{
"name": "ensure",
"description": "What state the database should be in.",
"values": [
"present",
"absent",
"up",
"down"
],
"aliases": {
"up": "present",
"down": "absent"
},
"default": "up"
},
{
"name": "file",
"description": "The database file to use."
},
{
"name": "log_level",
"description": "The log level to use.",
"values": [
"debug",
"warn",
"error"
],
"default": "warn"
}
],
"parameters": [
{
"name": "address",
"description": "The database server name.",
"isnamevar": true
},
{
"name": "encryption_key",
"description": "The encryption key to use."
},
{
"name": "encrypt",
"description": "Whether or not to encrypt the database.",
"values": [
"true",
"false",
"yes",
"no"
],
"default": "false"
}
],
"features": [
{
"name": "encryption",
"description": "The provider supports encryption."
}
]
}
],
"providers": [
{
"name": "linux",
"type_name": "database",
"file": "(stdin)",
"line": 33,
"docstring": {
"text": "An example provider on Linux."
},
"confines": {
"kernel": "Linux",
"osfamily": "RedHat"
},
"features": [
"implements_some_feature",
"some_other_feature"
],
"defaults": {
"kernel": "Linux"
},
"commands": {
"foo": "/usr/bin/foo"
}
}
],
"puppet_functions": [
{
"name": "func",
"file": "(stdin)",
"line": 20,
"type": "puppet",
"signature": "func(Integer $param1, Any $param2, String $param3 = hi)",
"docstring": {
"text": "A simple function.",
"tags": [
{
"tag_name": "param",
"text": "First param.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "Second param.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "Third param.",
"types": [
"String"
],
"name": "param3"
},
{
"tag_name": "return",
"text": "Returns nothing.",
"types": [
"Undef"
]
}
]
},
"defaults": {
"param3": "hi"
},
"source": "function func(Integer $param1, $param2, String $param3 = hi) {\n}"
},
{
"name": "func3x",
"file": "(stdin)",
"line": 1,
"type": "ruby3x",
"signature": "func3x(String $first, Any $second)",
"docstring": {
"text": "An example 3.x function.",
"tags": [
{
"tag_name": "param",
"text": "The first parameter.",
"types": [
"String"
],
"name": "first"
},
{
"tag_name": "param",
"text": "The second parameter.",
"types": [
"Any"
],
"name": "second"
},
{
"tag_name": "return",
"text": "Returns nothing.",
"types": [
"Undef"
]
}
]
},
"source": "Puppet::Parser::Functions.newfunction(:func3x, doc: <<-DOC\nAn example 3.x function.\n@param [String] first The first parameter.\n@param second The second parameter.\n@return [Undef] Returns nothing.\nDOC\n) do |*args|\nend"
},
{
"name": "func4x",
"file": "(stdin)",
"line": 11,
"type": "ruby4x",
"docstring": {
"text": "An example 4.x function.",
"tags": [
{
"tag_name": "overload",
"signature": "func4x(Integer $param1, Any $param2, Optional[Array[String]] $param3)",
"docstring": {
"text": "The first overload.",
"tags": [
{
"tag_name": "param",
"text": "The first parameter.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "The second parameter.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "The third parameter.",
"types": [
"Optional[Array[String]]"
],
"name": "param3"
},
{
"tag_name": "return",
"text": "Returns nothing.",
"types": [
"Undef"
]
}
]
},
"name": "func4x"
},
{
"tag_name": "overload",
"signature": "func4x(Boolean $param, Callable &$block)",
"docstring": {
"text": "The second overload.",
"tags": [
{
"tag_name": "param",
"text": "The first parameter.",
"types": [
"Boolean"
],
"name": "param"
},
{
"tag_name": "param",
"text": "The block parameter.",
"types": [
"Callable"
],
"name": "&block"
},
{
"tag_name": "return",
"text": "Returns a string.",
"types": [
"String"
]
}
]
},
"name": "func4x"
}
]
},
"source": "Puppet::Functions.create_function(:func4x) do\n # The first overload.\n # @param param1 The first parameter.\n # @param param2 The second parameter.\n # @param param3 The third parameter.\n # @return [Undef] Returns nothing.\n dispatch :foo do\n param 'Integer', :param1\n param 'Any', :param2\n optional_param 'Array[String]', :param3\n end\n\n # The second overload.\n # @param param The first parameter.\n # @param block The block parameter.\n # @return [String] Returns a string.\n dispatch :other do\n param 'Boolean', :param\n block_param\n end\nend"
}
]
}

View File

@ -1,13 +1,17 @@
dir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift File.join(dir, 'lib')
require 'mocha'
require 'puppet'
require 'rspec'
require 'puppet-strings'
require 'puppet-strings/yard'
# Explicitly set up YARD once
PuppetStrings::Yard.setup!
RSpec.configure do |config|
config.mock_with :mocha
config.mock_with :mocha
config.before(:each) do
# Always clear the YARD registry before each example
YARD::Registry.clear
end
end

View File

@ -12,12 +12,11 @@ RSpec.configure do |c|
# Configure all nodes in nodeset
c.before :suite do
hosts.each do |host|
hosts.each do |host|
scp_to(host, Dir.glob('puppet-strings*.gem').first, 'puppet-strings.gem')
on host, 'gem install puppet-strings.gem'
scp_to(host, Dir.glob('spec/unit/puppet/examples/test/pkg/username-test*.gz').first, 'test.tar.gz')
scp_to(host, Dir.glob('acceptance/fixtures/modules/test/pkg/username-test*.gz').first, 'test.tar.gz')
on host, puppet('module', 'install', 'test.tar.gz')
on host, 'gem install yard'

View File

@ -0,0 +1,132 @@
require 'spec_helper'
require 'puppet-strings/json'
require 'tempfile'
describe PuppetStrings::Json do
before :each do
# Populate the YARD registry with both Puppet and Ruby source
YARD::Parser::SourceParser.parse_string(<<-SOURCE, :puppet)
# A simple class.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
class klass(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
}
# A simple defined type.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
define dt(Integer $param1, $param2, String $param3 = hi) {
}
# A simple function.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
# @return [Undef] Returns nothing.
function func(Integer $param1, $param2, String $param3 = hi) {
}
SOURCE
YARD::Parser::SourceParser.parse_string(<<-SOURCE, :ruby)
Puppet::Parser::Functions.newfunction(:func3x, doc: <<-DOC
An example 3.x function.
@param [String] first The first parameter.
@param second The second parameter.
@return [Undef] Returns nothing.
DOC
) do |*args|
end
# An example 4.x function.
Puppet::Functions.create_function(:func4x) do
# The first overload.
# @param param1 The first parameter.
# @param param2 The second parameter.
# @param param3 The third parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
param 'Integer', :param1
param 'Any', :param2
optional_param 'Array[String]', :param3
end
# The second overload.
# @param param The first parameter.
# @param block The block parameter.
# @return [String] Returns a string.
dispatch :other do
param 'Boolean', :param
block_param
end
end
Puppet::Type.type(:database).provide :linux do
desc 'An example provider on Linux.'
confine kernel: 'Linux'
confine osfamily: 'RedHat'
defaultfor kernel: 'Linux'
has_feature :implements_some_feature
has_feature :some_other_feature
commands foo: /usr/bin/foo
end
Puppet::Type.newtype(:database) do
desc 'An example database server resource type.'
feature :encryption, 'The provider supports encryption.', methods: [:encrypt]
ensurable do
desc 'What state the database should be in.'
defaultvalues
aliasvalue(:up, :present)
aliasvalue(:down, :absent)
defaultto :up
end
newparam(:address) do
isnamevar
desc 'The database server name.'
end
newparam(:encryption_key, required_features: :encryption) do
desc 'The encryption key to use.'
end
newparam(:encrypt, :parent => Puppet::Parameter::Boolean) do
desc 'Whether or not to encrypt the database.'
defaultto false
end
newproperty(:file) do
desc 'The database file to use.'
end
newproperty(:log_level) do
desc 'The log level to use.'
newvalue(:debug)
newvalue(:warn)
newvalue(:error)
defaultto 'warn'
end
end
SOURCE
end
let(:baseline_path) { File.join(File.dirname(__FILE__), '../../fixtures/unit/json/output.json') }
let(:baseline) { File.read(baseline_path) }
describe 'rendering JSON to a file' do
it 'should output the expected JSON content' do
Tempfile.open('json') do |file|
PuppetStrings::Json.render(file.path)
expect(File.read(file.path)).to eq(baseline)
end
end
end
describe 'rendering JSON to stdout' do
it 'should output the expected JSON content' do
expect{ PuppetStrings::Json.render(nil) }.to output(baseline).to_stdout
end
end
end

View File

@ -0,0 +1,155 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Handlers::Puppet::ClassHandler do
subject {
YARD::Parser::SourceParser.parse_string(source, :puppet)
YARD::Registry.all(:puppet_class)
}
describe 'parsing source without a class definition' do
let(:source) { 'notice hi' }
it 'no classes should be in the registry' do
expect(subject.empty?).to eq(true)
end
end
describe 'parsing source with a syntax error' do
let(:source) { 'class foo{' }
it 'should log an error' do
expect{ subject }.to output(/\[error\]: Failed to parse \(stdin\): Syntax error at end of file/).to_stdout_from_any_process
expect(subject.empty?).to eq(true)
end
end
describe 'parsing a class with a missing docstring' do
let(:source) { 'class foo{}' }
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet class 'foo' at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a class with a docstring' do
let(:source) { <<-SOURCE
# A simple foo class.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should register a class object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Class)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Classes.instance)
expect(object.name).to eq(:foo)
expect(object.statement).not_to eq(nil)
expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'hi']])
expect(object.docstring).to eq('A simple foo class.')
expect(object.docstring.tags.size).to eq(4)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(3)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('First param.')
expect(tags[0].types).to eq(['Integer'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('Second param.')
expect(tags[1].types).to eq(['Any'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('Third param.')
expect(tags[2].types).to eq(['String'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a class with a missing parameter' do
let(:source) { <<-SOURCE
# A simple foo class.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
# @param param4 missing!
class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param4' has no matching parameter at \(stdin\):6\./).to_stdout_from_any_process
end
end
describe 'parsing a class with a missing @param tag' do
let(:source) { <<-SOURCE
# A simple foo class.
# @param param1 First param.
# @param param2 Second param.
class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param3' near \(stdin\):4\./).to_stdout_from_any_process
end
end
describe 'parsing a class with a typed parameter that also has a @param tag type' do
let(:source) { <<-SOURCE
# A simple foo class.
# @param [Boolean] param1 First param.
# @param param2 Second param.
# @param param3 Third param.
class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):5: ignoring in favor of parameter type information\./).to_stdout_from_any_process
end
end
describe 'parsing a class with a untyped parameter that also has a @param tag type' do
let(:source) { <<-SOURCE
# A simple foo class.
# @param param1 First param.
# @param [Boolean] param2 Second param.
# @param param3 Third param.
class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should respect the type that was documented' do
expect{ subject }.to output('').to_stdout_from_any_process
expect(subject.size).to eq(1)
tags = subject.first.tags(:param)
expect(tags.size).to eq(3)
expect(tags[1].types).to eq(['Boolean'])
end
end
end

View File

@ -0,0 +1,155 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Handlers::Puppet::DefinedTypeHandler do
subject {
YARD::Parser::SourceParser.parse_string(source, :puppet)
YARD::Registry.all(:puppet_defined_type)
}
describe 'parsing source without a defined type definition' do
let(:source) { 'notice hi' }
it 'no defined types should be in the registry' do
expect(subject.empty?).to eq(true)
end
end
describe 'parsing source with a syntax error' do
let(:source) { 'define foo{' }
it 'should log an error' do
expect{ subject }.to output(/\[error\]: Failed to parse \(stdin\): Syntax error at end of file/).to_stdout_from_any_process
expect(subject.empty?).to eq(true)
end
end
describe 'parsing a defined type with a missing docstring' do
let(:source) { 'define foo{}' }
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet defined type 'foo' at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a defined type with a docstring' do
let(:source) { <<-SOURCE
# A simple foo defined type.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
define foo(Integer $param1, $param2, String $param3 = hi) {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should register a defined type object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::DefinedType)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::DefinedTypes.instance)
expect(object.name).to eq(:foo)
expect(object.statement).not_to eq(nil)
expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'hi']])
expect(object.docstring).to eq('A simple foo defined type.')
expect(object.docstring.tags.size).to eq(4)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(3)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('First param.')
expect(tags[0].types).to eq(['Integer'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('Second param.')
expect(tags[1].types).to eq(['Any'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('Third param.')
expect(tags[2].types).to eq(['String'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a defined type with a missing parameter' do
let(:source) { <<-SOURCE
# A simple foo defined type.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
# @param param4 missing!
define foo(Integer $param1, $param2, String $param3 = hi) {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param4' has no matching parameter at \(stdin\):6\./).to_stdout_from_any_process
end
end
describe 'parsing a defined type with a missing @param tag' do
let(:source) { <<-SOURCE
# A simple foo defined type.
# @param param1 First param.
# @param param2 Second param.
define foo(Integer $param1, $param2, String $param3 = hi) {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param3' near \(stdin\):4\./).to_stdout_from_any_process
end
end
describe 'parsing a defined type with a typed parameter that also has a @param tag type' do
let(:source) { <<-SOURCE
# A simple foo defined type.
# @param [Boolean] param1 First param.
# @param param2 Second param.
# @param param3 Third param.
define foo(Integer $param1, $param2, String $param3 = hi) {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):5: ignoring in favor of parameter type information\./).to_stdout_from_any_process
end
end
describe 'parsing a defined type with a untyped parameter that also has a @param tag type' do
let(:source) { <<-SOURCE
# A simple foo defined type.
# @param param1 First param.
# @param [Boolean] param2 Second param.
# @param param3 Third param.
define foo(Integer $param1, $param2, String $param3 = hi) {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should respect the type that was documented' do
expect{ subject }.to output('').to_stdout_from_any_process
expect(subject.size).to eq(1)
tags = subject.first.tags(:param)
expect(tags.size).to eq(3)
expect(tags[1].types).to eq(['Boolean'])
end
end
end

View File

@ -0,0 +1,168 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Handlers::Puppet::FunctionHandler do
subject {
YARD::Parser::SourceParser.parse_string(source, :puppet)
YARD::Registry.all(:puppet_function)
}
describe 'parsing source without a function definition' do
let(:source) { 'notice hi' }
it 'no functions should be in the registry' do
expect(subject.empty?).to eq(true)
end
end
describe 'parsing source with a syntax error' do
let(:source) { 'function foo{' }
it 'should log an error' do
expect{ subject }.to output(/\[error\]: Failed to parse \(stdin\): Syntax error at end of file/).to_stdout_from_any_process
expect(subject.empty?).to eq(true)
end
end
describe 'parsing a function with a missing docstring' do
let(:source) { 'function foo{}' }
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet function 'foo' at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a docstring' do
let(:source) { <<-SOURCE
# A simple foo function.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
# @return [Undef] Returns nothing.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice 'hello world'
undef
}
SOURCE
}
it 'should register a function object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::PUPPET))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(Integer $param1, Any $param2, String $param3 = hi)')
expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'hi']])
expect(object.docstring).to eq('A simple foo function.')
expect(object.docstring.tags.size).to eq(5)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(3)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('First param.')
expect(tags[0].types).to eq(['Integer'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('Second param.')
expect(tags[1].types).to eq(['Any'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('Third param.')
expect(tags[2].types).to eq(['String'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with a missing parameter' do
let(:source) { <<-SOURCE
# A simple foo function.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
# @param param4 missing!
# @return [Undef] Returns nothing.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice 'hello world'
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param4' has no matching parameter at \(stdin\):7\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a missing @param tag' do
let(:source) { <<-SOURCE
# A simple foo function.
# @param param1 First param.
# @param param2 Second param.
# @return [Undef] Returns nothing.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice 'hello world'
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param3' near \(stdin\):5\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a typed parameter that also has a @param tag type' do
let(:source) { <<-SOURCE
# A simple foo function.
# @param [Boolean] param1 First param.
# @param param2 Second param.
# @param param3 Third param.
# @return [Undef] Returns nothing.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice 'hello world'
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):6: ignoring in favor of parameter type information\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a untyped parameter that also has a @param tag type' do
let(:source) { <<-SOURCE
# A simple foo function.
# @param param1 First param.
# @param [Boolean] param2 Second param.
# @param param3 Third param.
# @return [Undef] Returns nothing.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice 'hello world'
}
SOURCE
}
it 'should respect the type that was documented' do
expect{ subject }.to output('').to_stdout_from_any_process
expect(subject.size).to eq(1)
tags = subject.first.tags(:param)
expect(tags.size).to eq(3)
expect(tags[1].types).to eq(['Boolean'])
end
end
describe 'parsing a function with a missing @return tag' do
let(:source) { <<-SOURCE
# A simple foo function.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice 'hello world'
}
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: Missing @return tag near \(stdin\):5\./).to_stdout_from_any_process
end
end
end

View File

@ -0,0 +1,613 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Handlers::Ruby::FunctionHandler do
subject {
YARD::Parser::SourceParser.parse_string(source, :ruby)
YARD::Registry.all(:puppet_function)
}
describe 'parsing source without a function definition' do
let(:source) { 'puts "hi"' }
it 'no functions should be in the registry' do
expect(subject.empty?).to eq(true)
end
end
describe 'parsing 3.x API functions' do
describe 'parsing a function with a missing docstring' do
let(:source) { <<-SOURCE
Puppet::Parser::Functions.newfunction(:foo) do |*args|
end
SOURCE
}
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet function 'foo' at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a doc parameter' do
let(:source) { <<-SOURCE
Puppet::Parser::Functions.newfunction(:foo, doc: <<-DOC
An example 3.x function.
@param [String] first The first parameter.
@param second The second parameter.
@return [Undef] Returns nothing.
DOC
) do |*args|
end
SOURCE
}
it 'should register a function object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_3X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(String $first, Any $second)')
expect(object.parameters).to eq([['first', nil], ['second', nil]])
expect(object.docstring).to eq('An example 3.x function.')
expect(object.docstring.tags.size).to eq(4)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(2)
expect(tags[0].name).to eq('first')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['String'])
expect(tags[1].name).to eq('second')
expect(tags[1].text).to eq('The second parameter.')
expect(tags[1].types).to eq(['Any'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with a missing @return tag' do
let(:source) { <<-SOURCE
Puppet::Parser::Functions.newfunction(:foo, doc: <<-DOC) do |*args|
An example 3.x function.
@param [String] first The first parameter.
@param second The second parameter.
DOC
end
SOURCE
}
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing @return tag near \(stdin\):1/).to_stdout_from_any_process
end
end
end
describe 'parsing 4.x API functions' do
describe 'parsing a function with a missing docstring' do
let(:source) { <<-SOURCE
Puppet::Functions.create_function(:foo) do
end
SOURCE
}
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet function 'foo' at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a simple docstring' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
end
SOURCE
}
it 'should register a function object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo()')
expect(object.parameters).to eq([])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags.size).to eq(1)
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function without any dispatches' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param [Integer] param1 The first parameter.
# @param param2 The second parameter.
# @param [String] param3 The third parameter.
# @return [Undef] Returns nothing.
def foo(param1, param2, param3 = nil)
end
end
SOURCE
}
it 'should register a function object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(Integer $param1, Any $param2, Optional[String] $param3 = undef)')
expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'undef']])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags.size).to eq(5)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(3)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['Integer'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('The second parameter.')
expect(tags[1].types).to eq(['Any'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('The third parameter.')
expect(tags[2].types).to eq(['Optional[String]'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with a single dispatch' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param param1 The first parameter.
# @param param2 The second parameter.
# @param param3 The third parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
param 'Integer', :param1
param 'Any', :param2
optional_param 'Array[String]', :param3
end
def foo(param1, param2, param3 = nil)
end
end
SOURCE
}
it 'should register a function object without any overload tags' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(Integer $param1, Any $param2, Optional[Array[String]] $param3)')
expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', nil]])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags(:overload).empty?).to be_truthy
expect(object.docstring.tags.size).to eq(5)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(3)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['Integer'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('The second parameter.')
expect(tags[1].types).to eq(['Any'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('The third parameter.')
expect(tags[2].types).to eq(['Optional[Array[String]]'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with various dispatch parameters.' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param param1 The first parameter.
# @param param2 The second parameter.
# @param param3 The third parameter.
# @param param4 The fourth parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
param 'String', :param1
required_param 'Integer', :param2
optional_param 'Array', :param3
repeated_param 'String', :param4
end
end
SOURCE
}
it 'should register a function object with the expected parameters' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(String $param1, Integer $param2, Optional[Array] $param3, String *$param4)')
expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', nil], ['*param4', nil]])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags(:overload).empty?).to be_truthy
expect(object.docstring.tags.size).to eq(6)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(4)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['String'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('The second parameter.')
expect(tags[1].types).to eq(['Integer'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('The third parameter.')
expect(tags[2].types).to eq(['Optional[Array]'])
expect(tags[3].name).to eq('*param4')
expect(tags[3].text).to eq('The fourth parameter.')
expect(tags[3].types).to eq(['String'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with an optional repeated param.' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param param The first parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
optional_repeated_param 'String', :param
end
end
SOURCE
}
it 'should register a function object with the expected parameters' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(Optional[String] *$param)')
expect(object.parameters).to eq([['*param', nil]])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags(:overload).empty?).to be_truthy
expect(object.docstring.tags.size).to eq(3)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(1)
expect(tags[0].name).to eq('*param')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['Optional[String]'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with a block param with one parameter' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param a_block The block parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
block_param :a_block
end
end
SOURCE
}
it 'should register a function object with the expected parameters' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(Callable &$a_block)')
expect(object.parameters).to eq([['&a_block', nil]])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags(:overload).empty?).to be_truthy
expect(object.docstring.tags.size).to eq(3)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(1)
expect(tags[0].name).to eq('&a_block')
expect(tags[0].text).to eq('The block parameter.')
expect(tags[0].types).to eq(['Callable'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with a block param with two parameter' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param a_block The block parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
optional_block_param 'Callable[String]', :a_block
end
end
SOURCE
}
it 'should register a function object with the expected parameters' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('foo(Optional[Callable[String]] &$a_block)')
expect(object.parameters).to eq([['&a_block', nil]])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags(:overload).empty?).to be_truthy
expect(object.docstring.tags.size).to eq(3)
tags = object.docstring.tags(:param)
expect(tags.size).to eq(1)
expect(tags[0].name).to eq('&a_block')
expect(tags[0].text).to eq('The block parameter.')
expect(tags[0].types).to eq(['Optional[Callable[String]]'])
tags = object.docstring.tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
end
describe 'parsing a function with a multiple dispatches' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# The first overload.
# @param param1 The first parameter.
# @param param2 The second parameter.
# @param param3 The third parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
param 'Integer', :param1
param 'Any', :param2
optional_param 'Array[String]', :param3
end
# The second overload.
# @param param The first parameter.
# @param block The block parameter.
# @return [String] Returns a string.
dispatch :other do
param 'Boolean', :param
block_param
end
def foo(param1, param2, param3 = nil)
end
def other(b)
'lol'
end
end
SOURCE
}
it 'should register a function object with overload tags' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X))
expect(object.name).to eq(:foo)
expect(object.signature).to eq('')
expect(object.parameters).to eq([])
expect(object.docstring).to eq('An example 4.x function.')
expect(object.docstring.tags(:param).empty?).to be_truthy
expect(object.docstring.tags(:return).empty?).to be_truthy
expect(object.docstring.tags.size).to eq(3)
overloads = object.docstring.tags(:overload)
expect(overloads.size).to eq(2)
expect(overloads[0]).to be_a(PuppetStrings::Yard::Tags::OverloadTag)
expect(overloads[0].docstring).to eq('The first overload.')
expect(overloads[0].signature).to eq('foo(Integer $param1, Any $param2, Optional[Array[String]] $param3)')
expect(overloads[0].tags.size).to eq(4)
tags = overloads[0].tags(:param)
expect(tags.size).to eq(3)
expect(tags[0].name).to eq('param1')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['Integer'])
expect(tags[1].name).to eq('param2')
expect(tags[1].text).to eq('The second parameter.')
expect(tags[1].types).to eq(['Any'])
expect(tags[2].name).to eq('param3')
expect(tags[2].text).to eq('The third parameter.')
expect(tags[2].types).to eq(['Optional[Array[String]]'])
tags = overloads[0].tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns nothing.')
expect(tags[0].types).to eq(['Undef'])
expect(overloads[1]).to be_a(PuppetStrings::Yard::Tags::OverloadTag)
expect(overloads[1].docstring).to eq('The second overload.')
expect(overloads[1].signature).to eq('foo(Boolean $param, Callable &$block)')
expect(overloads[1].tags.size).to eq(3)
tags = overloads[1].tags(:param)
expect(tags.size).to eq(2)
expect(tags[0].name).to eq('param')
expect(tags[0].text).to eq('The first parameter.')
expect(tags[0].types).to eq(['Boolean'])
expect(tags[1].name).to eq('&block')
expect(tags[1].text).to eq('The block parameter.')
expect(tags[1].types).to eq(['Callable'])
tags = overloads[1].tags(:return)
expect(tags.size).to eq(1)
expect(tags[0].name).to be_nil
expect(tags[0].text).to eq('Returns a string.')
expect(tags[0].types).to eq(['String'])
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
end
end
describe 'parsing a function with a missing parameter' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param missing A missing parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'missing' has no matching parameter at \(stdin\):5/).to_stdout_from_any_process
end
end
describe 'parsing a function with a missing @param tag' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @return [Undef] Returns nothing.
dispatch :foo do
param 'String', :param1
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param1' near \(stdin\):5/).to_stdout_from_any_process
end
end
describe 'parsing a function with a typed @param tag' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param [Integer] param1 The first parameter.
# @return [Undef] Returns nothing.
dispatch :foo do
param 'String', :param1
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):6: ignoring in favor of dispatch type information\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a typed @param tag' do
let(:source) { <<-SOURCE
# An example 4.x function.
Puppet::Functions.create_function(:foo) do
# @param param1 The first parameter.
dispatch :foo do
param 'String', :param1
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: Missing @return tag near \(stdin\):4/).to_stdout_from_any_process
end
end
describe 'parsing a function with a root @param tag' do
let(:source) { <<-SOURCE
# An example 4.x function.
# @param param Nope.
Puppet::Functions.create_function(:foo) do
# @return [Undef]
dispatch :foo do
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The docstring for Puppet 4.x function 'foo' contains @param tags near \(stdin\):3: parameter documentation should be made on the dispatch call\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a root @overload tag' do
let(:source) { <<-SOURCE
# An example 4.x function.
# @overload foo
Puppet::Functions.create_function(:foo) do
# @return [Undef]
dispatch :foo do
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The docstring for Puppet 4.x function 'foo' contains @overload tags near \(stdin\):3: overload tags are automatically generated from the dispatch calls\./).to_stdout_from_any_process
end
end
describe 'parsing a function with a root @return tag' do
let(:source) { <<-SOURCE
# An example 4.x function.
# @return [Undef] foo
Puppet::Functions.create_function(:foo) do
# @return [Undef]
dispatch :foo do
end
end
SOURCE
}
it 'should output a warning' do
expect{ subject }.to output(/\[warn\]: The docstring for Puppet 4.x function 'foo' contains @return tags near \(stdin\):3: return value documentation should be made on the dispatch call\./).to_stdout_from_any_process
end
end
end

View File

@ -0,0 +1,62 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Handlers::Ruby::ProviderHandler do
subject {
YARD::Parser::SourceParser.parse_string(source, :ruby)
YARD::Registry.all(:puppet_provider)
}
describe 'parsing source without a provider definition' do
let(:source) { 'puts "hi"' }
it 'no providers should be in the registry' do
expect(subject.empty?).to eq(true)
end
end
describe 'parsing a provider with a missing description' do
let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
end
SOURCE
}
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing a description for Puppet provider 'linux' \(resource type 'custom'\) at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a provider definition' do
let(:source) { <<-SOURCE
Puppet::Type.type(:custom).provide :linux do
desc 'An example provider on Linux.'
confine kernel: 'Linux'
confine osfamily: 'RedHat'
defaultfor kernel: 'Linux'
has_feature :implements_some_feature
has_feature :some_other_feature
commands foo: /usr/bin/foo
end
SOURCE
}
it 'should register a provider object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Provider)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Providers.instance('custom'))
expect(object.name).to eq(:linux)
expect(object.type_name).to eq('custom')
expect(object.docstring).to eq('An example provider on Linux.')
expect(object.docstring.tags.size).to eq(1)
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
expect(object.confines).to eq({ 'kernel' => 'Linux', 'osfamily' => 'RedHat'})
expect(object.defaults).to eq({ 'kernel' => 'Linux'})
expect(object.features).to eq(['implements_some_feature', 'some_other_feature'])
expect(object.commands).to eq({'foo' => '/usr/bin/foo'})
end
end
end

View File

@ -0,0 +1,126 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Handlers::Ruby::TypeHandler do
subject {
YARD::Parser::SourceParser.parse_string(source, :ruby)
YARD::Registry.all(:puppet_type)
}
describe 'parsing source without a type definition' do
let(:source) { 'puts "hi"' }
it 'no types should be in the registry' do
expect(subject.empty?).to eq(true)
end
end
describe 'parsing a type with a missing description' do
let(:source) { <<-SOURCE
Puppet::Type.newtype(:database) do
end
SOURCE
}
it 'should log a warning' do
expect{ subject }.to output(/\[warn\]: Missing a description for Puppet resource type 'database' at \(stdin\):1\./).to_stdout_from_any_process
end
end
describe 'parsing a type definition' do
let(:source) { <<-SOURCE
Puppet::Type.newtype(:database) do
desc 'An example database server resource type.'
feature :encryption, 'The provider supports encryption.', methods: [:encrypt]
ensurable do
desc 'What state the database should be in.'
defaultvalues
aliasvalue(:up, :present)
aliasvalue(:down, :absent)
defaultto :up
end
newparam(:address) do
isnamevar
desc 'The database server name.'
end
newparam(:encryption_key, required_features: :encryption) do
desc 'The encryption key to use.'
end
newparam(:encrypt, :parent => Puppet::Parameter::Boolean) do
desc 'Whether or not to encrypt the database.'
defaultto false
end
newproperty(:file) do
desc 'The database file to use.'
end
newproperty(:log_level) do
desc 'The log level to use.'
newvalue(:debug)
newvalue(:warn)
newvalue(:error)
defaultto 'warn'
end
end
SOURCE
}
it 'should register a type object' do
expect(subject.size).to eq(1)
object = subject.first
expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Type)
expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Types.instance)
expect(object.name).to eq(:database)
expect(object.docstring).to eq('An example database server resource type.')
expect(object.docstring.tags.size).to eq(1)
tags = object.docstring.tags(:api)
expect(tags.size).to eq(1)
expect(tags[0].text).to eq('public')
expect(object.properties.size).to eq(3)
expect(object.properties[0].name).to eq('ensure')
expect(object.properties[0].docstring).to eq('What state the database should be in.')
expect(object.properties[0].isnamevar).to eq(false)
expect(object.properties[0].default).to eq('up')
expect(object.properties[0].values).to eq(%w(present absent up down))
expect(object.properties[0].aliases).to eq({ 'down' => 'absent', 'up' => 'present' })
expect(object.properties[1].name).to eq('file')
expect(object.properties[1].docstring).to eq('The database file to use.')
expect(object.properties[1].isnamevar).to eq(false)
expect(object.properties[1].default).to be_nil
expect(object.properties[1].values).to eq([])
expect(object.properties[1].aliases).to eq({})
expect(object.properties[2].name).to eq('log_level')
expect(object.properties[2].docstring).to eq('The log level to use.')
expect(object.properties[2].isnamevar).to eq(false)
expect(object.properties[2].default).to eq('warn')
expect(object.properties[2].values).to eq(%w(debug warn error))
expect(object.properties[2].aliases).to eq({})
expect(object.parameters.size).to eq(3)
expect(object.parameters[0].name).to eq('address')
expect(object.parameters[0].docstring).to eq('The database server name.')
expect(object.parameters[0].isnamevar).to eq(true)
expect(object.parameters[0].default).to be_nil
expect(object.parameters[0].values).to eq([])
expect(object.parameters[0].aliases).to eq({})
expect(object.parameters[1].name).to eq('encryption_key')
expect(object.parameters[1].docstring).to eq('The encryption key to use.')
expect(object.parameters[1].isnamevar).to eq(false)
expect(object.parameters[1].default).to be_nil
expect(object.parameters[1].values).to eq([])
expect(object.parameters[1].aliases).to eq({})
expect(object.parameters[2].name).to eq('encrypt')
expect(object.parameters[2].docstring).to eq('Whether or not to encrypt the database.')
expect(object.parameters[2].isnamevar).to eq(false)
expect(object.parameters[2].default).to eq('false')
expect(object.parameters[2].values).to eq(%w(true false yes no))
expect(object.parameters[2].aliases).to eq({})
expect(object.features.size).to eq(1)
expect(object.features[0].name).to eq('encryption')
expect(object.features[0].docstring).to eq('The provider supports encryption.')
end
end
end

View File

@ -0,0 +1,171 @@
require 'spec_helper'
require 'puppet-strings/yard'
describe PuppetStrings::Yard::Parsers::Puppet::Parser do
subject { PuppetStrings::Yard::Parsers::Puppet::Parser.new(source, file) }
let(:file) { 'test.pp' }
describe 'initialization of the parser' do
let(:source) { 'notice hi' }
it 'should store the original source' do
expect(subject.source).to eq(source)
end
it 'should store the original file name' do
expect(subject.file).to eq(file)
end
it 'should have no relevant statements' do
subject.parse
expect(subject.enumerator.empty?).to be_truthy
end
end
describe 'parsing invalid Puppet source code' do
let(:source) { <<SOURCE
class foo {
SOURCE
}
it 'should raise an exception' do
expect{ subject.parse }.to output(/\[error\]: Failed to parse test.pp: Syntax error at end of file/).to_stdout_from_any_process
end
end
describe 'parsing class definitions' do
let(:source) { <<SOURCE
notice hello
# A simple foo class.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should only return the class statement' do
subject.parse
expect(subject.enumerator.size).to eq(1)
statement = subject.enumerator.first
expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::ClassStatement)
expect(statement.source).to eq("class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {\n file { '/tmp/foo':\n ensure => present\n }\n}")
expect(statement.file).to eq(file)
expect(statement.line).to eq(6)
expect(statement.docstring).to eq('A simple foo class.')
expect(statement.name).to eq('foo')
expect(statement.parent_class).to eq('foo::bar')
expect(statement.parameters.size).to eq(3)
expect(statement.parameters[0].name).to eq('param1')
expect(statement.parameters[0].type).to eq('Integer')
expect(statement.parameters[0].value).to be_nil
expect(statement.parameters[1].name).to eq('param2')
expect(statement.parameters[1].type).to be_nil
expect(statement.parameters[1].value).to be_nil
expect(statement.parameters[2].name).to eq('param3')
expect(statement.parameters[2].type).to eq('String')
expect(statement.parameters[2].value).to eq('hi')
end
end
describe 'parsing nested class definitions' do
let(:source) { <<SOURCE
class foo {
class bar {
}
}
SOURCE
}
it 'should parse both class statements' do
subject.parse
expect(subject.enumerator.size).to eq(2)
statement = subject.enumerator[0]
expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::ClassStatement)
expect(statement.name).to eq('foo::bar')
expect(statement.parameters.size).to eq(0)
statement = subject.enumerator[1]
expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::ClassStatement)
expect(statement.name).to eq('foo')
expect(statement.parameters.size).to eq(0)
end
end
describe 'parsing defined types' do
let(:source) { <<SOURCE
notice hello
# A simple foo defined type.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
define foo(Integer $param1, $param2, String $param3 = hi) {
file { '/tmp/foo':
ensure => present
}
}
SOURCE
}
it 'should parse the defined type statement' do
subject.parse
expect(subject.enumerator.size).to eq(1)
statement = subject.enumerator.first
expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::DefinedTypeStatement)
expect(statement.name).to eq('foo')
expect(statement.source).to eq("define foo(Integer $param1, $param2, String $param3 = hi) {\n file { '/tmp/foo':\n ensure => present\n }\n}")
expect(statement.file).to eq(file)
expect(statement.line).to eq(6)
expect(statement.docstring).to eq('A simple foo defined type.')
expect(statement.parameters.size).to eq(3)
expect(statement.parameters[0].name).to eq('param1')
expect(statement.parameters[0].type).to eq('Integer')
expect(statement.parameters[0].value).to be_nil
expect(statement.parameters[1].name).to eq('param2')
expect(statement.parameters[1].type).to be_nil
expect(statement.parameters[1].value).to be_nil
expect(statement.parameters[2].name).to eq('param3')
expect(statement.parameters[2].type).to eq('String')
expect(statement.parameters[2].value).to eq('hi')
end
end
describe 'parsing puppet functions' do
let(:source) { <<SOURCE
notice hello
# A simple foo function.
# @param param1 First param.
# @param param2 Second param.
# @param param3 Third param.
function foo(Integer $param1, $param2, String $param3 = hi) {
notice world
}
SOURCE
}
it 'should parse the puppet function statement' do
subject.parse
expect(subject.enumerator.size).to eq(1)
statement = subject.enumerator.first
expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::FunctionStatement)
expect(statement.name).to eq('foo')
expect(statement.source).to eq("function foo(Integer $param1, $param2, String $param3 = hi) {\n notice world\n}")
expect(statement.file).to eq(file)
expect(statement.line).to eq(6)
expect(statement.docstring).to eq('A simple foo function.')
expect(statement.parameters.size).to eq(3)
expect(statement.parameters[0].name).to eq('param1')
expect(statement.parameters[0].type).to eq('Integer')
expect(statement.parameters[0].value).to be_nil
expect(statement.parameters[1].name).to eq('param2')
expect(statement.parameters[1].type).to be_nil
expect(statement.parameters[1].value).to be_nil
expect(statement.parameters[2].name).to eq('param3')
expect(statement.parameters[2].type).to eq('String')
expect(statement.parameters[2].value).to eq('hi')
end
end
end