kitchen-pester 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbb89787809d137742a16f65d796fc8c5d718e50
4
- data.tar.gz: e83b27e18475e6c5e2c19432831e27ec44402e6d
3
+ metadata.gz: 3545642684f7cc181d1b44232885c7093c43f108
4
+ data.tar.gz: 5074d03f0cc3b4537a09e4058ca046dff0d6c7dd
5
5
  SHA512:
6
- metadata.gz: f64ff2bca5cabb2e8bb274d1c504ce90534486c22ae55ac9e6b19e8e7a7c42892cc466a2cc25174581ff3b5b404dc40994a0571c81da759092241c90009d97bf
7
- data.tar.gz: 8818549361ffe6e7c31d2626f9b1e982a9313a043596cd8e0fc3d07b5ccc53ee67fd45fcfacc3b3f6699477bc295c399565712b4092631aca03fddbff86dc8b9
6
+ metadata.gz: 1a0c25c85991c7f8b84c9b8ebfd70ecef2aec428f3c65f2009a3033d89912345b983d7d3eb00ec34ee84aab2555ee247af795901883c950d6300466fc269451d
7
+ data.tar.gz: ad5537103ecda5373ca7ae0091ad5c250a9c66484c451ba5cce3b84f32183c4055accc50855de4ba5572a72d6b8ad5e7f2c3ae2664a3a044b4ae570eaf5c8af7
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in kitchen-pester.gemspec
4
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kitchen-pester.gemspec
4
+ gemspec
data/LICENSE CHANGED
@@ -1,22 +1,22 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2015 Steven Murawski
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Steven Murawski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md CHANGED
@@ -1,36 +1,36 @@
1
- [![Gem Version](https://badge.fury.io/rb/kitchen-pester.svg)](http://badge.fury.io/rb/kitchen-pester)
2
- # Kitchen::Pester
3
-
4
- Execute [Pester](https://github.com/pester/Pester) tests right from Test-Kitchen, without having to transit the Busser layer.
5
-
6
- ## Usage
7
-
8
- Either
9
- ```
10
- gem install kitchen-pester
11
- ```
12
- or include
13
- ```
14
- gem 'kitchen-pester'
15
- ```
16
- in your Gemfile.
17
-
18
- In your .kitchen.yml include
19
- ```
20
- verifier:
21
- name: pester
22
- ```
23
- This can be a top-level declaration, a per-node declaration, or a per-suite declaration.
24
-
25
- ### Options
26
-
27
- * `restart_winrm` - boolean, default is false. This is primarily to support powershell v2 scenarios. If Pester is not being found, enable this option.
28
- * `test_folder` - string, default is nil. `test-folder` allows you to specify a custom path (the default is ./test/integration/) for your integration tests. This can be an absolute path or relative to the root of the folder kitchen is running from. This path must exist.
29
-
30
- ## Contributing
31
-
32
- 1. Fork it ( https://github.com/[my-github-username]/kitchen-pester/fork )
33
- 2. Create your feature branch (`git checkout -b my-new-feature`)
34
- 3. Commit your changes (`git commit -am 'Add some feature'`)
35
- 4. Push to the branch (`git push origin my-new-feature`)
36
- 5. Create a new Pull Request
1
+ [![Gem Version](https://badge.fury.io/rb/kitchen-pester.svg)](http://badge.fury.io/rb/kitchen-pester)
2
+ # Kitchen::Pester
3
+
4
+ Execute [Pester](https://github.com/pester/Pester) tests right from Test-Kitchen, without having to transit the Busser layer.
5
+
6
+ ## Usage
7
+
8
+ Either
9
+ ```
10
+ gem install kitchen-pester
11
+ ```
12
+ or include
13
+ ```
14
+ gem 'kitchen-pester'
15
+ ```
16
+ in your Gemfile.
17
+
18
+ In your .kitchen.yml include
19
+ ```
20
+ verifier:
21
+ name: pester
22
+ ```
23
+ This can be a top-level declaration, a per-node declaration, or a per-suite declaration.
24
+
25
+ ### Options
26
+
27
+ * `restart_winrm` - boolean, default is false. This is primarily to support powershell v2 scenarios. If Pester is not being found, enable this option.
28
+ * `test_folder` - string, default is nil. `test-folder` allows you to specify a custom path (the default is ./test/integration/) for your integration tests. This can be an absolute path or relative to the root of the folder kitchen is running from. This path must exist.
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it ( https://github.com/[my-github-username]/kitchen-pester/fork )
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create a new Pull Request
data/Rakefile CHANGED
@@ -1,52 +1,52 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- require "bundler/gem_tasks"
4
-
5
- require "rake/testtask"
6
- Rake::TestTask.new(:unit) do |t|
7
- t.libs.push "lib"
8
- t.test_files = FileList["spec/**/*_spec.rb"]
9
- t.verbose = true
10
- end
11
-
12
- desc "Run all test suites"
13
- task :test => [:unit]
14
-
15
- desc "Display LOC stats"
16
- task :stats do
17
- puts "\n## Production Code Stats"
18
- sh "countloc -r lib"
19
- puts "\n## Test Code Stats"
20
- sh "countloc -r spec"
21
- end
22
-
23
- require "finstyle"
24
- require "rubocop/rake_task"
25
- RuboCop::RakeTask.new(:style) do |task|
26
- task.options << "--display-cop-names"
27
- task.options << "--lint"
28
- task.options << '--config' << '.rubocop.yml'
29
- task.patterns = ['lib/**/*.rb']
30
- end
31
-
32
- require "cane/rake_task"
33
- desc "Run cane to check quality metrics"
34
- Cane::RakeTask.new do |cane|
35
- cane.canefile = "./.cane"
36
- end
37
-
38
- desc "Run all quality tasks"
39
- task :quality => [:cane, :style, :stats]
40
-
41
- require "yard"
42
- YARD::Rake::YardocTask.new
43
-
44
- desc "Generate gem dependency graph"
45
- task :viz do
46
- Bundler.with_clean_env do
47
- sh "bundle viz --without test development guard " \
48
- "--requirements --version"
49
- end
50
- end
51
-
52
- task :default => [:test, :quality]
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ require "rake/testtask"
6
+ Rake::TestTask.new(:unit) do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList["spec/**/*_spec.rb"]
9
+ t.verbose = true
10
+ end
11
+
12
+ desc "Run all test suites"
13
+ task :test => [:unit]
14
+
15
+ desc "Display LOC stats"
16
+ task :stats do
17
+ puts "\n## Production Code Stats"
18
+ sh "countloc -r lib"
19
+ puts "\n## Test Code Stats"
20
+ sh "countloc -r spec"
21
+ end
22
+
23
+ require "finstyle"
24
+ require "rubocop/rake_task"
25
+ RuboCop::RakeTask.new(:style) do |task|
26
+ task.options << "--display-cop-names"
27
+ task.options << "--lint"
28
+ task.options << '--config' << '.rubocop.yml'
29
+ task.patterns = ['lib/**/*.rb']
30
+ end
31
+
32
+ require "cane/rake_task"
33
+ desc "Run cane to check quality metrics"
34
+ Cane::RakeTask.new do |cane|
35
+ cane.canefile = "./.cane"
36
+ end
37
+
38
+ desc "Run all quality tasks"
39
+ task :quality => [:cane, :style, :stats]
40
+
41
+ require "yard"
42
+ YARD::Rake::YardocTask.new
43
+
44
+ desc "Generate gem dependency graph"
45
+ task :viz do
46
+ Bundler.with_clean_env do
47
+ sh "bundle viz --without test development guard " \
48
+ "--requirements --version"
49
+ end
50
+ end
51
+
52
+ task :default => [:test, :quality]
@@ -1,27 +1,27 @@
1
- # encoding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
-
5
- require 'kitchen/verifier/pester_version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = "kitchen-pester"
9
- spec.version = Kitchen::Verifier::PESTER_VERSION
10
- spec.authors = ["Steven Murawski"]
11
- spec.email = ["steven.murawski@gmail.com"]
12
- spec.summary = 'Test-Kitchen verifier for Pester.'
13
- spec.description = 'Skip all that Busser stuff and jump right into Pester.'
14
- spec.homepage = "https://github.com/test-kitchen/kitchen-pester"
15
- spec.license = "Apache 2"
16
-
17
- spec.files = `git ls-files -z`.split("\x0")
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
- spec.require_paths = ["lib"]
21
-
22
- spec.add_development_dependency "bundler", "~> 1.7"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "pry", "~> 0.10"
25
-
26
- spec.add_dependency "test-kitchen", "~> 1.4"
27
- end
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'kitchen/verifier/pester_version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "kitchen-pester"
9
+ spec.version = Kitchen::Verifier::PESTER_VERSION
10
+ spec.authors = ["Steven Murawski"]
11
+ spec.email = ["steven.murawski@gmail.com"]
12
+ spec.summary = 'Test-Kitchen verifier for Pester.'
13
+ spec.description = 'Skip all that Busser stuff and jump right into Pester.'
14
+ spec.homepage = "https://github.com/test-kitchen/kitchen-pester"
15
+ spec.license = "Apache 2"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.7"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "pry", "~> 0.10"
25
+
26
+ spec.add_dependency "test-kitchen", "~> 1.4"
27
+ end
@@ -1,197 +1,310 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Steven Murawski (<steven.murawski@gmail.com>)
4
- #
5
- # Copyright (C) 2015, Steven Murawski
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
-
19
- require 'pathname'
20
- require 'kitchen/verifier/base'
21
- require 'kitchen/verifier/pester_version'
22
-
23
- module Kitchen
24
-
25
- module Verifier
26
-
27
- class Pester < Kitchen::Verifier::Base
28
-
29
- kitchen_verifier_api_version 1
30
-
31
- plugin_version Kitchen::Verifier::PESTER_VERSION
32
-
33
- default_config :restart_winrm, false
34
- default_config :test_folder
35
-
36
- # Creates a new Verifier object using the provided configuration data
37
- # which will be merged with any default configuration.
38
- #
39
- # @param config [Hash] provided verifier configuration
40
- def initialize(config = {})
41
- init_config(config)
42
- end
43
-
44
- # Creates a temporary directory on the local workstation into which
45
- # verifier related files and directories can be copied or created. The
46
- # contents of this directory will be copied over to the instance before
47
- # invoking the verifier's run command. After this method completes, it
48
- # is expected that the contents of the sandbox is complete and ready for
49
- # copy to the remote instance.
50
- #
51
- # **Note:** any subclasses would be well advised to call super first when
52
- # overriding this method, for example:
53
- #
54
- # @example overriding `#create_sandbox`
55
- #
56
- # class MyVerifier < Kitchen::Verifier::Base
57
- # def create_sandbox
58
- # super
59
- # # any further file copies, preparations, etc.
60
- # end
61
- # end
62
- def create_sandbox
63
- super
64
- prepare_pester_tests
65
- end
66
-
67
- # Generates a command string which will install and configure the
68
- # verifier software on an instance. If no work is required, then `nil`
69
- # will be returned.
70
- #
71
- # @return [String] a command string
72
- def install_command
73
- return if local_suite_files.empty?
74
-
75
- cmd = <<-CMD
76
- set-executionpolicy unrestricted -force
77
- if (-not (get-module -list pester)) {
78
- if (get-module -list PowerShellGet){
79
- import-module PowerShellGet -force
80
- import-module PackageManagement -force
81
- get-packageprovider -name NuGet -force | out-null
82
- install-module Pester -force
83
- }
84
- else {
85
- if (-not (get-module -list PsGet)){
86
- iex (new-object Net.WebClient).DownloadString('http://bit.ly/GetPsGet')
87
- }
88
- import-module psget -force
89
- Install-Module Pester
90
- }
91
- }
92
- CMD
93
- wrap_shell_code(Util.outdent!(cmd))
94
- end
95
-
96
- # Generates a command string which will perform any data initialization
97
- # or configuration required after the verifier software is installed
98
- # but before the sandbox has been transferred to the instance. If no work
99
- # is required, then `nil` will be returned.
100
- #
101
- # @return [String] a command string
102
- def init_command
103
- restart_winrm_service if config[:restart_winrm]
104
- end
105
-
106
- # Generates a command string which will perform any commands or
107
- # configuration required just before the main verifier run command but
108
- # after the sandbox has been transferred to the instance. If no work is
109
- # required, then `nil` will be returned.
110
- #
111
- # @return [String] a command string
112
- def prepare_command
113
- end
114
-
115
- # Generates a command string which will invoke the main verifier
116
- # command on the prepared instance. If no work is required, then `nil`
117
- # will be returned.
118
- #
119
- # @return [String] a command string
120
- def run_command
121
- return if local_suite_files.empty?
122
- wrap_shell_code(Util.outdent!(<<-CMD
123
- $global:ProgressPreference = 'SilentlyContinue'
124
- $TestPath = "#{File.join(config[:root_path], 'suites')}"
125
- import-module Pester -force; invoke-pester -path $testpath -enableexit
126
- CMD
127
- ))
128
- end
129
-
130
- #private
131
-
132
- def restart_winrm_service
133
-
134
- cmd = 'schtasks /Create /TN restart_winrm /TR ' \
135
- '"powershell -command restart-service winrm" ' \
136
- '/SC ONCE /ST 00:00 '
137
- wrap_shell_code(Util.outdent!(<<-CMD
138
- #{cmd}
139
- schtasks /RUN /TN restart_winrm
140
- CMD
141
- ))
142
- end
143
-
144
- # Returns an Array of test suite filenames for the related suite currently
145
- # residing on the local workstation. Any special provisioner-specific
146
- # directories (such as a Chef roles/ directory) are excluded.
147
- #
148
- # @return [Array<String>] array of suite files
149
- # @api private
150
-
151
- def local_suite_files
152
- base = File.join(test_folder, config[:suite_name])
153
- top_level_glob = File.join(base, "*")
154
- folder_glob = File.join(base, "*/**/*")
155
- top = Dir.glob(top_level_glob)
156
- nested = Dir.glob(folder_glob)
157
- (top << nested).flatten!.reject do |f|
158
- File.directory?(f)
159
- end
160
- end
161
-
162
- # Copies all test suite files into the suites directory in the sandbox.
163
- #
164
- # @api private
165
- def prepare_pester_tests
166
- base = File.join(test_folder, config[:suite_name])
167
- info("Preparing to copy files from #{base} to the SUT.")
168
-
169
- local_suite_files.each do |src|
170
- dest = File.join(sandbox_suites_dir, src.sub("#{base}/", ""))
171
- debug("Copying #{src} to #{dest}")
172
- FileUtils.mkdir_p(File.dirname(dest))
173
- FileUtils.cp(src, dest, preserve: true)
174
- end
175
- end
176
-
177
- # @return [String] path to suites directory under sandbox path
178
- # @api private
179
- def sandbox_suites_dir
180
- File.join(sandbox_path, "suites")
181
- end
182
-
183
- def test_folder
184
- return config[:test_base_path] if config[:test_folder].nil?
185
- absolute_test_folder
186
- end
187
-
188
- def absolute_test_folder
189
- path = (Pathname.new config[:test_folder]).realpath
190
- integration_path = File.join(path, 'integration')
191
- return path unless Dir.exist?(integration_path)
192
- integration_path
193
- end
194
-
195
- end
196
- end
197
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Steven Murawski (<steven.murawski@gmail.com>)
4
+ #
5
+ # Copyright (C) 2015, Steven Murawski
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'pathname'
20
+ require 'kitchen/verifier/base'
21
+ require 'kitchen/verifier/pester_version'
22
+
23
+ module Kitchen
24
+
25
+ module Verifier
26
+
27
+ class Pester < Kitchen::Verifier::Base
28
+
29
+ kitchen_verifier_api_version 1
30
+
31
+ plugin_version Kitchen::Verifier::PESTER_VERSION
32
+
33
+ default_config :restart_winrm, false
34
+ default_config :test_folder
35
+ default_config :run_as_scheduled_task, false
36
+ default_config :use_local_pester_module, false
37
+
38
+ # Creates a new Verifier object using the provided configuration data
39
+ # which will be merged with any default configuration.
40
+ #
41
+ # @param config [Hash] provided verifier configuration
42
+ def initialize(config = {})
43
+ init_config(config)
44
+ end
45
+
46
+ # Creates a temporary directory on the local workstation into which
47
+ # verifier related files and directories can be copied or created. The
48
+ # contents of this directory will be copied over to the instance before
49
+ # invoking the verifier's run command. After this method completes, it
50
+ # is expected that the contents of the sandbox is complete and ready for
51
+ # copy to the remote instance.
52
+ #
53
+ # **Note:** any subclasses would be well advised to call super first when
54
+ # overriding this method, for example:
55
+ #
56
+ # @example overriding `#create_sandbox`
57
+ #
58
+ # class MyVerifier < Kitchen::Verifier::Base
59
+ # def create_sandbox
60
+ # super
61
+ # # any further file copies, preparations, etc.
62
+ # end
63
+ # end
64
+ def create_sandbox
65
+ super
66
+ prepare_powershell_modules
67
+ prepare_pester_tests
68
+ end
69
+
70
+ # Generates a command string which will install and configure the
71
+ # verifier software on an instance. If no work is required, then `nil`
72
+ # will be returned.
73
+ #
74
+ # @return [String] a command string
75
+ def install_command
76
+ return if local_suite_files.empty?
77
+ return if config[:use_local_pester_module]
78
+
79
+ really_wrap_shell_code(install_command_script)
80
+ end
81
+
82
+ # Generates a command string which will perform any data initialization
83
+ # or configuration required after the verifier software is installed
84
+ # but before the sandbox has been transferred to the instance. If no work
85
+ # is required, then `nil` will be returned.
86
+ #
87
+ # @return [String] a command string
88
+ def init_command
89
+ restart_winrm_service if config[:restart_winrm]
90
+ end
91
+
92
+ # Generates a command string which will perform any commands or
93
+ # configuration required just before the main verifier run command but
94
+ # after the sandbox has been transferred to the instance. If no work is
95
+ # required, then `nil` will be returned.
96
+ #
97
+ # @return [String] a command string
98
+ def prepare_command
99
+ end
100
+
101
+ # Generates a command string which will invoke the main verifier
102
+ # command on the prepared instance. If no work is required, then `nil`
103
+ # will be returned.
104
+ #
105
+ # @return [String] a command string
106
+ def run_command
107
+ return if local_suite_files.empty?
108
+
109
+ cmd = if config[:run_as_scheduled_task]
110
+ wrap_scheduled_task('verify-run', run_command_script)
111
+ else
112
+ run_command_script
113
+ end
114
+
115
+ really_wrap_shell_code(cmd)
116
+ end
117
+
118
+ #private
119
+ def run_command_script
120
+ <<-CMD
121
+ $TestPath = "#{File.join(config[:root_path], 'pester')}";
122
+ import-module Pester -force;
123
+ $result = invoke-pester -path $testpath -passthru ;
124
+ $result |
125
+ export-clixml (join-path $testpath 'result.xml');
126
+ $host.setshouldexit($result.failedcount)
127
+ CMD
128
+ end
129
+
130
+ def really_wrap_shell_code(code)
131
+ wrap_shell_code(Util.outdent!(use_local_powershell_modules(code)))
132
+ end
133
+
134
+ def use_local_powershell_modules(script)
135
+ <<-EOH
136
+ set-executionpolicy unrestricted -force;
137
+ $global:ProgressPreference = 'SilentlyContinue'
138
+ #{"$VerbosePreference = 'Continue'" if instance.logger.logdev.level == 0}
139
+ $env:psmodulepath += ";$(join-path (resolve-path $env:temp).path 'verifier/modules')";
140
+ # $env:psmodulepath -split ';' | % {write-output "PSModulePath contains:"} {write-output "`t$_"}
141
+ #{script}
142
+ EOH
143
+ end
144
+
145
+ def random_string
146
+ (0...8).map { (65 + rand(26)).chr }.join
147
+ end
148
+
149
+ def wrap_scheduled_task (name, script)
150
+ randomized_name = "#{name}-#{random_string}"
151
+ <<-EOH
152
+ import-module NamedPipes, ScheduledTaskRunner, PesterUtil
153
+ $Action = @'
154
+ #{script}
155
+ '@
156
+ $ScriptBlock = [scriptblock]::Create($action)
157
+ Add-ScheduledTaskCommand -name #{randomized_name} -Action $ScriptBlock
158
+ Invoke-ScheduledTaskCommand -name #{randomized_name}
159
+ $ExitCode = Get-ScheduledTaskExitCode -name #{randomized_name}
160
+ Remove-ScheduledTaskCommand -name #{randomized_name}
161
+ $TestResultPath = "#{File.join(config[:root_path], 'pester/result.xml')}"
162
+ $TestResults = import-clixml $TestResultPath
163
+ Write-Host
164
+ ConvertFrom-PesterOutputObject $TestResults
165
+ Write-Host
166
+ $host.SetShouldExit($ExitCode)
167
+ EOH
168
+ end
169
+
170
+ def install_command_script
171
+ <<-EOH
172
+ function directory($path){
173
+ if (test-path $path) {(resolve-path $path).providerpath}
174
+ else {(resolve-path (mkdir $path)).providerpath}
175
+ }
176
+ $VerifierModulePath = directory $env:temp/verifier/modules
177
+ $VerifierTestsPath = directory $env:temp/verifier/pester
178
+
179
+ function test-module($module){
180
+ (get-module $module -list) -ne $null
181
+ }
182
+ if (-not (test-module pester)) {
183
+ if (test-module PowerShellGet){
184
+ import-module PowerShellGet -force
185
+ import-module PackageManagement -force
186
+ get-packageprovider -name NuGet -force | out-null
187
+ install-module Pester -force
188
+ }
189
+ else {
190
+ if (-not (test-module PsGet)){
191
+ iex (new-object Net.WebClient).DownloadString('http://bit.ly/GetPsGet')
192
+ }
193
+ try {
194
+ import-module psget -force -erroraction stop
195
+ Install-Module Pester
196
+ }
197
+ catch {
198
+ Write-Output "Installing from Github"
199
+ $zipfile = join-path(resolve-path "$env:temp/verifier") "pester.zip"
200
+ if (-not (test-path $zipfile)){
201
+ $source = 'https://github.com/pester/Pester/archive/3.3.14.zip'
202
+ [byte[]]$bytes = (new-object System.net.WebClient).DownloadData($source)
203
+ [IO.File]::WriteAllBytes($zipfile, $bytes)
204
+ $bytes = $null
205
+ [gc]::collect()
206
+ write-output "Downloaded Pester.zip"
207
+ }
208
+ write-output "Creating Shell.Application COM object"
209
+ $shellcom = new-object -com shell.application
210
+ Write-Output "Creating COM object for zip file."
211
+ $zipcomobject = $shellcom.namespace($zipfile)
212
+ Write-Output "Creating COM object for module destination."
213
+ $destination = $shellcom.namespace($VerifierModulePath)
214
+ Write-Output "Unpacking zip file."
215
+ $destination.CopyHere($zipcomobject.Items(), 0x610)
216
+ rename-item (join-path $VerifierModulePath "Pester-3.3.14") -newname 'Pester' -force
217
+ }
218
+ }
219
+ }
220
+ if (-not (test-module Pester)) {
221
+ throw "Unable to install Pester. Please include Pester in your base image or install during your converge."
222
+ }
223
+ EOH
224
+ end
225
+
226
+ def restart_winrm_service
227
+
228
+ cmd = 'schtasks /Create /TN restart_winrm /TR ' \
229
+ '"powershell -command restart-service winrm" ' \
230
+ '/SC ONCE /ST 00:00 '
231
+ wrap_shell_code(Util.outdent!(<<-CMD
232
+ #{cmd}
233
+ schtasks /RUN /TN restart_winrm
234
+ CMD
235
+ ))
236
+ end
237
+
238
+ # Returns an Array of test suite filenames for the related suite currently
239
+ # residing on the local workstation. Any special provisioner-specific
240
+ # directories (such as a Chef roles/ directory) are excluded.
241
+ #
242
+ # @return [Array<String>] array of suite files
243
+ # @api private
244
+
245
+ def suite_test_folder
246
+ @suite_test_folder ||= File.join(test_folder, config[:suite_name])
247
+ end
248
+
249
+ def suite_level_glob
250
+ Dir.glob(File.join(suite_test_folder, "*"))
251
+ end
252
+
253
+ def suite_verifier_level_glob
254
+ Dir.glob(File.join(suite_test_folder, "*/**/*"))
255
+ end
256
+
257
+ def local_suite_files
258
+ suite = suite_level_glob
259
+ suite_verifier = suite_verifier_level_glob
260
+ (suite << suite_verifier).flatten!.reject do |f|
261
+ File.directory?(f)
262
+ end
263
+ end
264
+
265
+ def sandboxify_path(path)
266
+ File.join(sandbox_path, path.sub("#{suite_test_folder}/", ""))
267
+ end
268
+
269
+ # Copies all test suite files into the suites directory in the sandbox.
270
+ #
271
+ # @api private
272
+ def prepare_pester_tests
273
+ info("Preparing to copy files from #{suite_test_folder} to the SUT.")
274
+
275
+ local_suite_files.each do |src|
276
+ dest = sandboxify_path(src)
277
+ debug("Copying #{src} to #{dest}")
278
+ FileUtils.mkdir_p(File.dirname(dest))
279
+ FileUtils.cp(src, dest, preserve: true)
280
+ end
281
+ end
282
+
283
+ def prepare_powershell_module(name)
284
+ FileUtils.mkdir_p(File.join(sandbox_path, "modules/#{name}"))
285
+ FileUtils.cp(File.join(File.dirname(__FILE__), "../../support/powershell/#{name}/#{name}.psm1"), File.join(sandbox_path, "modules/#{name}/#{name}.psm1"), preserve: true)
286
+ end
287
+
288
+ def prepare_powershell_modules
289
+ info("Preparing to copy supporting powershell modules.")
290
+ %w[NamedPipes ScheduledTaskRunner PesterUtil].each do |module_name|
291
+ prepare_powershell_module module_name
292
+ end
293
+
294
+ end
295
+
296
+ def test_folder
297
+ return config[:test_base_path] if config[:test_folder].nil?
298
+ absolute_test_folder
299
+ end
300
+
301
+ def absolute_test_folder
302
+ path = (Pathname.new config[:test_folder]).realpath
303
+ integration_path = File.join(path, 'integration')
304
+ return path unless Dir.exist?(integration_path)
305
+ integration_path
306
+ end
307
+
308
+ end
309
+ end
310
+ end
@@ -1,5 +1,5 @@
1
- module Kitchen
2
- module Verifier
3
- PESTER_VERSION = '0.5.0'
4
- end
1
+ module Kitchen
2
+ module Verifier
3
+ PESTER_VERSION = '0.7.0'
4
+ end
5
5
  end
@@ -0,0 +1,134 @@
1
+ Add-Type -AssemblyName System.Core;
2
+
3
+ $script:NamedPipes = @()
4
+
5
+ function New-NamedPipe {
6
+ param (
7
+ [parameter(Mandatory=$true)]
8
+ [ValidateSet('In', 'Out')]
9
+ [string]
10
+ $Direction,
11
+ [parameter(Mandatory=$true)]
12
+ [ValidateSet('Client', 'Server')]
13
+ [string]
14
+ $Role,
15
+ [parameter(Mandatory=$true)]
16
+ [ValidateNotNullOrEmpty()]
17
+ [string]
18
+ $Name,
19
+ [switch]
20
+ $Quiet
21
+ )
22
+ try {
23
+ $pipeReader = $null
24
+ $pipeWriter = $null
25
+
26
+ if ($Role -like 'Server') {
27
+ $pipe = new-object -ErrorAction Stop System.IO.Pipes.NamedPipeServerStream($Name,
28
+ [System.IO.Pipes.PipeDirection]::$Direction)
29
+ }
30
+ else {
31
+ $pipe = new-object -ErrorAction Stop System.IO.Pipes.NamedPipeClientStream($env:ComputerName,
32
+ $Name, [System.IO.Pipes.PipeDirection]::$Direction)
33
+ }
34
+ if ($Direction -like 'In') {
35
+ $pipeReader = new-object System.IO.StreamReader($pipe)
36
+ }
37
+ else {
38
+ $pipeWriter = new-object System.IO.StreamWriter($pipe)
39
+ }
40
+ $output = new-object PSObject -property @{
41
+ Name = $Name
42
+ NamedPipe = $pipe
43
+ Role = $Role
44
+ Direction = $Direction
45
+ PipeReader = $pipeReader
46
+ PipeWriter = $pipeWriter
47
+ }
48
+ $script:NamedPipes += $output
49
+ if (-not $Quiet) {$output}
50
+ }
51
+ catch {
52
+ throw "Failed to create the named pipe."
53
+ }
54
+ }
55
+
56
+ function Get-NamedPipe {
57
+ param (
58
+ $Name = '*',
59
+ $Role = '*'
60
+ )
61
+ $pipe = $null
62
+ $pipe = $script:NamedPipes |
63
+ where {$_.name -like $Name -and $_.role -like $Role}
64
+ if ($pipe -eq $null) { throw "Unable to find pipe $Name" }
65
+ return $pipe
66
+ }
67
+
68
+ function Start-NamedPipeServer {
69
+ param ($Name)
70
+ $PipeServer = Get-NamedPipe -Name $Name -Role Server
71
+ $PipeServer.NamedPipe.WaitForConnection()
72
+ Write-Host "Named Pipe $Name is connected."
73
+ }
74
+
75
+ function Connect-NamedPipeClient {
76
+ param ($Name)
77
+ $PipeClient = Get-NamedPipe -Name $Name -Role Client
78
+ $PipeClient.NamedPipe.Connect()
79
+ if ($PipeClient.PipeWriter -ne $null) {
80
+ $PipeClient.pipeWriter.AutoFlush = $true
81
+ }
82
+ }
83
+
84
+ function Write-NamedPipe {
85
+ param(
86
+ [parameter(Mandatory = $true)]
87
+ [ValidateNotNullOrEmpty()]
88
+ [string]
89
+ $Name,
90
+ [parameter(ValueFromPipeline=$true)]
91
+ [string]
92
+ $Content)
93
+ begin {
94
+ $Pipe = Get-NamedPipe -Name $Name |
95
+ where {$_.PipeWriter -ne $null}
96
+ }
97
+ process {
98
+ if ($Pipe.NamedPipe.IsConnected){
99
+ $Pipe.PipeWriter.Writeline($Content)
100
+ }
101
+ }
102
+ }
103
+
104
+ function Read-NamedPipe {
105
+ param ($Name)
106
+ $Pipe = Get-NamedPipe -Name $Name | where {$_.PipeReader -ne $null}
107
+ while ($Pipe.NamedPipe.IsConnected) {
108
+ $output = $Pipe.PipeReader.ReadLine()
109
+ if ($output -like "STOP READING $Name") {
110
+ break;
111
+ }
112
+ $output
113
+ }
114
+ }
115
+
116
+ function Send-StopReadingCommand {
117
+ param ($Name)
118
+ Write-NamedPipe -Name $Name -Content "STOP READING $Name"
119
+ }
120
+
121
+ function Remove-NamedPipe {
122
+ param ($name)
123
+ Get-NamedPipe -Name $Name |
124
+ foreach {
125
+ if ($_.Pipewriter -ne $null) {
126
+ $_.PipeWriter.dispose()
127
+ }
128
+ if ($_.Pipereader -ne $null) {
129
+ $_.Pipereader.dispose()
130
+ }
131
+ $_.NamedPipe.dispose()
132
+ }
133
+ $script:NamedPipes = $script:NamedPipes | where {$_.name -notlike $Name}
134
+ }
@@ -0,0 +1,24 @@
1
+ function ConvertFrom-PesterOutputObject {
2
+ param (
3
+ [parameter(ValueFromPipeline=$true)]
4
+ [object]
5
+ $InputObject
6
+ )
7
+ begin {
8
+ $PesterModule = Import-Module Pester -Passthru
9
+ }
10
+ process {
11
+ $DescribeGroup = $InputObject.testresult | Group-Object Describe
12
+ foreach ($DescribeBlock in $DescribeGroup) {
13
+ $PesterModule.Invoke({Write-Screen $args[0]}, "Describing $($DescribeBlock.Name)")
14
+ $ContextGroup = $DescribeBlock.group | Group-Object Context
15
+ foreach ($ContextBlock in $ContextGroup) {
16
+ $PesterModule.Invoke({Write-Screen $args[0]}, "`tContext $($subheader.name)")
17
+ foreach ($TestResult in $ContextBlock.group) {
18
+ $PesterModule.Invoke({Write-PesterResult $args[0]}, $TestResult)
19
+ }
20
+ }
21
+ }
22
+ $PesterModule.Invoke({Write-PesterReport $args[0]}, $InputObject)
23
+ }
24
+ }
@@ -0,0 +1,92 @@
1
+ import-module NamedPipes -force
2
+
3
+ $script:TaskLastExitCode = @{}
4
+
5
+ function Add-ScheduledTaskCommand {
6
+ param ([string]$Name, [scriptblock]$Action)
7
+ $command = @"
8
+ `$env:temp = '$env:temp';
9
+ `$env:psmodulepath = '$env:psmodulepath';
10
+ import-module NamedPipes -force;
11
+
12
+ # Set up named pipe
13
+ start-sleep -seconds 5
14
+
15
+ New-NamedPipe -Role Client -Direction Out -Quiet -Name 'kitchen-$name';
16
+ Connect-NamedPipeClient -Name 'kitchen-$name';
17
+
18
+ # Run the real action and send it down the named pipe
19
+ try {
20
+ `$ActionString = @'
21
+ $($action.ToString())
22
+ '@
23
+
24
+ [scriptblock]::create(`$ActionString).InvokeReturnAsIs() |
25
+ write-NamedPipe -Name 'kitchen-$name'
26
+ }
27
+ catch [Exception] {
28
+ write-NamedPipe -name 'kitchen-$name' -content `$_.exception.message
29
+ Write-NamedPipe -Name 'kitchen-$name' -content `$_.exception.stacktrace
30
+ }
31
+ finally {
32
+ # Close out named pipe
33
+ Send-StopReadingCommand -Name 'kitchen-$name';
34
+ Remove-NamedPipe -Name 'kitchen-$name';
35
+ }
36
+ "@
37
+
38
+ try {
39
+ $ActionWithEnvironment = [scriptblock]::Create($command)
40
+ }
41
+ catch {
42
+ Write-Output "Failed to validate: "
43
+ Write-Output $command
44
+ throw $_.exception
45
+ }
46
+
47
+ $ActionWithEnvironment.ToString() | Out-file "$env:temp/$Name.ps1"
48
+ if (test-path "$env:temp/$name.ps1") {
49
+ schtasks /create /tn "kitchen-$name" /ru System /sc daily /st 00:00 /rl HIGHEST /f /tr "powershell -noprofile -executionpolicy unrestricted -file $env:temp/$name.ps1" | Out-Null
50
+ Write-Host "`tCreated Scheduled Task $Name."
51
+ }
52
+ else {
53
+ throw "failed to create scheduled task command."
54
+ }
55
+ }
56
+
57
+ function Remove-ScheduledTaskCommand {
58
+ param ([string]$Name)
59
+ schtasks /delete /tn "kitchen-$name" /f | Out-Null
60
+ Write-Host "`tDeleted Scheduld Task $Name."
61
+ }
62
+
63
+ function Get-ScheduledTaskExitCode {
64
+ param ([string]$Name)
65
+ (Get-ScheduledTaskStatus -name $name).'Last Result' |
66
+ where {-not [string]::IsNullOrEmpty($_)} |
67
+ foreach {[int]::Parse($_.trim())}
68
+ }
69
+
70
+ function Get-ScheduledTaskStatus {
71
+ param ([string]$Name)
72
+ $task = schtasks /query /tn "kitchen-$name" /fo csv /v |
73
+ ConvertFrom-Csv
74
+ $task
75
+ }
76
+
77
+ function Invoke-ScheduledTaskCommand {
78
+ param ([string]$Name)
79
+ try {
80
+ new-namedpipe -Role Server -Direction In -Quiet -Name "kitchen-$name"
81
+ schtasks /run /tn "kitchen-$name" | Out-Null
82
+ Write-Host "`tRunning Scheduled Task $Name."
83
+ Start-NamedPipeServer -Name "kitchen-$name"
84
+ Read-NamedPipe -Name "kitchen-$name"
85
+ }
86
+ finally {
87
+ Remove-NamedPipe -name "kitchen-$name"
88
+ while ((Get-ScheduledTaskStatus -name $name).Status -notlike 'Ready') {
89
+ start-sleep -seconds 1
90
+ }
91
+ }
92
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-pester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Murawski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-09 00:00:00.000000000 Z
11
+ date: 2016-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -81,6 +81,9 @@ files:
81
81
  - kitchen-pester.gemspec
82
82
  - lib/kitchen/verifier/pester.rb
83
83
  - lib/kitchen/verifier/pester_version.rb
84
+ - lib/support/powershell/NamedPipes/NamedPipes.psm1
85
+ - lib/support/powershell/PesterUtil/PesterUtil.psm1
86
+ - lib/support/powershell/ScheduledTaskRunner/ScheduledTaskRunner.psm1
84
87
  homepage: https://github.com/test-kitchen/kitchen-pester
85
88
  licenses:
86
89
  - Apache 2