summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorSalim Alam <salam@chef.io>2015-12-08 10:17:11 -0800
committerSalim Alam <salam@chef.io>2015-12-08 10:17:11 -0800
commit6a9cec48804f097ce55d2934f97ea4409890506a (patch)
treefeb19eedf7151c47fb5db7b831a07cba8b155670 /spec
parent5982661df7c09bbdadd5d2b8431f2a7bbba05c0d (diff)
parent3e704d162e3ef5dff9e929eca7c82b48c4d66305 (diff)
downloadchef-6a9cec48804f097ce55d2934f97ea4409890506a.tar.gz
Merge pull request #4193 from chef/mwrock/package
adds support for installer types inno, nsis, wise and installshield to windows_package resource
Diffstat (limited to 'spec')
-rw-r--r--spec/functional/resource/windows_package_spec.rb177
-rw-r--r--spec/functional/win32/version_info_spec.rb50
-rw-r--r--spec/unit/provider/package/windows/exe_spec.rb251
-rw-r--r--spec/unit/provider/package/windows/msi_spec.rb104
-rw-r--r--spec/unit/provider/package/windows_spec.rb253
5 files changed, 799 insertions, 36 deletions
diff --git a/spec/functional/resource/windows_package_spec.rb b/spec/functional/resource/windows_package_spec.rb
new file mode 100644
index 0000000000..65378653b0
--- /dev/null
+++ b/spec/functional/resource/windows_package_spec.rb
@@ -0,0 +1,177 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# 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'
+require 'functional/resource/base'
+
+describe Chef::Resource::WindowsPackage, :windows_only, :volatile do
+ let(:pkg_name) { nil }
+ let(:pkg_path) { nil }
+ let(:pkg_checksum) { nil }
+ let(:pkg_version) { nil }
+ let(:pkg_type) { nil }
+ let(:pkg_options) { nil }
+
+ subject do
+ new_resource = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
+ new_resource.source pkg_path
+ new_resource.version pkg_version
+ new_resource.installer_type pkg_type
+ new_resource.options pkg_options
+ new_resource.checksum pkg_checksum
+ new_resource
+ end
+
+ describe "multi package scenario" do
+ let(:pkg_name) { 'Microsoft Visual C++ 2005 Redistributable' }
+ let(:pkg_path) { 'https://download.microsoft.com/download/6/B/B/6BB661D6-A8AE-4819-B79F-236472F6070C/vcredist_x86.exe' }
+ let(:pkg_checksum) { nil }
+ let(:pkg_version) { '8.0.59193' }
+ let(:pkg_type) { :custom }
+ let(:pkg_options) { "/Q" }
+
+ it "updates resource on first install" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+
+ it "does not update resource when already installed" do
+ subject.run_action(:install)
+ expect(subject).not_to be_updated_by_last_action
+ end
+
+ context "installing additional version" do
+ let(:pkg_path) { 'https://download.microsoft.com/download/e/1/c/e1c773de-73ba-494a-a5ba-f24906ecf088/vcredist_x86.exe' }
+ let(:pkg_version) { '8.0.56336' }
+
+ it "installs older version" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ describe "removing package" do
+ subject { Chef::Resource::WindowsPackage.new(pkg_name, run_context) }
+
+ context "multiple versions and a version given to remove" do
+ before { subject.version('8.0.56336')}
+
+ it "removes specified version" do
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ prov = subject.provider_for_action(:remove)
+ prov.load_current_resource
+ expect(prov.current_version_array).to eq([['8.0.59193']])
+ end
+ end
+
+ context "single version installed and no version given to remove" do
+ it "removes last remaining version" do
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ prov = subject.provider_for_action(:remove)
+ prov.load_current_resource
+ expect(prov.current_version_array).to eq([nil])
+ end
+ end
+
+ describe "removing multiple versions at once" do
+ let(:pkg_version) { nil }
+ before do
+ install1 = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
+ install1.source pkg_path
+ install1.version pkg_version
+ install1.installer_type pkg_type
+ install1.options pkg_options
+ install1.run_action(:install)
+
+ install2 = Chef::Resource::WindowsPackage.new(pkg_name, run_context)
+ install2.source 'https://download.microsoft.com/download/e/1/c/e1c773de-73ba-494a-a5ba-f24906ecf088/vcredist_x86.exe'
+ install2.version '8.0.56336'
+ install2.installer_type pkg_type
+ install2.options pkg_options
+ install2.run_action(:install)
+ end
+
+ it "removes all versions" do
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ prov = subject.provider_for_action(:remove)
+ prov.load_current_resource
+ expect(prov.current_version_array).to eq([nil])
+ end
+ end
+ end
+ end
+
+ describe "package version and installer type" do
+ after { subject.run_action(:remove) }
+
+ context "null soft" do
+ let(:pkg_name) { 'Ultra Defragmenter' }
+ let(:pkg_path) { 'http://iweb.dl.sourceforge.net/project/ultradefrag/stable-release/6.1.1/ultradefrag-6.1.1.bin.amd64.exe' }
+ let(:pkg_checksum) { '11d53ed4c426c8c867ad43f142b7904226ffd9938c02e37086913620d79e3c09' }
+
+ it "finds the correct package version" do
+ subject.run_action(:install)
+ expect(subject.version).to eq('6.1.1')
+ end
+
+ it "finds the correct installer type" do
+ subject.run_action(:install)
+ expect(subject.provider_for_action(:install).installer_type).to eq(:nsis)
+ end
+ end
+
+ context "inno" do
+ let(:pkg_name) { 'Mercurial 3.6.1 (64-bit)' }
+ let(:pkg_path) { 'http://mercurial.selenic.com/release/windows/Mercurial-3.6.1-x64.exe' }
+ let(:pkg_checksum) { 'febd29578cb6736163d232708b834a2ddd119aa40abc536b2c313fc5e1b5831d' }
+
+ it "finds the correct package version" do
+ subject.run_action(:install)
+ expect(subject.version).to eq(nil) # Mercurial does not include versioning
+ end
+
+ it "finds the correct installer type" do
+ subject.run_action(:install)
+ expect(subject.provider_for_action(:install).installer_type).to eq(:inno)
+ end
+ end
+ end
+
+ describe "install from local file" do
+ let(:pkg_name) { 'Mercurial 3.6.1 (64-bit)' }
+ let(:pkg_path) { ::File.join(Chef::Config[:file_cache_path], "package", "Mercurial-3.6.1-x64.exe") }
+ let(:pkg_checksum) { 'febd29578cb6736163d232708b834a2ddd119aa40abc536b2c313fc5e1b5831d' }
+
+ it "installs the app" do
+ subject.run_action(:install)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+
+ describe "uninstall exe without source" do
+ let(:pkg_name) { 'Mercurial 3.6.1 (64-bit)' }
+
+ it "uninstalls the app" do
+ subject.run_action(:remove)
+ expect(subject).to be_updated_by_last_action
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/functional/win32/version_info_spec.rb b/spec/functional/win32/version_info_spec.rb
new file mode 100644
index 0000000000..c7d41f9616
--- /dev/null
+++ b/spec/functional/win32/version_info_spec.rb
@@ -0,0 +1,50 @@
+#
+# Author:: Matt Wrock (<matt@mattwrock.com>)
+# Copyright:: Copyright 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'
+if Chef::Platform.windows?
+ require 'chef/win32/file/version_info'
+end
+
+describe "Chef::ReservedNames::Win32::File::VersionInfo", :windows_only do
+ require 'wmi-lite/wmi'
+ let(:file_path) { ENV['ComSpec'] }
+ let(:os_version) do
+ wmi = WmiLite::Wmi.new
+ os_info = wmi.first_of('Win32_OperatingSystem')
+ os_info['version']
+ end
+
+ subject { Chef::ReservedNames::Win32::File::VersionInfo.new(file_path) }
+
+ it "file version has the same version as windows" do
+ expect(subject.FileVersion).to start_with(os_version)
+ end
+
+ it "product version has the same version as windows" do
+ expect(subject.ProductVersion).to start_with(os_version)
+ end
+
+ it "company is microsoft" do
+ expect(subject.CompanyName).to eq("Microsoft Corporation")
+ end
+
+ it "file description is command processor" do
+ expect(subject.FileDescription).to eq("Windows Command Processor")
+ end
+end
diff --git a/spec/unit/provider/package/windows/exe_spec.rb b/spec/unit/provider/package/windows/exe_spec.rb
new file mode 100644
index 0000000000..730df5e067
--- /dev/null
+++ b/spec/unit/provider/package/windows/exe_spec.rb
@@ -0,0 +1,251 @@
+#
+# Author:: Matt Wrock <matt@mattwrock.com>
+# 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'
+require 'chef/provider/package/windows/exe'
+
+unless Chef::Platform.windows?
+ class Chef
+ module ReservedNames::Win32
+ class File
+ def version_info
+ nil
+ end
+ end
+ end
+ end
+end
+
+describe Chef::Provider::Package::Windows::Exe do
+ let(:package_name) { "calculator" }
+ let(:resource_source) { "calculator.exe" }
+ let(:new_resource) do
+ new_resource = Chef::Resource::WindowsPackage.new(package_name)
+ new_resource.source(resource_source)
+ new_resource
+ end
+ let(:uninstall_hash) do
+ [{
+ 'DisplayVersion' => 'outdated',
+ 'UninstallString' => File.join("uninst_dir", "uninst_file")
+ }]
+ end
+ let(:uninstall_entry) do
+ entries = []
+ uninstall_hash.each do |entry|
+ entries.push(Chef::Provider::Package::Windows::RegistryUninstallEntry.new('hive', 'key', entry))
+ end
+ entries
+ end
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :nsis, uninstall_entry) }
+ let(:file_version) { nil }
+ let(:product_version) { nil }
+ let(:version_info) { instance_double("Chef::ReservedNames::Win32::File::Version_info", FileVersion: file_version, ProductVersion: product_version) }
+
+ before(:each) do
+ allow(Chef::ReservedNames::Win32::File).to receive(:version_info).and_return(version_info)
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(true)
+ end
+
+ it "responds to shell_out!" do
+ expect(provider).to respond_to(:shell_out!)
+ end
+
+ describe "expand_options" do
+ it "returns an empty string if passed no options" do
+ expect(provider.expand_options(nil)).to eql ""
+ end
+
+ it "returns a string with a leading space if passed options" do
+ expect(provider.expand_options("--train nope --town no_way")).to eql(" --train nope --town no_way")
+ end
+ end
+
+ describe "installed_version" do
+ it "returns the installed version" do
+ expect(provider.installed_version).to eql(["outdated"])
+ end
+
+ context "no versions installed" do
+ let(:uninstall_hash) { [] }
+
+ it "returns the installed version" do
+ expect(provider.installed_version).to eql(nil)
+ end
+ end
+ end
+
+ describe "package_version" do
+ before { new_resource.version(nil) }
+
+ context "source file does not exist" do
+ before do
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
+ end
+
+ it "returns nil" do
+ expect(provider.package_version).to eql(nil)
+ end
+ end
+
+ context "file version is empty" do
+ let(:file_version) { '' }
+
+ it "returns nil" do
+ expect(provider.package_version).to eql(nil)
+ end
+
+ it "returns the version of a package if given" do
+ new_resource.version('v55555')
+ expect(provider.package_version).to eql('v55555')
+ end
+ end
+
+ context "both file and product version are in installer" do
+ let(:file_version) { '1.1.1' }
+ let(:product_version) { '1.1' }
+
+ it "returns the file version" do
+ expect(provider.package_version).to eql('1.1.1')
+ end
+
+ it "returns the version of a package if given" do
+ new_resource.version('v55555')
+ expect(provider.package_version).to eql('v55555')
+ end
+ end
+
+ context "only file version is in installer" do
+ let(:file_version) { '1.1.1' }
+
+ it "returns the file version" do
+ expect(provider.package_version).to eql('1.1.1')
+ end
+
+ it "returns the version of a package if given" do
+ new_resource.version('v55555')
+ expect(provider.package_version).to eql('v55555')
+ end
+ end
+
+ context "only product version is in installer" do
+ let(:product_version) { '1.1' }
+
+ it "returns the product version" do
+ expect(provider.package_version).to eql('1.1')
+ end
+
+ it "returns the version of a package if given" do
+ new_resource.version('v55555')
+ expect(provider.package_version).to eql('v55555')
+ end
+ end
+
+ context "no version info is in installer" do
+ let(:file_version) { nil }
+ let(:product_version) { nil }
+
+ it "returns the version of a package" do
+ new_resource.version('v55555')
+ expect(provider.package_version).to eql('v55555')
+ end
+ end
+
+ context "no version info is in installer and none in attribute" do
+ it "returns the version of a package" do
+ expect(provider.package_version).to eql(nil)
+ end
+ end
+ end
+
+ describe "remove_package" do
+ context "no version given and one package installed" do
+ it "removes installed package" do
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir\" uninst_file \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+
+ context "several packages installed" do
+ let(:uninstall_hash) do
+ [
+ {
+ 'DisplayVersion' => 'v1',
+ 'UninstallString' => File.join("uninst_dir1", "uninst_file1")
+ },
+ {
+ 'DisplayVersion' => 'v2',
+ 'UninstallString' => File.join("uninst_dir2", "uninst_file2")
+ }
+ ]
+ end
+
+ context "version given and installed" do
+ it "removes given version" do
+ new_resource.version('v2')
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+
+ context "no version given" do
+ it "removes both versions" do
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir1\" uninst_file1 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+ end
+ end
+
+ context "installs nsis installer" do
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :nsis, uninstall_entry) }
+
+ it "calls installer with the correct flags" do
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.install_package
+ end
+ end
+
+ context "installs installshield installer" do
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :installshield, uninstall_entry) }
+
+ it "calls installer with the correct flags" do
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/s \/sms & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.install_package
+ end
+ end
+
+ context "installs inno installer" do
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :inno, uninstall_entry) }
+
+ it "calls installer with the correct flags" do
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/VERYSILENT \/SUPPRESSMSGBOXES \/NORESTART & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.install_package
+ end
+ end
+
+ context "installs wise installer" do
+ let(:provider) { Chef::Provider::Package::Windows::Exe.new(new_resource, :wise, uninstall_entry) }
+
+ it "calls installer with the correct flags" do
+ expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"#{Regexp.quote(new_resource.source)}\" \/s & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+ provider.install_package
+ end
+ end
+end
diff --git a/spec/unit/provider/package/windows/msi_spec.rb b/spec/unit/provider/package/windows/msi_spec.rb
index bef202847f..9377dcaad9 100644
--- a/spec/unit/provider/package/windows/msi_spec.rb
+++ b/spec/unit/provider/package/windows/msi_spec.rb
@@ -17,17 +17,37 @@
#
require 'spec_helper'
+require 'chef/provider/package/windows/msi'
describe Chef::Provider::Package::Windows::MSI do
let(:node) { double('Chef::Node') }
let(:events) { double('Chef::Events').as_null_object } # mock all the methods
let(:run_context) { double('Chef::RunContext', :node => node, :events => events) }
- let(:new_resource) { Chef::Resource::WindowsPackage.new("calculator.msi") }
- let(:provider) { Chef::Provider::Package::Windows::MSI.new(new_resource) }
-
- before(:each) do
- stub_const("File::ALT_SEPARATOR", "\\")
- allow(::File).to receive(:absolute_path).with("calculator.msi").and_return("calculator.msi")
+ let(:package_name) { "calculator" }
+ let(:resource_source) { "calculator.msi" }
+ let(:resource_version) { nil }
+ let(:new_resource) do
+ new_resource = Chef::Resource::WindowsPackage.new(package_name)
+ new_resource.source(resource_source)
+ new_resource.version(resource_version)
+ new_resource
+ end
+ let(:uninstall_hash) do
+ [{
+ 'DisplayVersion' => 'outdated',
+ 'UninstallString' => "MsiExec.exe /X{guid}"
+ }]
+ end
+ let(:uninstall_entry) do
+ entries = []
+ uninstall_hash.each do |entry|
+ entries.push(Chef::Provider::Package::Windows::RegistryUninstallEntry.new('hive', 'key', entry))
+ end
+ entries
+ end
+ let(:provider) { Chef::Provider::Package::Windows::MSI.new(new_resource, uninstall_entry) }
+ before do
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(true)
end
it "responds to shell_out!" do
@@ -50,6 +70,11 @@ describe Chef::Provider::Package::Windows::MSI do
allow(provider).to receive(:get_installed_version).with("{23170F69-40C1-2702-0920-000001000000}").and_return("3.14159.1337.42")
expect(provider.installed_version).to eql("3.14159.1337.42")
end
+
+ it "returns the installed version in the registry when install file not present" do
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
+ expect(provider.installed_version).to eql(["outdated"])
+ end
end
describe "package_version" do
@@ -57,19 +82,78 @@ describe Chef::Provider::Package::Windows::MSI do
allow(provider).to receive(:get_product_property).with(/calculator.msi$/, "ProductVersion").and_return(42)
expect(provider.package_version).to eql(42)
end
+
+ context "version is explicitly provided" do
+ let(:resource_version) { "given_version" }
+
+ it "returns the given version" do
+ expect(provider.package_version).to eql("given_version")
+ end
+ end
+
+ context "no source or version is given" do
+ before do
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
+ end
+
+ it "returns nil" do
+ expect(provider.package_version).to eql(nil)
+ end
+ end
end
describe "install_package" do
it "calls msiexec /qn /i" do
- expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"calculator.msi\"/, kind_of(Hash))
- provider.install_package("unused", "unused")
+ expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"#{Regexp.quote(new_resource.source)}\"/, kind_of(Hash))
+ provider.install_package
end
end
describe "remove_package" do
it "calls msiexec /qn /x" do
- expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"calculator.msi\"/, kind_of(Hash))
- provider.remove_package("unused", "unused")
+ expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"#{Regexp.quote(new_resource.source)}\"/, kind_of(Hash))
+ provider.remove_package
+ end
+
+ context "no source is provided" do
+ before do
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
+ end
+
+ it "removes installed package" do
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ provider.remove_package
+ end
+
+ context "there are multiple installs" do
+ let(:uninstall_hash) do
+ [
+ {
+ 'DisplayVersion' => 'outdated',
+ 'UninstallString' => "MsiExec.exe /X{guid}"
+ },
+ {
+ 'DisplayVersion' => 'really_outdated',
+ 'UninstallString' => "MsiExec.exe /X{guid2}"
+ }
+ ]
+ end
+
+ it "removes both installed package" do
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid2} \/Q/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
+
+ context "custom options includes /Q" do
+ before { new_resource.options("/Q") }
+
+ it "does not duplicate quiet switch" do
+ expect(provider).to receive(:shell_out!).with(/MsiExec.exe \/X{guid} \/Q/, kind_of(Hash))
+ provider.remove_package
+ end
+ end
end
end
end
diff --git a/spec/unit/provider/package/windows_spec.rb b/spec/unit/provider/package/windows_spec.rb
index e5acc87694..c26c446b5b 100644
--- a/spec/unit/provider/package/windows_spec.rb
+++ b/spec/unit/provider/package/windows_spec.rb
@@ -17,6 +17,8 @@
#
require 'spec_helper'
+require 'chef/provider/package/windows/exe'
+require 'chef/provider/package/windows/msi'
describe Chef::Provider::Package::Windows, :windows_only do
before(:each) do
@@ -28,10 +30,19 @@ describe Chef::Provider::Package::Windows, :windows_only do
let(:events) { double('Chef::Events').as_null_object } # mock all the methods
let(:run_context) { double('Chef::RunContext', :node => node, :events => events) }
let(:resource_source) { 'calculator.msi' }
- let(:new_resource) { Chef::Resource::WindowsPackage.new(resource_source) }
+ let(:resource_name) { 'calculator' }
+ let(:new_resource) do
+ new_resource = Chef::Resource::WindowsPackage.new(resource_name)
+ new_resource.source(resource_source)
+ new_resource
+ end
let(:provider) { Chef::Provider::Package::Windows.new(new_resource, run_context) }
let(:cache_path) { 'c:\\cache\\' }
+ before(:each) do
+ allow(::File).to receive(:exist?).with(provider.new_resource.source).and_return(true)
+ end
+
describe "load_current_resource" do
shared_examples "a local file" do
before(:each) do
@@ -43,7 +54,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
it "creates a current resource with the name of the new resource" do
provider.load_current_resource
expect(provider.current_resource).to be_a(Chef::Resource::WindowsPackage)
- expect(provider.current_resource.name).to eql(resource_source)
+ expect(provider.current_resource.name).to eql(resource_name)
end
it "sets the current version if the package is installed" do
@@ -76,19 +87,6 @@ describe Chef::Provider::Package::Windows, :windows_only do
end
it_behaves_like "a local file"
end
-
- context "when remote_file_attributes are provided" do
- let (:remote_file_attributes) { {:path => 'C:\\foobar.msi'} }
- before(:each) do
- new_resource.remote_file_attributes(remote_file_attributes)
- end
-
- it 'should override the attributes of the remote file resource used' do
- expect(::File).to receive(:exists?).with(remote_file_attributes[:path])
- provider.load_current_resource
- end
-
- end
end
context "when source is a local file" do
@@ -98,6 +96,7 @@ describe Chef::Provider::Package::Windows, :windows_only do
describe "package_provider" do
shared_examples "a local file" do
+
it "checks that the source path is valid" do
expect(Chef::Util::PathHelper).to receive(:validate_path)
provider.package_provider
@@ -108,9 +107,29 @@ describe Chef::Provider::Package::Windows, :windows_only do
expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::MSI)
end
- it "raises an error if the installer_type is unknown" do
- allow(provider).to receive(:installer_type).and_return(:apt_for_windows)
- expect { provider.package_provider }.to raise_error
+ it "sets the package provider to Exe if the the installer type is :inno" do
+ allow(provider).to receive(:installer_type).and_return(:inno)
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
+ end
+
+ it "sets the package provider to Exe if the the installer type is :nsis" do
+ allow(provider).to receive(:installer_type).and_return(:nsis)
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
+ end
+
+ it "sets the package provider to Exe if the the installer type is :wise" do
+ allow(provider).to receive(:installer_type).and_return(:wise)
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
+ end
+
+ it "sets the package provider to Exe if the the installer type is :installshield" do
+ allow(provider).to receive(:installer_type).and_return(:installshield)
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
+ end
+
+ it "defaults to exe if the installer_type is unknown" do
+ allow(provider).to receive(:installer_type).and_return(nil)
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::Exe)
end
end
@@ -146,20 +165,202 @@ describe Chef::Provider::Package::Windows, :windows_only do
end
describe "installer_type" do
- it "it returns @installer_type if it is set" do
+ let(:resource_source) { "microsoft_installer.exe" }
+
+ context "there is no source" do
+ let(:uninstall_hash) do
+ [{
+ 'DisplayVersion' => 'outdated',
+ 'UninstallString' => "blah blah"
+ }]
+ end
+ let(:uninstall_key) { "blah" }
+ let(:uninstall_entry) do
+ entries = []
+ uninstall_hash.each do |entry|
+ entries.push(Chef::Provider::Package::Windows::RegistryUninstallEntry.new('hive', uninstall_key, entry))
+ end
+ entries
+ end
+
+ before do
+ allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:find_entries).and_return(uninstall_entry)
+ allow(::File).to receive(:exist?).with(Chef::Util::PathHelper.canonical_path(resource_source, false)).and_return(false)
+ end
+
+ context "uninstall string contains MsiExec.exe" do
+ let(:uninstall_hash) do
+ [{
+ 'DisplayVersion' => 'outdated',
+ 'UninstallString' => "MsiExec.exe /X{guid}"
+ }]
+ end
+
+ it "sets installer_type to MSI" do
+ expect(provider.installer_type).to eql(:msi)
+ end
+ end
+
+ context "uninstall string ends with uninst.exe" do
+ let(:uninstall_hash) do
+ [{
+ 'DisplayVersion' => 'outdated',
+ 'UninstallString' => %q{"c:/hfhfheru/uninst.exe"}
+ }]
+ end
+
+ it "sets installer_type to NSIS" do
+ expect(provider.installer_type).to eql(:nsis)
+ end
+ end
+
+ context "uninstall key ends in _is1" do
+ let(:uninstall_key) { "blah_is1" }
+
+ it "sets installer_type to inno" do
+ expect(provider.installer_type).to eql(:inno)
+ end
+ end
+
+ context "eninstall entries is empty" do
+ before { allow(Chef::Provider::Package::Windows::RegistryUninstallEntry).to receive(:find_entries).and_return([]) }
+
+ it "returns nil" do
+ expect(provider.installer_type).to eql(nil)
+ end
+ end
+ end
+
+ it "returns @installer_type if it is set" do
provider.new_resource.installer_type(:downeaster)
expect(provider.installer_type).to eql(:downeaster)
end
- it "sets installer_type to msi if the source ends in .msi" do
- provider.new_resource.source("microsoft_installer.msi")
- expect(provider.installer_type).to eql(:msi)
+ it "sets installer_type to inno if the source contains inno" do
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new('blah blah inno blah'))
+ expect(provider.installer_type).to eql(:inno)
end
- it "raises an error if it cannot determine the installer type" do
- provider.new_resource.installer_type(nil)
- provider.new_resource.source("tomfoolery.now")
- expect { provider.installer_type }.to raise_error(ArgumentError)
+ it "sets installer_type to wise if the source contains wise" do
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new('blah blah wise blah'))
+ expect(provider.installer_type).to eql(:wise)
+ end
+
+ it "sets installer_type to nsis if the source contains nsis" do
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new('blah blah nullsoft blah'))
+ expect(provider.installer_type).to eql(:nsis)
+ end
+
+ context "source ends in .msi" do
+ let(:resource_source) { "microsoft_installer.msi" }
+
+ it "sets installer_type to msi" do
+ expect(provider.installer_type).to eql(:msi)
+ end
+ end
+
+ context "the source is setup.exe" do
+ let(:resource_source) { "setup.exe" }
+
+ it "sets installer_type to installshield" do
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new(''))
+ expect(provider.installer_type).to eql(:installshield)
+ end
+ end
+
+ context "cannot determine the installer type" do
+ let(:resource_source) { "tomfoolery.now" }
+
+ it "raises an error" do
+ allow(::Kernel).to receive(:open).and_yield(StringIO.new(''))
+ provider.new_resource.installer_type(nil)
+ expect { provider.installer_type }.to raise_error(Chef::Exceptions::CannotDetermineWindowsInstallerType)
+ end
+ end
+ end
+
+ describe "action_install" do
+ let(:new_resource) { Chef::Resource::WindowsPackage.new("blah.exe") }
+ before do
+ new_resource.installer_type(:inno)
+ allow_any_instance_of(Chef::Provider::Package::Windows::Exe).to receive(:package_version).and_return(new_resource.version)
+ end
+
+ context "no version given, discovered or installed" do
+ it "installs latest" do
+ expect(provider).to receive(:install_package).with("blah.exe", "latest")
+ provider.run_action(:install)
+ end
+ end
+
+ context "no version given or discovered but package is installed" do
+ before { allow(provider).to receive(:current_version_array).and_return(["5.5.5"]) }
+
+ it "does not install" do
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ end
+ end
+
+ context "a version is given and none is installed" do
+ before { new_resource.version('5.5.5') }
+
+ it "installs given version" do
+ expect(provider).to receive(:install_package).with("blah.exe", "5.5.5")
+ provider.run_action(:install)
+ end
+ end
+
+ context "a version is given and several are installed" do
+ context "given version matches an installed version" do
+ before do
+ new_resource.version('5.5.5')
+ allow(provider).to receive(:current_version_array).and_return([ ["5.5.5", "4.3.0", "1.1.1"] ])
+ end
+
+ it "does not install" do
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ end
+ end
+
+ context "given version does not match an installed version" do
+ before do
+ new_resource.version('5.5.5')
+ allow(provider).to receive(:current_version_array).and_return([ ["5.5.0", "4.3.0", "1.1.1"] ])
+ end
+
+ it "installs given version" do
+ expect(provider).to receive(:install_package).with("blah.exe", "5.5.5")
+ provider.run_action(:install)
+ end
+ end
+ end
+
+ context "a version is given and one is installed" do
+ context "given version matches installed version" do
+ before do
+ new_resource.version('5.5.5')
+ allow(provider).to receive(:current_version_array).and_return(["5.5.5"])
+ end
+
+ it "does not install" do
+ expect(provider).not_to receive(:install_package)
+ provider.run_action(:install)
+ end
+ end
+
+ context "given version does not match installed version" do
+ before do
+ new_resource.version('5.5.5')
+ allow(provider).to receive(:current_version_array).and_return(["5.5.0"])
+ end
+
+ it "installs given version" do
+ expect(provider).to receive(:install_package).with("blah.exe", "5.5.5")
+ provider.run_action(:install)
+ end
+ end
end
end
end