summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2015-12-02 19:37:36 -0800
committerLamont Granquist <lamont@scriptkiddie.org>2015-12-02 19:37:36 -0800
commit98d553ced4bde95210fac82006c9be24ddb8f817 (patch)
tree84354ce72351f2c7aacabd4bdee1be63addf2725
parent2a610564a8a42208adf2158a7c5c0657b243d58a (diff)
parent2046cb8e519817619472384296328f9cdcbb5c16 (diff)
downloadchef-98d553ced4bde95210fac82006c9be24ddb8f817.tar.gz
Merge pull request #4196 from chef/lcg/dpkg-multipackage
multipackage dpkg_package and bonus fixes
-rw-r--r--CHANGELOG.md1
-rw-r--r--RELEASE_NOTES.md6
-rw-r--r--lib/chef/mixin/get_source_from_package.rb9
-rw-r--r--lib/chef/mixin/subclass_directive.rb37
-rw-r--r--lib/chef/provider/package.rb41
-rw-r--r--lib/chef/provider/package/dpkg.rb179
-rw-r--r--lib/chef/resource/dpkg_package.rb7
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/README.Debian6
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/README.source10
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/changelog5
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.cron.d.ex4
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.default.ex10
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.doc-base.EX20
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/compat1
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/conffiles1
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/control15
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/copyright38
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/docs0
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/init.d.ex166
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/manpage.1.ex56
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/manpage.sgml.ex154
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/manpage.xml.ex291
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/menu.ex2
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/postinst.ex39
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/postrm.ex37
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/preinst.ex35
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/prerm.ex38
-rwxr-xr-xspec/data/apt/chef-integration-test2-1.0/debian/rules8
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/source/format1
-rw-r--r--spec/data/apt/chef-integration-test2-1.0/debian/watch.ex23
-rw-r--r--spec/data/apt/chef-integration-test2_1.0-1.debian.tar.gzbin0 -> 9737 bytes
-rw-r--r--spec/data/apt/chef-integration-test2_1.0-1.dsc20
-rw-r--r--spec/data/apt/chef-integration-test2_1.0-1_amd64.changes31
-rw-r--r--spec/data/apt/chef-integration-test2_1.0-1_amd64.debbin0 -> 2258 bytes
-rw-r--r--spec/data/apt/chef-integration-test2_1.0.orig.tar.gzbin0 -> 248 bytes
-rw-r--r--spec/functional/resource/dpkg_package_spec.rb336
-rw-r--r--spec/functional/resource/package_spec.rb2
-rw-r--r--spec/spec_helper.rb5
-rw-r--r--spec/support/platform_helpers.rb4
-rw-r--r--spec/unit/mixin/subclass_directive_spec.rb45
-rw-r--r--spec/unit/provider/package/dpkg_spec.rb79
-rw-r--r--spec/unit/provider/package_spec.rb14
42 files changed, 1662 insertions, 114 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6919283413..899fa979e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,7 @@
* [**Nolan Davidson**](https://github.com/nsdavidson)
[pr#4014](https://github.com/chef/chef/pull/4014) Adding ksh resource
+* [pr#4196](https://github.com/chef/chef/pull/4196) multipackage dpkg_package and bonus fixes
* [pr#4185](https://github.com/chef/chef/pull/4185) dpkg provider cleanup
* [pr#4165](https://github.com/chef/chef/pull/4165) Multipackage internal API improvements
* [pr#4081](https://github.com/chef/chef/pull/4081) RFC-037: add `chef_version` and `ohai_version` metadata
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index c27984101b..44dcc64894 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -34,4 +34,10 @@ not designed to be a general solution to performance issues inside of chef-clien
This debugging feature will mostly be useful to people who are already Ruby experts.
+## `dpkg_package` now accepts an array of packages
+Similar to the `yum_package` and `apt_package` resources, the `dpkg_package` resource now handles an Array of package names (and
+also array of versions and array of sources).
+
+Some edge conditions in the `:remove` and `:purge` actions in `dpkg_package` were also fixed and the `:purge` action will now
+purge packages that were previously removed (`apt_package` still does not do this).
diff --git a/lib/chef/mixin/get_source_from_package.rb b/lib/chef/mixin/get_source_from_package.rb
index 2ed251854a..cb5583b431 100644
--- a/lib/chef/mixin/get_source_from_package.rb
+++ b/lib/chef/mixin/get_source_from_package.rb
@@ -1,5 +1,5 @@
# Author:: Lamont Granquist (<lamont@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,12 @@
class Chef
module Mixin
module GetSourceFromPackage
+ # FIXME: this is some bad code that I wrote a long time ago.
+ # - it does too much in the initializer
+ # - it mutates the new_resource
+ # - it does not support multipackage arrays
+ # this code is deprecated, check out the :use_package_names_for_source
+ # subclass directive instead
def initialize(new_resource, run_context)
super
return if new_resource.package_name.is_a?(Array)
@@ -40,4 +46,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/mixin/subclass_directive.rb b/lib/chef/mixin/subclass_directive.rb
new file mode 100644
index 0000000000..0f386b6cb2
--- /dev/null
+++ b/lib/chef/mixin/subclass_directive.rb
@@ -0,0 +1,37 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ module Mixin
+ module SubclassDirective
+ def subclass_directive(sym)
+ define_singleton_method sym do
+ instance_variable_set(:"@#{sym}", true)
+ end
+
+ define_singleton_method :"#{sym}?" do
+ !!instance_variable_get(:"@#{sym}")
+ end
+
+ define_method :"#{sym}?" do
+ self.class.send(:"#{sym}?")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 888b171fa4..abb181c571 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -18,6 +18,7 @@
require 'chef/mixin/shell_out'
require 'chef/mixin/command'
+require 'chef/mixin/subclass_directive'
require 'chef/log'
require 'chef/file_cache'
require 'chef/platform'
@@ -27,6 +28,12 @@ class Chef
class Package < Chef::Provider
include Chef::Mixin::Command
include Chef::Mixin::ShellOut
+ extend Chef::Mixin::SubclassDirective
+
+ # subclasses declare this if they want all their arguments as arrays of packages and names
+ subclass_directive :use_multipackage_api
+ # subclasses declare this if they want sources (filenames) pulled from their package names
+ subclass_directive :use_package_name_for_source
#
# Hook that subclasses use to populate the candidate_version(s)
@@ -44,6 +51,8 @@ class Chef
end
def check_resource_semantics!
+ # FIXME: this is not universally true and subclasses are needing to override this and no-ops it. It should be turned into
+ # another "subclass_directive" and the apt and yum providers should declare that they need this behavior.
if new_resource.package_name.is_a?(Array) && new_resource.source != nil
raise Chef::Exceptions::InvalidResourceSpecification, "You may not specify both multipackage and source"
end
@@ -211,7 +220,7 @@ class Chef
# @todo use composition rather than inheritance
def multipackage_api_adapter(name, version)
- if supports_arrays?
+ if use_multipackage_api?
yield [name].flatten, [version].flatten
else
yield name, version
@@ -242,7 +251,7 @@ class Chef
raise( Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reconfig" )
end
- # this is heavily used by subclasses
+ # used by subclasses. deprecated. use #a_to_s instead.
def expand_options(options)
options ? " #{options}" : ""
end
@@ -298,24 +307,6 @@ class Chef
[ thing ].flatten
end
- class << self
- attr_accessor :supports_arrays
-
- def supports_arrays?
- !!@supports_arrays
- end
-
- private
-
- def use_multipackage_api
- @supports_arrays = true
- end
- end
-
- def supports_arrays?
- self.class.supports_arrays?
- end
-
private
# Returns the package names which need to be modified. If the resource was called with an array of packages
@@ -528,6 +519,16 @@ class Chef
end
args
end
+
+ # Helper for sublcasses to convert an array of string args into a string. It
+ # will compact nil or empty strings in the array and will join the array elements
+ # with spaces, without introducing any double spaces for nil/empty elements.
+ #
+ # @param args [String] variable number of string arguments
+ # @return [String] nicely concatenated string or empty string
+ def a_to_s(*args)
+ args.reject {|i| i.nil? || i == "" }.join(" ")
+ end
end
end
end
diff --git a/lib/chef/provider/package/dpkg.rb b/lib/chef/provider/package/dpkg.rb
index 2de6226bb9..35b6f4beee 100644
--- a/lib/chef/provider/package/dpkg.rb
+++ b/lib/chef/provider/package/dpkg.rb
@@ -19,31 +19,40 @@
require 'chef/provider/package'
require 'chef/mixin/command'
require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
class Chef
class Provider
class Package
class Dpkg < Chef::Provider::Package
+ DPKG_REMOVED = /^Status: deinstall ok config-files/
DPKG_INSTALLED = /^Status: install ok installed/
DPKG_VERSION = /^Version: (.+)$/
provides :dpkg_package, os: "linux"
- include Chef::Mixin::GetSourceFromPackage
+ use_multipackage_api
+ use_package_name_for_source
+
+ # semantics of dpkg properties:
+ #
+ # new_resource.name is always an array for this resource
+ # new_resource.package_name is always an array for this resource
+ # new_resource.source is always an array and may be [ nil ] for this resource. properly use #sources or
+ # #name_sources to also get the automatic package-name-to-source-conversion. this will never be nil?
+ #
def define_resource_requirements
super
requirements.assert(:install, :upgrade) do |a|
- a.assertion { !new_resource.source.nil? }
+ a.assertion { !sources.compact.empty? }
a.failure_message Chef::Exceptions::Package, "#{new_resource} the source property is required for action :install or :upgrade"
end
requirements.assert(:install, :upgrade) do |a|
- a.assertion { source_file_exist? }
- a.failure_message Chef::Exceptions::Package, "#{new_resource} source file does not exist: #{new_resource.source}"
- a.whyrun "Assuming it would have been previously created."
+ a.assertion { source_files_exist? }
+ a.failure_message Chef::Exceptions::Package, "#{new_resource} source file(s) do not exist: #{missing_sources}"
+ a.whyrun "Assuming they would have been previously created."
end
end
@@ -51,11 +60,11 @@ class Chef
@current_resource = Chef::Resource::Package.new(new_resource.name)
current_resource.package_name(new_resource.package_name)
- if source_file_exist?
+ if source_files_exist?
@candidate_version = get_candidate_version
current_resource.package_name(get_package_name)
# if the source file exists then our package_name is right
- current_resource.version(get_current_version)
+ current_resource.version(get_current_version_from(current_package_name_array))
elsif !installing?
# we can't do this if we're installing with no source, because our package_name
# is probably not right.
@@ -65,31 +74,26 @@ class Chef
#
# we don't error here on the dpkg command since we'll handle the exception or
# the why-run message in define_resource_requirements.
- current_resource.version(get_current_version)
+ current_resource.version(get_current_version_from(current_package_name_array))
end
current_resource
end
def install_package(name, version)
- Chef::Log.info("#{new_resource} installing #{new_resource.source}")
- run_noninteractive(
- "dpkg -i#{expand_options(new_resource.options)} #{new_resource.source}"
- )
+ sources = name.map { |n| name_sources[n] }
+ Chef::Log.info("#{new_resource} installing package(s): #{name.join(' ')}")
+ run_noninteractive("dpkg -i", new_resource.options, *sources)
end
def remove_package(name, version)
- Chef::Log.info("#{new_resource} removing #{new_resource.package_name}")
- run_noninteractive(
- "dpkg -r#{expand_options(new_resource.options)} #{new_resource.package_name}"
- )
+ Chef::Log.info("#{new_resource} removing package(s): #{name.join(' ')}")
+ run_noninteractive("dpkg -r", new_resource.options, *name)
end
def purge_package(name, version)
- Chef::Log.info("#{new_resource} purging #{new_resource.package_name}")
- run_noninteractive(
- "dpkg -P#{expand_options(new_resource.options)} #{new_resource.package_name}"
- )
+ Chef::Log.info("#{new_resource} purging packages(s): #{name.join(' ')}")
+ run_noninteractive("dpkg -P", new_resource.options, *name)
end
def upgrade_package(name, version)
@@ -98,22 +102,29 @@ class Chef
def preseed_package(preseed_file)
Chef::Log.info("#{new_resource} pre-seeding package installation instructions")
- run_noninteractive("debconf-set-selections #{preseed_file}")
+ run_noninteractive("debconf-set-selections", *preseed_file)
end
def reconfig_package(name, version)
Chef::Log.info("#{new_resource} reconfiguring")
- run_noninteractive("dpkg-reconfigure #{name}")
+ run_noninteractive("dpkg-reconfigure", *name)
+ end
+
+ # Override the superclass check. Multiple sources are required here.
+ def check_resource_semantics!
end
private
- def get_current_version
- Chef::Log.debug("#{new_resource} checking install state")
- status = shell_out_with_timeout("dpkg -s #{current_resource.package_name}")
+ def read_current_version_of_package(package_name)
+ Chef::Log.debug("#{new_resource} checking install state of #{package_name}")
+ status = shell_out_with_timeout("dpkg -s #{package_name}")
package_installed = false
status.stdout.each_line do |line|
case line
+ when DPKG_REMOVED
+ # if we are 'purging' then we consider 'removed' to be 'installed'
+ package_installed = true if action == :purge
when DPKG_INSTALLED
package_installed = true
when DPKG_VERSION
@@ -126,34 +137,124 @@ class Chef
return nil
end
+ def get_current_version_from(array)
+ array.map do |name|
+ read_current_version_of_package(name)
+ end
+ end
+
# Runs command via shell_out_with_timeout with magic environment to disable
- # interactive prompts. Command is run with default localization rather
- # than forcing locale to "C", so command output may not be stable.
- def run_noninteractive(command)
- shell_out_with_timeout!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive" })
+ # interactive prompts.
+ def run_noninteractive(*command)
+ shell_out_with_timeout!(a_to_s(*command), :env => { "DEBIAN_FRONTEND" => "noninteractive" })
+ end
+
+ # Returns true if all sources exist. Returns false if any do not, or if no
+ # sources were specified.
+ #
+ # @return [Boolean] True if all sources exist
+ def source_files_exist?
+ sources.all? {|s| s && ::File.exist?(s) }
end
- def source_file_exist?
- new_resource.source && ::File.exist?(new_resource.source)
+ # Helper to return all the nanes of the missing sources for error messages.
+ #
+ # @return [Array<String>] Array of missing sources
+ def missing_sources
+ sources.select {|s| s.nil? || !::File.exist?(s) }
+ end
+
+ def current_package_name_array
+ [ current_resource.package_name ].flatten
+ end
+
+ def source_array
+ if new_resource.source.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.source ].flatten
+ end
+ end
+
+ # Helper to construct Array of sources. If the new_resource.source is nil it
+ # will return an array filled will nil the same size as the package_name array
+ # For all the nil source values, if a file exists on the filesystem that
+ # matches the package name it will use that name as the source.
+ #
+ # @return [Array] Array of normalized sources with package_names converted to sources
+ def sources
+ @sources ||=
+ begin
+ source_array.each_with_index.map do |source, i|
+ package_name = package_name_array[i]
+ # we require at least one '/' in the package_name to avoid dpkg_package 'foo' breaking due to a random 'foo' file in cwd
+ if use_package_name_for_source? && source.nil? && package_name.match(/#{::File::SEPARATOR}/) && ::File.exist?(package_name)
+ Chef::Log.debug("No package source specified, but #{package_name} exists on filesystem, using #{package_name} as source.")
+ package_name
+ else
+ source
+ end
+ end
+ end
+ end
+
+ # Helper to construct Hash of names-to-sources.
+ #
+ # @return [Hash] Mapping of package names to sources
+ def name_sources
+ @name_sources =
+ begin
+ Hash[*package_name_array.zip(sources).flatten]
+ end
+ end
+
+ # Helper to construct Hash of names-to-package-information.
+ #
+ # @return [Hash] Mapping of package names to package information
+ def name_pkginfo
+ @name_pkginfo ||=
+ begin
+ pkginfos = sources.map do |src|
+ Chef::Log.debug("#{new_resource} checking #{src} dpkg status")
+ status = shell_out_with_timeout!("dpkg-deb -W #{src}")
+ status.stdout
+ end
+ Hash[*package_name_array.zip(pkginfos).flatten]
+ end
+ end
+
+ def name_candidate_version
+ @name_candidate_version ||=
+ begin
+ Hash[name_pkginfo.map {|k, v| [k, v ? v.split("\t")[1].strip : nil] }]
+ end
end
- def pkginfo
- @pkginfo ||=
+ def name_package_name
+ @name_package_name ||=
begin
- Chef::Log.debug("#{new_resource} checking dpkg status")
- status = shell_out_with_timeout!("dpkg-deb -W #{new_resource.source}")
- status.stdout.split("\t")
+ Hash[name_pkginfo.map {|k, v| [k, v ? v.split("\t")[0] : nil] }]
end
end
+ # Return candidate version array from pkg-deb -W against the source file(s).
+ #
+ # @return [Array] Array of candidate versions read from the source files
def get_candidate_version
- pkginfo[1].strip unless pkginfo.empty?
+ package_name_array.map { |name| name_candidate_version[name] }
end
+ # Return package names from the candidate source file(s).
+ #
+ # @return [Array] Array of actual package names read from the source files
def get_package_name
- pkginfo[0] unless pkginfo.empty?
+ package_name_array.map { |name| name_package_name[name] }
end
+ # Since upgrade just calls install, this is a helper to determine
+ # if our action means that we'll be calling install_package.
+ #
+ # @return [Boolean] true if we're doing :install or :upgrade
def installing?
[:install, :upgrade].include?(action)
end
diff --git a/lib/chef/resource/dpkg_package.rb b/lib/chef/resource/dpkg_package.rb
index 38adf24cf6..9288c18632 100644
--- a/lib/chef/resource/dpkg_package.rb
+++ b/lib/chef/resource/dpkg_package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,14 +17,15 @@
#
require 'chef/resource/package'
-require 'chef/provider/package/dpkg'
class Chef
class Resource
class DpkgPackage < Chef::Resource::Package
-
provides :dpkg_package, os: "linux"
+ resource_name :dpkg_package
+
+ property :source, [String, Array, nil]
end
end
end
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/README.Debian b/spec/data/apt/chef-integration-test2-1.0/debian/README.Debian
new file mode 100644
index 0000000000..13f5dff153
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/README.Debian
@@ -0,0 +1,6 @@
+chef-integration-test2 for Debian
+---------------------------------
+
+<possible notes regarding this package - if none, delete this file>
+
+ -- Lamont Granquist <lamont@coredump.scriptkiddie.lan> Mon, 23 Nov 2015 12:57:22 -0800
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/README.source b/spec/data/apt/chef-integration-test2-1.0/debian/README.source
new file mode 100644
index 0000000000..086a592106
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/README.source
@@ -0,0 +1,10 @@
+chef-integration-test2 for Debian
+---------------------------------
+
+<this file describes information about the source package, see Debian policy
+manual section 4.14. You WILL either need to modify or delete this file>
+
+
+
+ -- Lamont Granquist <lamont@coredump.scriptkiddie.lan> Mon, 23 Nov 2015 12:57:22 -0800
+
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/changelog b/spec/data/apt/chef-integration-test2-1.0/debian/changelog
new file mode 100644
index 0000000000..63e92728de
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/changelog
@@ -0,0 +1,5 @@
+chef-integration-test2 (1.0-1) unstable; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+
+ -- Lamont Granquist <lamont@coredump.scriptkiddie.lan> Mon, 23 Nov 2015 12:57:22 -0800
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.cron.d.ex b/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.cron.d.ex
new file mode 100644
index 0000000000..7590a38aa3
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.cron.d.ex
@@ -0,0 +1,4 @@
+#
+# Regular cron jobs for the chef-integration-test2 package
+#
+0 4 * * * root [ -x /usr/bin/chef-integration-test2_maintenance ] && /usr/bin/chef-integration-test2_maintenance
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.default.ex b/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.default.ex
new file mode 100644
index 0000000000..b5ba987ece
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.default.ex
@@ -0,0 +1,10 @@
+# Defaults for chef-integration-test2 initscript
+# sourced by /etc/init.d/chef-integration-test2
+# installed at /etc/default/chef-integration-test2 by the maintainer scripts
+
+#
+# This is a POSIX shell fragment
+#
+
+# Additional options that are passed to the Daemon.
+DAEMON_OPTS=""
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.doc-base.EX b/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.doc-base.EX
new file mode 100644
index 0000000000..84c8f9dd28
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/chef-integration-test2.doc-base.EX
@@ -0,0 +1,20 @@
+Document: chef-integration-test2
+Title: Debian chef-integration-test2 Manual
+Author: <insert document author here>
+Abstract: This manual describes what chef-integration-test2 is
+ and how it can be used to
+ manage online manuals on Debian systems.
+Section: unknown
+
+Format: debiandoc-sgml
+Files: /usr/share/doc/chef-integration-test2/chef-integration-test2.sgml.gz
+
+Format: postscript
+Files: /usr/share/doc/chef-integration-test2/chef-integration-test2.ps.gz
+
+Format: text
+Files: /usr/share/doc/chef-integration-test2/chef-integration-test2.text.gz
+
+Format: HTML
+Index: /usr/share/doc/chef-integration-test2/html/index.html
+Files: /usr/share/doc/chef-integration-test2/html/*.html
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/compat b/spec/data/apt/chef-integration-test2-1.0/debian/compat
new file mode 100644
index 0000000000..ec635144f6
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/conffiles b/spec/data/apt/chef-integration-test2-1.0/debian/conffiles
new file mode 100644
index 0000000000..ac4307eadf
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/conffiles
@@ -0,0 +1 @@
+/usr/share/doc/chef-integration-test2/copyright
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/control b/spec/data/apt/chef-integration-test2-1.0/debian/control
new file mode 100644
index 0000000000..f8b66fcb5a
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/control
@@ -0,0 +1,15 @@
+Source: chef-integration-test2
+Section: unknown
+Priority: optional
+Maintainer: Lamont Granquist <lamont@coredump.scriptkiddie.lan>
+Build-Depends: debhelper (>= 8.0.0)
+Standards-Version: 3.9.4
+Homepage: <insert the upstream URL, if relevant>
+#Vcs-Git: git://git.debian.org/collab-maint/chef-integration-test2.git
+#Vcs-Browser: http://git.debian.org/?p=collab-maint/chef-integration-test2.git;a=summary
+
+Package: chef-integration-test2
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: <insert up to 60 chars description>
+ <insert long description, indented with spaces>
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/copyright b/spec/data/apt/chef-integration-test2-1.0/debian/copyright
new file mode 100644
index 0000000000..dd891763ec
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/copyright
@@ -0,0 +1,38 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: chef-integration-test2
+Source: <url://example.com>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+ <years> <likewise for another author>
+License: <special license>
+ <Put the license of the package here indented by 1 space>
+ <This follows the format of Description: lines in control file>
+ .
+ <Including paragraphs>
+
+# If you want to use GPL v2 or later for the /debian/* files use
+# the following clauses, or change it to suit. Delete these two lines
+Files: debian/*
+Copyright: 2015 Lamont Granquist <lamont@coredump.scriptkiddie.lan>
+License: GPL-2+
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
+# Please avoid to pick license terms that are more restrictive than the
+# packaged work, as it may make Debian's contributions unacceptable upstream.
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/docs b/spec/data/apt/chef-integration-test2-1.0/debian/docs
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/docs
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/init.d.ex b/spec/data/apt/chef-integration-test2-1.0/debian/init.d.ex
new file mode 100644
index 0000000000..a776222376
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/init.d.ex
@@ -0,0 +1,166 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: chef-integration-test2
+# Required-Start: $local_fs $network $remote_fs $syslog
+# Required-Stop: $local_fs $network $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: <Enter a short description of the software>
+# Description: <Enter a long description of the software>
+# <...>
+# <...>
+### END INIT INFO
+
+# Author: Lamont Granquist <lamont@coredump.scriptkiddie.lan>
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="chef-integration-test2"
+NAME=chef-integration-test2
+DAEMON=/usr/sbin/chef-integration-test2
+DAEMON_ARGS=""
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
+# and status_of_proc is working.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ $DAEMON_ARGS \
+ || return 2
+ # The above code will not work for interpreted scripts, use the next
+ # six lines below instead (Ref: #643337, start-stop-daemon(8) )
+ #start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON \
+ # --name $NAME --test > /dev/null \
+ # || return 1
+ #start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON \
+ # --name $NAME -- $DAEMON_ARGS \
+ # || return 2
+
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ status)
+ status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+ ;;
+ #reload|force-reload)
+ #
+ # If do_reload() is not implemented then leave this commented out
+ # and leave 'force-reload' as an alias for 'restart'.
+ #
+ #log_daemon_msg "Reloading $DESC" "$NAME"
+ #do_reload
+ #log_end_msg $?
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented then remove the
+ # 'force-reload' alias
+ #
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/manpage.1.ex b/spec/data/apt/chef-integration-test2-1.0/debian/manpage.1.ex
new file mode 100644
index 0000000000..b05b3bc906
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/manpage.1.ex
@@ -0,0 +1,56 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" (C) Copyright 2015 Lamont Granquist <lamont@coredump.scriptkiddie.lan>,
+.\"
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH CHEF-INTEGRATION-TEST2 SECTION "November 23, 2015"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp <n> insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+chef-integration-test2 \- program to do something
+.SH SYNOPSIS
+.B chef-integration-test2
+.RI [ options ] " files" ...
+.br
+.B bar
+.RI [ options ] " files" ...
+.SH DESCRIPTION
+This manual page documents briefly the
+.B chef-integration-test2
+and
+.B bar
+commands.
+.PP
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
+.\" respectively.
+\fBchef-integration-test2\fP is a program that...
+.SH OPTIONS
+These programs follow the usual GNU command line syntax, with long
+options starting with two dashes (`-').
+A summary of options is included below.
+For a complete description, see the Info files.
+.TP
+.B \-h, \-\-help
+Show summary of options.
+.TP
+.B \-v, \-\-version
+Show version of program.
+.SH SEE ALSO
+.BR bar (1),
+.BR baz (1).
+.br
+The programs are documented fully by
+.IR "The Rise and Fall of a Fooish Bar" ,
+available via the Info system.
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/manpage.sgml.ex b/spec/data/apt/chef-integration-test2-1.0/debian/manpage.sgml.ex
new file mode 100644
index 0000000000..f74dbe0b86
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/manpage.sgml.ex
@@ -0,0 +1,154 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
+
+<!-- Process this file with docbook-to-man to generate an nroff manual
+ page: `docbook-to-man manpage.sgml > manpage.1'. You may view
+ the manual page with: `docbook-to-man manpage.sgml | nroff -man |
+ less'. A typical entry in a Makefile or Makefile.am is:
+
+manpage.1: manpage.sgml
+ docbook-to-man $< > $@
+
+
+ The docbook-to-man binary is found in the docbook-to-man package.
+ Please remember that if you create the nroff version in one of the
+ debian/rules file targets (such as build), you will need to include
+ docbook-to-man in your Build-Depends control field.
+
+ -->
+
+ <!-- Fill in your name for FIRSTNAME and SURNAME. -->
+ <!ENTITY dhfirstname "<firstname>FIRSTNAME</firstname>">
+ <!ENTITY dhsurname "<surname>SURNAME</surname>">
+ <!-- Please adjust the date whenever revising the manpage. -->
+ <!ENTITY dhdate "<date>November 23, 2015</date>">
+ <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+ allowed: see man(7), man(1). -->
+ <!ENTITY dhsection "<manvolnum>SECTION</manvolnum>">
+ <!ENTITY dhemail "<email>lamont@coredump.scriptkiddie.lan</email>">
+ <!ENTITY dhusername "Lamont Granquist">
+ <!ENTITY dhucpackage "<refentrytitle>CHEF-INTEGRATION-TEST2</refentrytitle>">
+ <!ENTITY dhpackage "chef-integration-test2">
+
+ <!ENTITY debian "<productname>Debian</productname>">
+ <!ENTITY gnu "<acronym>GNU</acronym>">
+ <!ENTITY gpl "&gnu; <acronym>GPL</acronym>">
+]>
+
+<refentry>
+ <refentryinfo>
+ <address>
+ &dhemail;
+ </address>
+ <author>
+ &dhfirstname;
+ &dhsurname;
+ </author>
+ <copyright>
+ <year>2003</year>
+ <holder>&dhusername;</holder>
+ </copyright>
+ &dhdate;
+ </refentryinfo>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+
+ <refpurpose>program to do something</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+
+ <arg><option>-e <replaceable>this</replaceable></option></arg>
+
+ <arg><option>--example <replaceable>that</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>This manual page documents briefly the
+ <command>&dhpackage;</command> and <command>bar</command>
+ commands.</para>
+
+ <para>This manual page was written for the &debian; distribution
+ because the original program does not have a manual page.
+ Instead, it has documentation in the &gnu;
+ <application>Info</application> format; see below.</para>
+
+ <para><command>&dhpackage;</command> is a program that...</para>
+
+ </refsect1>
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>These programs follow the usual &gnu; command line syntax,
+ with long options starting with two dashes (`-'). A summary of
+ options is included below. For a complete description, see the
+ <application>Info</application> files.</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>-h</option>
+ <option>--help</option>
+ </term>
+ <listitem>
+ <para>Show summary of options.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-v</option>
+ <option>--version</option>
+ </term>
+ <listitem>
+ <para>Show version of program.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>bar (1), baz (1).</para>
+
+ <para>The programs are documented fully by <citetitle>The Rise and
+ Fall of a Fooish Bar</citetitle> available via the
+ <application>Info</application> system.</para>
+ </refsect1>
+ <refsect1>
+ <title>AUTHOR</title>
+
+ <para>This manual page was written by &dhusername; &dhemail; for
+ the &debian; system (and may be used by others). Permission is
+ granted to copy, distribute and/or modify this document under
+ the terms of the &gnu; General Public License, Version 2 any
+ later version published by the Free Software Foundation.
+ </para>
+ <para>
+ On Debian systems, the complete text of the GNU General Public
+ License can be found in /usr/share/common-licenses/GPL.
+ </para>
+
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/manpage.xml.ex b/spec/data/apt/chef-integration-test2-1.0/debian/manpage.xml.ex
new file mode 100644
index 0000000000..4ad8c86fc8
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/manpage.xml.ex
@@ -0,0 +1,291 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+
+<!--
+
+`xsltproc -''-nonet \
+ -''-param man.charmap.use.subset "0" \
+ -''-param make.year.ranges "1" \
+ -''-param make.single.year.ranges "1" \
+ /usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \
+ manpage.xml'
+
+A manual page <package>.<section> will be generated. You may view the
+manual page with: nroff -man <package>.<section> | less'. A typical entry
+in a Makefile or Makefile.am is:
+
+DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/docbook-xsl/manpages/docbook.xsl
+XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0"
+
+manpage.1: manpage.xml
+ $(XP) $(DB2MAN) $<
+
+The xsltproc binary is found in the xsltproc package. The XSL files are in
+docbook-xsl. A description of the parameters you can use can be found in the
+docbook-xsl-doc-* packages. Please remember that if you create the nroff
+version in one of the debian/rules file targets (such as build), you will need
+to include xsltproc and docbook-xsl in your Build-Depends control field.
+Alternatively use the xmlto command/package. That will also automatically
+pull in xsltproc and docbook-xsl.
+
+Notes for using docbook2x: docbook2x-man does not automatically create the
+AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as
+<refsect1> ... </refsect1>.
+
+To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections
+read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be
+found in the docbook-xsl-doc-html package.
+
+Validation can be done using: `xmllint -''-noout -''-valid manpage.xml`
+
+General documentation about man-pages and man-page-formatting:
+man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
+
+-->
+
+ <!-- Fill in your name for FIRSTNAME and SURNAME. -->
+ <!ENTITY dhfirstname "FIRSTNAME">
+ <!ENTITY dhsurname "SURNAME">
+ <!-- dhusername could also be set to "&dhfirstname; &dhsurname;". -->
+ <!ENTITY dhusername "Lamont Granquist">
+ <!ENTITY dhemail "lamont@coredump.scriptkiddie.lan">
+ <!-- SECTION should be 1-8, maybe w/ subsection other parameters are
+ allowed: see man(7), man(1) and
+ http://www.tldp.org/HOWTO/Man-Page/q2.html. -->
+ <!ENTITY dhsection "SECTION">
+ <!-- TITLE should be something like "User commands" or similar (see
+ http://www.tldp.org/HOWTO/Man-Page/q2.html). -->
+ <!ENTITY dhtitle "chef-integration-test2 User Manual">
+ <!ENTITY dhucpackage "CHEF-INTEGRATION-TEST2">
+ <!ENTITY dhpackage "chef-integration-test2">
+]>
+
+<refentry>
+ <refentryinfo>
+ <title>&dhtitle;</title>
+ <productname>&dhpackage;</productname>
+ <authorgroup>
+ <author>
+ <firstname>&dhfirstname;</firstname>
+ <surname>&dhsurname;</surname>
+ <contrib>Wrote this manpage for the Debian system.</contrib>
+ <address>
+ <email>&dhemail;</email>
+ </address>
+ </author>
+ </authorgroup>
+ <copyright>
+ <year>2007</year>
+ <holder>&dhusername;</holder>
+ </copyright>
+ <legalnotice>
+ <para>This manual page was written for the Debian system
+ (and may be used by others).</para>
+ <para>Permission is granted to copy, distribute and/or modify this
+ document under the terms of the GNU General Public License,
+ Version 2 or (at your option) any later version published by
+ the Free Software Foundation.</para>
+ <para>On Debian systems, the complete text of the GNU General Public
+ License can be found in
+ <filename>/usr/share/common-licenses/GPL</filename>.</para>
+ </legalnotice>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>&dhucpackage;</refentrytitle>
+ <manvolnum>&dhsection;</manvolnum>
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+ <refpurpose>program to do something</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <!-- These are several examples, how syntaxes could look -->
+ <arg choice="plain"><option>-e <replaceable>this</replaceable></option></arg>
+ <arg choice="opt"><option>--example=<parameter>that</parameter></option></arg>
+ <arg choice="opt">
+ <group choice="req">
+ <arg choice="plain"><option>-e</option></arg>
+ <arg choice="plain"><option>--example</option></arg>
+ </group>
+ <replaceable class="option">this</replaceable>
+ </arg>
+ <arg choice="opt">
+ <group choice="req">
+ <arg choice="plain"><option>-e</option></arg>
+ <arg choice="plain"><option>--example</option></arg>
+ </group>
+ <group choice="req">
+ <arg choice="plain"><replaceable>this</replaceable></arg>
+ <arg choice="plain"><replaceable>that</replaceable></arg>
+ </group>
+ </arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <!-- Normally the help and version options make the programs stop
+ right after outputting the requested information. -->
+ <group choice="opt">
+ <arg choice="plain">
+ <group choice="req">
+ <arg choice="plain"><option>-h</option></arg>
+ <arg choice="plain"><option>--help</option></arg>
+ </group>
+ </arg>
+ <arg choice="plain">
+ <group choice="req">
+ <arg choice="plain"><option>-v</option></arg>
+ <arg choice="plain"><option>--version</option></arg>
+ </group>
+ </arg>
+ </group>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1 id="description">
+ <title>DESCRIPTION</title>
+ <para>This manual page documents briefly the
+ <command>&dhpackage;</command> and <command>bar</command>
+ commands.</para>
+ <para>This manual page was written for the Debian distribution
+ because the original program does not have a manual page.
+ Instead, it has documentation in the GNU <citerefentry>
+ <refentrytitle>info</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry> format; see below.</para>
+ <para><command>&dhpackage;</command> is a program that...</para>
+ </refsect1>
+ <refsect1 id="options">
+ <title>OPTIONS</title>
+ <para>The program follows the usual GNU command line syntax,
+ with long options starting with two dashes (`-'). A summary of
+ options is included below. For a complete description, see the
+ <citerefentry>
+ <refentrytitle>info</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry> files.</para>
+ <variablelist>
+ <!-- Use the variablelist.term.separator and the
+ variablelist.term.break.after parameters to
+ control the term elements. -->
+ <varlistentry>
+ <term><option>-e <replaceable>this</replaceable></option></term>
+ <term><option>--example=<replaceable>that</replaceable></option></term>
+ <listitem>
+ <para>Does this and that.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-h</option></term>
+ <term><option>--help</option></term>
+ <listitem>
+ <para>Show summary of options.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>-v</option></term>
+ <term><option>--version</option></term>
+ <listitem>
+ <para>Show version of program.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1 id="files">
+ <title>FILES</title>
+ <variablelist>
+ <varlistentry>
+ <term><filename>/etc/foo.conf</filename></term>
+ <listitem>
+ <para>The system-wide configuration file to control the
+ behaviour of <application>&dhpackage;</application>. See
+ <citerefentry>
+ <refentrytitle>foo.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry> for further details.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename>${HOME}/.foo.conf</filename></term>
+ <listitem>
+ <para>The per-user configuration file to control the
+ behaviour of <application>&dhpackage;</application>. See
+ <citerefentry>
+ <refentrytitle>foo.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry> for further details.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1 id="environment">
+ <title>ENVIRONMENT</title>
+ <variablelist>
+ <varlistentry>
+ <term><envar>FOO_CONF</envar></term>
+ <listitem>
+ <para>If used, the defined file is used as configuration
+ file (see also <xref linkend="files"/>).</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1 id="diagnostics">
+ <title>DIAGNOSTICS</title>
+ <para>The following diagnostics may be issued
+ on <filename class="devicefile">stderr</filename>:</para>
+ <variablelist>
+ <varlistentry>
+ <term><errortext>Bad configuration file. Exiting.</errortext></term>
+ <listitem>
+ <para>The configuration file seems to contain a broken configuration
+ line. Use the <option>--verbose</option> option, to get more info.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para><command>&dhpackage;</command> provides some return codes, that can
+ be used in scripts:</para>
+ <segmentedlist>
+ <segtitle>Code</segtitle>
+ <segtitle>Diagnostic</segtitle>
+ <seglistitem>
+ <seg><errorcode>0</errorcode></seg>
+ <seg>Program exited successfully.</seg>
+ </seglistitem>
+ <seglistitem>
+ <seg><errorcode>1</errorcode></seg>
+ <seg>The configuration file seems to be broken.</seg>
+ </seglistitem>
+ </segmentedlist>
+ </refsect1>
+ <refsect1 id="bugs">
+ <!-- Or use this section to tell about upstream BTS. -->
+ <title>BUGS</title>
+ <para>The program is currently limited to only work
+ with the <package>foobar</package> library.</para>
+ <para>The upstreams <acronym>BTS</acronym> can be found
+ at <ulink url="http://bugzilla.foo.tld"/>.</para>
+ </refsect1>
+ <refsect1 id="see_also">
+ <title>SEE ALSO</title>
+ <!-- In alpabetical order. -->
+ <para><citerefentry>
+ <refentrytitle>bar</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>, <citerefentry>
+ <refentrytitle>baz</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>, <citerefentry>
+ <refentrytitle>foo.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry></para>
+ <para>The programs are documented fully by <citetitle>The Rise and
+ Fall of a Fooish Bar</citetitle> available via the <citerefentry>
+ <refentrytitle>info</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry> system.</para>
+ </refsect1>
+</refentry>
+
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/menu.ex b/spec/data/apt/chef-integration-test2-1.0/debian/menu.ex
new file mode 100644
index 0000000000..6d075924cf
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/menu.ex
@@ -0,0 +1,2 @@
+?package(chef-integration-test2):needs="X11|text|vc|wm" section="Applications/see-menu-manual"\
+ title="chef-integration-test2" command="/usr/bin/chef-integration-test2"
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/postinst.ex b/spec/data/apt/chef-integration-test2-1.0/debian/postinst.ex
new file mode 100644
index 0000000000..6604d5f775
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/postinst.ex
@@ -0,0 +1,39 @@
+#!/bin/sh
+# postinst script for chef-integration-test2
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/postrm.ex b/spec/data/apt/chef-integration-test2-1.0/debian/postrm.ex
new file mode 100644
index 0000000000..7cc62c867b
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/postrm.ex
@@ -0,0 +1,37 @@
+#!/bin/sh
+# postrm script for chef-integration-test2
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/preinst.ex b/spec/data/apt/chef-integration-test2-1.0/debian/preinst.ex
new file mode 100644
index 0000000000..d871e0e9b0
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/preinst.ex
@@ -0,0 +1,35 @@
+#!/bin/sh
+# preinst script for chef-integration-test2
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade)
+ ;;
+
+ abort-upgrade)
+ ;;
+
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/prerm.ex b/spec/data/apt/chef-integration-test2-1.0/debian/prerm.ex
new file mode 100644
index 0000000000..0fb0dc480c
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/prerm.ex
@@ -0,0 +1,38 @@
+#!/bin/sh
+# prerm script for chef-integration-test2
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <prerm> `remove'
+# * <old-prerm> `upgrade' <new-version>
+# * <new-prerm> `failed-upgrade' <old-version>
+# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+# * <deconfigured's-prerm> `deconfigure' `in-favour'
+# <package-being-installed> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ remove|upgrade|deconfigure)
+ ;;
+
+ failed-upgrade)
+ ;;
+
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/rules b/spec/data/apt/chef-integration-test2-1.0/debian/rules
new file mode 100755
index 0000000000..79fd842dca
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+ dh $@
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/source/format b/spec/data/apt/chef-integration-test2-1.0/debian/source/format
new file mode 100644
index 0000000000..163aaf8d82
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/spec/data/apt/chef-integration-test2-1.0/debian/watch.ex b/spec/data/apt/chef-integration-test2-1.0/debian/watch.ex
new file mode 100644
index 0000000000..0c5a2e0c17
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2-1.0/debian/watch.ex
@@ -0,0 +1,23 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 3 file
+version=3
+
+# Uncomment to examine a Webpage
+# <Webpage URL> <string match>
+#http://www.example.com/downloads.php chef-integration-test2-(.*)\.tar\.gz
+
+# Uncomment to examine a Webserver directory
+#http://www.example.com/pub/chef-integration-test2-(.*)\.tar\.gz
+
+# Uncommment to examine a FTP server
+#ftp://ftp.example.com/pub/chef-integration-test2-(.*)\.tar\.gz debian uupdate
+
+# Uncomment to find new files on sourceforge, for devscripts >= 2.9
+# http://sf.net/chef-integration-test2/chef-integration-test2-(.*)\.tar\.gz
+
+# Uncomment to find new files on GooglePages
+# http://example.googlepages.com/foo.html chef-integration-test2-(.*)\.tar\.gz
diff --git a/spec/data/apt/chef-integration-test2_1.0-1.debian.tar.gz b/spec/data/apt/chef-integration-test2_1.0-1.debian.tar.gz
new file mode 100644
index 0000000000..d4abcfca62
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2_1.0-1.debian.tar.gz
Binary files differ
diff --git a/spec/data/apt/chef-integration-test2_1.0-1.dsc b/spec/data/apt/chef-integration-test2_1.0-1.dsc
new file mode 100644
index 0000000000..43e5ff1057
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2_1.0-1.dsc
@@ -0,0 +1,20 @@
+Format: 3.0 (quilt)
+Source: chef-integration-test2
+Binary: chef-integration-test2
+Architecture: any
+Version: 1.0-1
+Maintainer: Lamont Granquist <lamont@coredump.scriptkiddie.lan>
+Homepage: <insert the upstream URL, if relevant>
+Standards-Version: 3.9.4
+Build-Depends: debhelper (>= 8.0.0)
+Package-List:
+ chef-integration-test2 deb unknown optional
+Checksums-Sha1:
+ 755c304197c6559128aed206ea70643fec2bb90d 248 chef-integration-test2_1.0.orig.tar.gz
+ 147e8003463c5b54862a84367ab8f9559306da60 9737 chef-integration-test2_1.0-1.debian.tar.gz
+Checksums-Sha256:
+ 8b206a7b3d422290bc8d82bd700cb89f1c6e3962b96be6a3955c7a0159f9031c 248 chef-integration-test2_1.0.orig.tar.gz
+ b0b4f3423dc8934f41543352ab3f4bd3e647fdfc490f825426fd71103403d4bc 9737 chef-integration-test2_1.0-1.debian.tar.gz
+Files:
+ f1f7d7bbe63ad631d25d707f564a8d33 248 chef-integration-test2_1.0.orig.tar.gz
+ ad45e1006026dbbe60edcbb7520e9379 9737 chef-integration-test2_1.0-1.debian.tar.gz
diff --git a/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes b/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes
new file mode 100644
index 0000000000..2596a2b2ec
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2_1.0-1_amd64.changes
@@ -0,0 +1,31 @@
+Format: 1.8
+Date: Mon, 23 Nov 2015 12:57:22 -0800
+Source: chef-integration-test2
+Binary: chef-integration-test2
+Architecture: source amd64
+Version: 1.0-1
+Distribution: unstable
+Urgency: low
+Maintainer: Lamont Granquist <lamont@coredump.scriptkiddie.lan>
+Changed-By: Lamont Granquist <lamont@coredump.scriptkiddie.lan>
+Description:
+ chef-integration-test2 - <insert up to 60 chars description>
+Changes:
+ chef-integration-test2 (1.0-1) unstable; urgency=low
+ .
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+Checksums-Sha1:
+ 496f99763cf1363587a218530839b888c9f21352 950 chef-integration-test2_1.0-1.dsc
+ 755c304197c6559128aed206ea70643fec2bb90d 248 chef-integration-test2_1.0.orig.tar.gz
+ 147e8003463c5b54862a84367ab8f9559306da60 9737 chef-integration-test2_1.0-1.debian.tar.gz
+ 9f6e72d07f4e7f7b835e81bf34890ecfdec57b30 2258 chef-integration-test2_1.0-1_amd64.deb
+Checksums-Sha256:
+ 3535259d2a747b4c9595d86db41691cf7408d6cfe409fda2d11c538c19d39925 950 chef-integration-test2_1.0-1.dsc
+ 8b206a7b3d422290bc8d82bd700cb89f1c6e3962b96be6a3955c7a0159f9031c 248 chef-integration-test2_1.0.orig.tar.gz
+ b0b4f3423dc8934f41543352ab3f4bd3e647fdfc490f825426fd71103403d4bc 9737 chef-integration-test2_1.0-1.debian.tar.gz
+ 42df491d0a3c3a077b6c808fffc93557f9b4747b2443a1c1da16ddda873730a4 2258 chef-integration-test2_1.0-1_amd64.deb
+Files:
+ 67b83ba0dcd7b8a3e061c38b9d70d0af 950 unknown optional chef-integration-test2_1.0-1.dsc
+ f1f7d7bbe63ad631d25d707f564a8d33 248 unknown optional chef-integration-test2_1.0.orig.tar.gz
+ ad45e1006026dbbe60edcbb7520e9379 9737 unknown optional chef-integration-test2_1.0-1.debian.tar.gz
+ 9932a1f0074e1b218c057b7f7f7a1399 2258 unknown optional chef-integration-test2_1.0-1_amd64.deb
diff --git a/spec/data/apt/chef-integration-test2_1.0-1_amd64.deb b/spec/data/apt/chef-integration-test2_1.0-1_amd64.deb
new file mode 100644
index 0000000000..4856df5236
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2_1.0-1_amd64.deb
Binary files differ
diff --git a/spec/data/apt/chef-integration-test2_1.0.orig.tar.gz b/spec/data/apt/chef-integration-test2_1.0.orig.tar.gz
new file mode 100644
index 0000000000..18f7aa17d6
--- /dev/null
+++ b/spec/data/apt/chef-integration-test2_1.0.orig.tar.gz
Binary files differ
diff --git a/spec/functional/resource/dpkg_package_spec.rb b/spec/functional/resource/dpkg_package_spec.rb
new file mode 100644
index 0000000000..8d67a1fa03
--- /dev/null
+++ b/spec/functional/resource/dpkg_package_spec.rb
@@ -0,0 +1,336 @@
+#
+# Copyright:: Copyright (c) 2014-2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/shell_out'
+
+describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only do
+ include Chef::Mixin::ShellOut
+
+ let(:apt_data) { File.join(CHEF_SPEC_DATA, "apt") }
+
+ let(:test1_0) { File.join(apt_data, "chef-integration-test_1.0-1_amd64.deb") }
+ let(:test1_1) { File.join(apt_data, "chef-integration-test_1.1-1_amd64.deb") }
+ let(:test2_0) { File.join(apt_data, "chef-integration-test2_1.0-1_amd64.deb") }
+
+ let(:run_context) {
+ node = TEST_NODE.dup
+ events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, events)
+ }
+
+ let(:dpkg_package) { Chef::Resource::DpkgPackage.new(test1_0, run_context)}
+
+ before(:each) do
+ shell_out("dpkg -P chef-integration-test chef-integration-test2")
+ end
+
+ # handles setting the name property after the initializer runs
+ def set_dpkg_package_name(name)
+ dpkg_package.name name
+ dpkg_package.package_name name
+ end
+
+ def should_be_purged_or_removed(package, action)
+ if action == :purge
+ shell_out!("dpkg -s #{package}", returns: [1])
+ else
+ expect(shell_out("dpkg -s #{package}").stdout).to match(/deinstall ok config-files/)
+ end
+ end
+
+ shared_examples_for "common behavior for upgrade or install" do
+ it "installs a package when given only the filename as a name argument (no source)" do
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ end
+
+ it "installs a package when given the name and a source argument" do
+ set_dpkg_package_name "chef-integration-test"
+ dpkg_package.source test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ end
+
+ it "installs a package when given a different name and a source argument" do
+ set_dpkg_package_name "some other name"
+ dpkg_package.source test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ end
+
+ it "installs a package when given a path as a package_name and no source" do
+ set_dpkg_package_name "chef-integration-test"
+ dpkg_package.package_name test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ end
+
+ it "raises an error when the name is not a path and the source is not given" do
+ set_dpkg_package_name "chef-integration-test"
+ dpkg_package.package_name "chef-integration-test"
+ expect { dpkg_package.run_action(action) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "raises an error when passed a package_name that does not exist" do
+ set_dpkg_package_name File.join(test1_0, "make.it.fail")
+ expect { dpkg_package.run_action(action) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "raises an error when passed a source that does not exist" do
+ set_dpkg_package_name "chef-integration-test"
+ dpkg_package.source File.join(test1_0, "make.it.fail")
+ expect { dpkg_package.run_action(action) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "should not install an already installed package" do
+ shell_out!("dpkg -i #{test1_0}")
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ end
+
+ it "should handle a multipackage install" do
+ set_dpkg_package_name [ test1_0, test2_0 ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ shell_out!('dpkg -s chef-integration-test2')
+ end
+
+ it "should not update multipackages that are up-to-date" do
+ shell_out!("dpkg -i #{test1_0} #{test2_0}")
+ set_dpkg_package_name [ test1_0, test2_0 ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ shell_out!('dpkg -s chef-integration-test2')
+ end
+
+ it "should install the second if the first is installed" do
+ shell_out!("dpkg -i #{test1_0}")
+ set_dpkg_package_name [ test1_0, test2_0 ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ shell_out!('dpkg -s chef-integration-test2')
+ end
+
+ it "should install the first if the second is installed" do
+ shell_out!("dpkg -i #{test2_0}")
+ set_dpkg_package_name [ test1_0, test2_0 ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test')
+ shell_out!('dpkg -s chef-integration-test2')
+ end
+ end
+
+ context "action :install" do
+ let(:action) { :install }
+ it_behaves_like "common behavior for upgrade or install"
+
+ it "should not upgrade a package" do
+ shell_out!("dpkg -i #{test1_0}")
+ set_dpkg_package_name test1_1
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ end
+
+ it "should not upgrade on a multipackage install" do
+ shell_out!("dpkg -i #{test1_0} #{test2_0}")
+ set_dpkg_package_name [ test1_1, test2_0 ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ end
+ end
+
+ context "action :upgrade" do
+ let(:action) { :upgrade }
+ it_behaves_like "common behavior for upgrade or install"
+
+ it "should upgrade a package" do
+ shell_out!("dpkg -i #{test1_0}")
+ set_dpkg_package_name test1_1
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ end
+
+ it "should upgrade on a multipackage install" do
+ shell_out!("dpkg -i #{test1_0} #{test2_0}")
+ set_dpkg_package_name [ test1_1, test2_0 ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ end
+ end
+
+ shared_examples_for "common behavior for remove or purge" do
+ it "should remove a package that is installed when the name is a source" do
+ shell_out!("dpkg -i #{test1_0}")
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should do nothing if the package is not installed when the name is a source" do
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should remove a package that is installed when the name is the package name and source is nil" do
+ shell_out!("dpkg -i #{test1_0}")
+ set_dpkg_package_name "chef-integration-test"
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should do nothing if the package is not installed when the name is the package name and the source is nil" do
+ set_dpkg_package_name "chef-integration-test"
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should remove a package that is installed when the name is changed but the source is a package" do
+ shell_out!("dpkg -i #{test1_0}")
+ set_dpkg_package_name "some other name"
+ dpkg_package.source test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should do nothing if the package is not installed when the name is changed but the source is a package" do
+ set_dpkg_package_name "some other name"
+ dpkg_package.source test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should remove a package if the name is a file that does not exist, but the source exists" do
+ shell_out!("dpkg -i #{test1_0}")
+ dpkg_package.name "whatever"
+ dpkg_package.package_name File.join(test1_0, "make.it.fail")
+ dpkg_package.source test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should do nothing if the package is not installed when the name is a file that does not exist, but the source exists" do
+ set_dpkg_package_name "some other name"
+ dpkg_package.name "whatever"
+ dpkg_package.package_name File.join(test1_0, "make.it.fail")
+ dpkg_package.source test1_0
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should remove a package if the package_name is correct, but the source does not exist" do
+ shell_out!("dpkg -i #{test1_0}")
+ dpkg_package.name "whatever"
+ dpkg_package.package_name "chef-integration-test"
+ dpkg_package.source File.join(test1_0, "make.it.fail")
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test', returns: [1])
+ end
+
+ it "should do nothing if the package_name is correct, but the source does not exist, and the package is not installed" do
+ dpkg_package.name "whatever"
+ dpkg_package.package_name "chef-integration-test"
+ dpkg_package.source File.join(test1_0, "make.it.fail")
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test').exitstatus).to eql(1)
+ end
+
+ it "should remove both packages when called with two" do
+ shell_out!("dpkg -i #{test1_0} #{test2_0}")
+ set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test', returns: [1])
+ should_be_purged_or_removed('chef-integration-test2', action)
+ end
+
+ it "should remove a package when only the first one is installed" do
+ shell_out!("dpkg -i #{test1_0}")
+ set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test', returns: [1])
+ shell_out!('dpkg -s chef-integration-test2', returns: [1])
+ end
+
+ it "should remove a package when only the second one is installed" do
+ shell_out!("dpkg -i #{test2_0}")
+ set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test', returns: [1])
+ should_be_purged_or_removed('chef-integration-test2', action)
+ end
+
+ it "should do nothing when both packages are not installed" do
+ set_dpkg_package_name [ "chef-integration-test", "chef-integration-test2" ]
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test', returns: [1])
+ shell_out!('dpkg -s chef-integration-test2', returns: [1])
+ end
+
+ end
+
+ context "action :remove" do
+ let(:action) { :remove }
+ it_behaves_like "common behavior for remove or purge"
+
+ it "should not remove a removed package when the name is a source" do
+ # the "test2" file has a conffile declared in it
+ shell_out!("dpkg -i #{test2_0}")
+ shell_out!("dpkg -r chef-integration-test2")
+ set_dpkg_package_name "chef-integration-test2"
+ dpkg_package.run_action(action)
+ expect(dpkg_package).not_to be_updated_by_last_action
+ shell_out!('dpkg -s chef-integration-test2') # its still 'installed'
+ end
+ end
+
+ context "action :purge" do
+ let(:action) { :purge }
+ it_behaves_like "common behavior for remove or purge"
+
+ it "should purge a removed package when the name is a source" do
+ # the "test2" file has a conffile declared in it
+ shell_out!("dpkg -i #{test2_0}")
+ shell_out!("dpkg -r chef-integration-test2")
+ set_dpkg_package_name "chef-integration-test2"
+ dpkg_package.run_action(action)
+ expect(dpkg_package).to be_updated_by_last_action
+ expect(shell_out('dpkg -s chef-integration-test2').exitstatus).to eql(1)
+ end
+ end
+end
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb
index 8d37b072e8..25ec872b9b 100644
--- a/spec/functional/resource/package_spec.rb
+++ b/spec/functional/resource/package_spec.rb
@@ -1,7 +1,7 @@
# encoding: UTF-8
#
# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2013 Opscode, Inc.
+# Copyright:: Copyright (c) 2013-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 5ebd9cb896..7b3b61cad0 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -94,8 +94,10 @@ test_node.automatic['platform_family'] = (OHAI_SYSTEM['platform_family'] || 'unk
test_node.automatic['platform'] = (OHAI_SYSTEM['platform'] || 'unknown_platform').dup.freeze
test_node.automatic['platform_version'] = (OHAI_SYSTEM['platform_version'] || 'unknown_platform_version').dup.freeze
TEST_NODE = test_node.freeze
+TEST_OS = TEST_NODE['os']
TEST_PLATFORM = TEST_NODE['platform']
TEST_PLATFORM_VERSION = TEST_NODE['platform_version']
+TEST_PLATFORM_FAMILY = TEST_NODE['platform_family']
RSpec.configure do |config|
config.include(Matchers)
@@ -145,6 +147,7 @@ RSpec.configure do |config|
config.filter_run_excluding :system_windows_service_gem_only => true unless system_windows_service_gem?
config.filter_run_excluding :unix_only => true unless unix?
config.filter_run_excluding :aix_only => true unless aix?
+ config.filter_run_excluding :debian_family_only => true unless debian_family?
config.filter_run_excluding :supports_cloexec => true unless supports_cloexec?
config.filter_run_excluding :selinux_only => true unless selinux_enabled?
config.filter_run_excluding :ruby_20_only => true unless ruby_20?
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 9c6c3fdf72..2fc4a02cf1 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -143,6 +143,10 @@ def freebsd?
!!(RUBY_PLATFORM =~ /freebsd/)
end
+def debian_family?
+ !!(ohai[:platform_family] == "debian")
+end
+
def aix?
!!(RUBY_PLATFORM =~ /aix/)
end
diff --git a/spec/unit/mixin/subclass_directive_spec.rb b/spec/unit/mixin/subclass_directive_spec.rb
new file mode 100644
index 0000000000..552f26c0c1
--- /dev/null
+++ b/spec/unit/mixin/subclass_directive_spec.rb
@@ -0,0 +1,45 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+class SubclassDirectiveParent
+ extend Chef::Mixin::SubclassDirective
+
+ subclass_directive :behave_differently
+end
+
+class SubclassDirectiveChild < SubclassDirectiveParent
+ behave_differently
+end
+
+class ChildWithoutDirective < SubclassDirectiveParent
+end
+
+describe Chef::Mixin::Uris do
+ let (:child) { SubclassDirectiveChild.new }
+
+ let (:other_child) { ChildWithoutDirective.new }
+
+ it "the child instance has the directive set" do
+ expect(child.behave_differently?).to be true
+ end
+
+ it "a child that does not declare it does not have it set" do
+ expect(other_child.behave_differently?).to be false
+ end
+end
diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb
index 2ded8313c0..15b2903a0e 100644
--- a/spec/unit/provider/package/dpkg_spec.rb
+++ b/spec/unit/provider/package/dpkg_spec.rb
@@ -1,4 +1,3 @@
-#
# Author:: Bryan McLellan (btm@loftninjas.org)
# Copyright:: Copyright (c) 2009 Bryan McLellan
# License:: Apache License, Version 2.0
@@ -25,7 +24,7 @@ describe Chef::Provider::Package::Dpkg do
let(:package) { "wget" }
let(:source) { "/tmp/wget_1.11.4-1ubuntu1_amd64.deb" }
let(:new_resource) do
- new_resource = Chef::Resource::Package.new(package)
+ new_resource = Chef::Resource::DpkgPackage.new(package)
new_resource.source source
new_resource
end
@@ -52,8 +51,8 @@ Conflicts: wget-ssl
end
before(:each) do
- allow(provider).to receive(:shell_out!).with("dpkg-deb -W #{new_resource.source}", timeout: 900).and_return(dpkg_deb_status)
- allow(provider).to receive(:shell_out).with("dpkg -s #{package}", timeout: 900).and_return(dpkg_s_status)
+ allow(provider).to receive(:shell_out!).with("dpkg-deb -W #{source}", timeout: 900).and_return(dpkg_deb_status)
+ allow(provider).to receive(:shell_out).with("dpkg -s #{package}", timeout: 900).and_return(double(stdout: "", exitstatus: -1))
allow(::File).to receive(:exist?).with(source).and_return(true)
end
@@ -112,16 +111,16 @@ Conflicts: wget-ssl
it "should create a current resource with the name of the new_resource" do
provider.load_current_resource
- expect(provider.current_resource.package_name).to eq("wget")
+ expect(provider.current_resource.package_name).to eq(["wget"])
end
describe 'gets the source package version from dpkg-deb' do
def check_version(version)
status = double(:stdout => "wget\t#{version}", :exitstatus => 0)
- expect(provider).to receive(:shell_out!).with("dpkg-deb -W #{new_resource.source}", timeout: 900).and_return(status)
+ expect(provider).to receive(:shell_out!).with("dpkg-deb -W #{source}", timeout: 900).and_return(status)
provider.load_current_resource
- expect(provider.current_resource.package_name).to eq("wget")
- expect(provider.candidate_version).to eq(version)
+ expect(provider.current_resource.package_name).to eq(["wget"])
+ expect(provider.candidate_version).to eq([version])
end
it 'if short version provided' do
@@ -146,7 +145,7 @@ Conflicts: wget-ssl
it "gets the source package name from dpkg-deb correctly" do
provider.load_current_resource
- expect(provider.current_resource.package_name).to eq("f.o.o-pkg++2")
+ expect(provider.current_resource.package_name).to eq(["f.o.o-pkg++2"])
end
end
@@ -157,21 +156,22 @@ Conflicts: wget-ssl
it "gets the source package version from dpkg-deb correctly when the package version has `~', `-', `+' or `.' characters" do
provider.load_current_resource
- expect(provider.candidate_version).to eq('1.2.3+3141592-1ubuntu1~lucid')
+ expect(provider.candidate_version).to eq(['1.2.3+3141592-1ubuntu1~lucid'])
end
end
- it "should raise an exception if the source is not set but we are installing" do
- new_resource = Chef::Resource::Package.new("wget")
- provider.new_resource = new_resource
- provider.load_current_resource
- provider.define_resource_requirements
- expect { provider.run_action(:install)}.to raise_error(Chef::Exceptions::Package)
+ describe "when the source is not set" do
+ let(:source) { nil }
+
+ it "should raise an exception if the source is not set but we are installing" do
+ expect { provider.run_action(:install)}.to raise_error(Chef::Exceptions::Package)
+ end
end
it "should return the current version installed if found by dpkg" do
+ allow(provider).to receive(:shell_out).with("dpkg -s #{package}", timeout: 900).and_return(dpkg_s_status)
provider.load_current_resource
- expect(provider.current_resource.version).to eq("1.11.4-1ubuntu1")
+ expect(provider.current_resource.version).to eq(["1.11.4-1ubuntu1"])
end
it "should raise an exception if dpkg fails to run" do
@@ -184,72 +184,73 @@ Conflicts: wget-ssl
describe Chef::Provider::Package::Dpkg, "install and upgrade" do
it "should run dpkg -i with the package source" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i /tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg -i", nil, "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
- provider.install_package("wget", "1.11.4-1ubuntu1")
+ provider.load_current_resource
+ provider.run_action(:install)
end
it "should run dpkg -i if the package is a path and the source is nil" do
- new_resource.name = "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ new_resource.name "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i /tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg -i", nil, "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
- provider.install_package("/tmp/wget_1.11.4-1ubuntu1_amd64.deb", "1.11.4-1ubuntu1")
+ provider.run_action(:install)
end
it "should run dpkg -i if the package is a path and the source is nil for an upgrade" do
- new_resource.name = "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ new_resource.name "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i /tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg -i", nil, "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
- provider.upgrade_package("/tmp/wget_1.11.4-1ubuntu1_amd64.deb", "1.11.4-1ubuntu1")
+ provider.run_action(:upgrade)
end
it "should run dpkg -i with the package source and options if specified" do
+ new_resource.options "--force-yes"
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -i --force-yes /tmp/wget_1.11.4-1ubuntu1_amd64.deb"
+ "dpkg -i", "--force-yes", "/tmp/wget_1.11.4-1ubuntu1_amd64.deb"
)
- allow(new_resource).to receive(:options).and_return("--force-yes")
-
- provider.install_package("wget", "1.11.4-1ubuntu1")
+ provider.run_action(:install)
end
+
it "should upgrade by running install_package" do
- expect(provider).to receive(:install_package).with("wget", "1.11.4-1ubuntu1")
- provider.upgrade_package("wget", "1.11.4-1ubuntu1")
+ expect(provider).to receive(:install_package).with(["wget"], ["1.11.4-1ubuntu1"])
+ provider.upgrade_package(["wget"], ["1.11.4-1ubuntu1"])
end
end
describe Chef::Provider::Package::Dpkg, "remove and purge" do
it "should run dpkg -r to remove the package" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -r wget"
+ "dpkg -r", nil, "wget"
)
- provider.remove_package("wget", "1.11.4-1ubuntu1")
+ provider.remove_package(["wget"], ["1.11.4-1ubuntu1"])
end
it "should run dpkg -r to remove the package with options if specified" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -r --force-yes wget"
+ "dpkg -r", "--force-yes", "wget"
)
allow(new_resource).to receive(:options).and_return("--force-yes")
- provider.remove_package("wget", "1.11.4-1ubuntu1")
+ provider.remove_package(["wget"], ["1.11.4-1ubuntu1"])
end
it "should run dpkg -P to purge the package" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -P wget"
+ "dpkg -P", nil, "wget"
)
- provider.purge_package("wget", "1.11.4-1ubuntu1")
+ provider.purge_package(["wget"], ["1.11.4-1ubuntu1"])
end
it "should run dpkg -P to purge the package with options if specified" do
expect(provider).to receive(:run_noninteractive).with(
- "dpkg -P --force-yes wget"
+ "dpkg -P", "--force-yes", "wget"
)
allow(new_resource).to receive(:options).and_return("--force-yes")
- provider.purge_package("wget", "1.11.4-1ubuntu1")
+ provider.purge_package(["wget"], ["1.11.4-1ubuntu1"])
end
end
end
diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb
index 9e4384820a..f77c2b5d0d 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -453,17 +453,13 @@ describe "Subclass with use_multipackage_api" do
provider
end
- it "should have supports_arrays class accessor" do
- expect(MyPackageProvider.supports_arrays).to be true
- MyPackageProvider.supports_arrays = false
- expect(MyPackageProvider.supports_arrays).to be false
- # class variables are global state, so better set it back...
- MyPackageProvider.supports_arrays = true
+ it "has use_multipackage_api? methods on the class and instance" do
+ expect(MyPackageProvider.use_multipackage_api?).to be true
+ expect(provider.use_multipackage_api?).to be true
end
- it "has supports_arrays? methods on the class and instance" do
- expect(MyPackageProvider.supports_arrays?).to be true
- expect(provider.supports_arrays?).to be true
+ it "offers a_to_s to subclasses to convert an array of strings to a single string" do
+ expect(provider.send(:a_to_s, "a", nil, "b", "", "c", " ", "d e", "f-g")).to eq("a b c d e f-g")
end
it "when user passes string to package_name, passes arrays to install_package" do