puppet 4.5.2-universal-darwin → 4.5.3-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

@@ -115,18 +115,44 @@ describe 'The string converter' do
115
115
  expect(converter.convert('3.1415', string_formats)).to eq('3.1415')
116
116
  end
117
117
 
118
- it 'The %p format for string produces quoted string' do
119
- string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%p'}
120
- expect(converter.convert("hello\tworld", string_formats)).to eq('"hello\\tworld"')
118
+ context 'The %p format for string produces' do
119
+ let!(:string_formats) { { Puppet::Pops::Types::PStringType::DEFAULT => '%p'} }
120
+ it 'double quoted result for string that contains control characters' do
121
+ expect(converter.convert("hello\tworld.\r\nSun is brigth today.", string_formats)).to eq('"hello\\tworld.\\r\\nSun is brigth today."')
122
+ end
123
+
124
+ it 'singe quoted result for string that is plain ascii without \\, $ or control characters' do
125
+ expect(converter.convert('hello world', string_formats)).to eq("'hello world'")
126
+ end
127
+
128
+ it 'quoted 5-byte unicode chars' do
129
+ expect(converter.convert("smile \u{1f603}.", string_formats)).to eq('"smile \\u{1F603}."')
130
+ end
131
+
132
+ it 'quoted 2-byte unicode chars' do
133
+ expect(converter.convert("esc \u{1b}.", string_formats)).to eq('"esc \\u{1B}."')
134
+ end
135
+
136
+ it 'escape for $' do
137
+ expect(converter.convert('escape the $ sign', string_formats)).to eq('"escape the \$ sign"')
138
+ end
139
+
140
+ it 'escape for double qoute but not for single quote' do
141
+ expect(converter.convert('the \' single and " double quote', string_formats)).to eq('"the \' single and \\" double quote"')
142
+ end
143
+
144
+ it 'no escape for #' do
145
+ expect(converter.convert('do not escape #{x}', string_formats)).to eq('\'do not escape #{x}\'')
146
+ end
121
147
  end
122
148
 
123
149
  {
124
150
  '%s' => 'hello::world',
125
- '%p' => '"hello::world"',
151
+ '%p' => "'hello::world'",
126
152
  '%c' => 'Hello::world',
127
- '%#c' => '"Hello::world"',
153
+ '%#c' => "'Hello::world'",
128
154
  '%u' => 'HELLO::WORLD',
129
- '%#u' => '"HELLO::WORLD"',
155
+ '%#u' => "'HELLO::WORLD'",
130
156
  }.each do |fmt, result |
131
157
  it "the format #{fmt} produces #{result}" do
132
158
  string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => fmt}
@@ -136,9 +162,9 @@ describe 'The string converter' do
136
162
 
137
163
  {
138
164
  '%c' => 'Hello::world',
139
- '%#c' => '"Hello::world"',
165
+ '%#c' => "'Hello::world'",
140
166
  '%d' => 'hello::world',
141
- '%#d' => '"hello::world"',
167
+ '%#d' => "'hello::world'",
142
168
  }.each do |fmt, result |
143
169
  it "the format #{fmt} produces #{result}" do
144
170
  string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => fmt}
@@ -146,6 +172,18 @@ describe 'The string converter' do
146
172
  end
147
173
  end
148
174
 
175
+ {
176
+ [nil, '%.1p'] => 'u',
177
+ [nil, '%#.2p'] => '"u',
178
+ [:default, '%.1p'] => 'd',
179
+ [true, '%.2s'] => 'tr',
180
+ [true, '%.2y'] => 'ye',
181
+ }.each do |args, result |
182
+ it "the format #{args[1]} produces #{result} for value #{args[0]}" do
183
+ expect(converter.convert(*args)).to eq(result)
184
+ end
185
+ end
186
+
149
187
  {
150
188
  ' a b ' => 'a b',
151
189
  'a b ' => 'a b',
@@ -158,9 +196,9 @@ describe 'The string converter' do
158
196
  end
159
197
 
160
198
  {
161
- ' a b ' => '"a b"',
162
- 'a b ' => '"a b"',
163
- ' a b' => '"a b"',
199
+ ' a b ' => "'a b'",
200
+ 'a b ' => "'a b'",
201
+ ' a b' => "'a b'",
164
202
  }.each do |input, result |
165
203
  it "the input #{input} is trimmed to #{result} by using format '%#t'" do
166
204
  string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => '%#t'}
@@ -192,15 +230,13 @@ describe 'The string converter' do
192
230
 
193
231
  {
194
232
  '%4.2s' => ' he',
195
- '%4.2p' => ' "h',
233
+ '%4.2p' => " 'h",
196
234
  '%4.2c' => ' He',
197
- '%#4.2c' => ' "H',
235
+ '%#4.2c' => " 'H",
198
236
  '%4.2u' => ' HE',
199
- '%#4.2u' => ' "H',
200
- '%4.2c' => ' He',
201
- '%#4.2c' => ' "H',
237
+ '%#4.2u' => " 'H",
202
238
  '%4.2d' => ' he',
203
- '%#4.2d' => ' "h',
239
+ '%#4.2d' => " 'h"
204
240
  }.each do |fmt, result |
205
241
  it "width and precision can be combined with #{fmt}" do
206
242
  string_formats = { Puppet::Pops::Types::PStringType::DEFAULT => fmt}
@@ -580,30 +616,30 @@ describe 'The string converter' do
580
616
 
581
617
  context 'when converting array' do
582
618
  it 'the default string representation is using [] delimiters, joins with ',' and uses %p for values' do
583
- expect(converter.convert(["hello", "world"], :default)).to eq('["hello", "world"]')
619
+ expect(converter.convert(["hello", "world"], :default)).to eq("['hello', 'world']")
584
620
  end
585
621
 
586
- { "%s" => '[1, "hello"]',
587
- "%p" => '[1, "hello"]',
588
- "%a" => '[1, "hello"]',
589
- "%<a" => '<1, "hello">',
590
- "%[a" => '[1, "hello"]',
591
- "%(a" => '(1, "hello")',
592
- "%{a" => '{1, "hello"}',
593
- "% a" => '1, "hello"',
622
+ { "%s" => "[1, 'hello']",
623
+ "%p" => "[1, 'hello']",
624
+ "%a" => "[1, 'hello']",
625
+ "%<a" => "<1, 'hello'>",
626
+ "%[a" => "[1, 'hello']",
627
+ "%(a" => "(1, 'hello')",
628
+ "%{a" => "{1, 'hello'}",
629
+ "% a" => "1, 'hello'",
594
630
 
595
631
  {'format' => '%(a',
596
632
  'separator' => ''
597
- } => '(1 "hello")',
633
+ } => "(1 'hello')",
598
634
 
599
635
  {'format' => '%|a',
600
636
  'separator' => ''
601
- } => '|1 "hello"|',
637
+ } => "|1 'hello'|",
602
638
 
603
- {'format' => '%(a',
604
- 'separator' => '',
639
+ {'format' => '%(a',
640
+ 'separator' => '',
605
641
  'string_formats' => {Puppet::Pops::Types::PIntegerType::DEFAULT => '%#x'}
606
- } => '(0x1 "hello")',
642
+ } => "(0x1 'hello')",
607
643
  }.each do |fmt, result |
608
644
  it "the format #{fmt} produces #{result}" do
609
645
  string_formats = { Puppet::Pops::Types::PArrayType::DEFAULT => fmt}
@@ -614,7 +650,7 @@ describe 'The string converter' do
614
650
  it "multiple rules selects most specific" do
615
651
  short_array_t = factory.array_of(factory.integer, factory.range(1,2))
616
652
  long_array_t = factory.array_of(factory.integer, factory.range(3,100))
617
- string_formats = {
653
+ string_formats = {
618
654
  short_array_t => "%(a",
619
655
  long_array_t => "%[a",
620
656
  }
@@ -715,27 +751,27 @@ describe 'The string converter' do
715
751
 
716
752
  context 'when converting hash' do
717
753
  it 'the default string representation is using {} delimiters, joins with '=>' and uses %p for values' do
718
- expect(converter.convert({"hello" => "world"}, :default)).to eq('{"hello" => "world"}')
754
+ expect(converter.convert({"hello" => "world"}, :default)).to eq("{'hello' => 'world'}")
719
755
  end
720
756
 
721
- { "%s" => '{1 => "world"}',
722
- "%p" => '{1 => "world"}',
723
- "%h" => '{1 => "world"}',
724
- "%a" => '[[1, "world"]]',
725
- "%<h" => '<1 => "world">',
726
- "%[h" => '[1 => "world"]',
727
- "%(h" => '(1 => "world")',
728
- "%{h" => '{1 => "world"}',
729
- "% h" => '1 => "world"',
757
+ { "%s" => "{1 => 'world'}",
758
+ "%p" => "{1 => 'world'}",
759
+ "%h" => "{1 => 'world'}",
760
+ "%a" => "[[1, 'world']]",
761
+ "%<h" => "<1 => 'world'>",
762
+ "%[h" => "[1 => 'world']",
763
+ "%(h" => "(1 => 'world')",
764
+ "%{h" => "{1 => 'world'}",
765
+ "% h" => "1 => 'world'",
730
766
 
731
767
  {'format' => '%(h',
732
768
  'separator2' => ' '
733
- } => '(1 "world")',
769
+ } => "(1 'world')",
734
770
 
735
- {'format' => '%(h',
736
- 'separator2' => ' ',
771
+ {'format' => '%(h',
772
+ 'separator2' => ' ',
737
773
  'string_formats' => {Puppet::Pops::Types::PIntegerType::DEFAULT => '%#x'}
738
- } => '(0x1 "world")',
774
+ } => "(0x1 'world')",
739
775
  }.each do |fmt, result |
740
776
  it "the format #{fmt} produces #{result}" do
741
777
  string_formats = { Puppet::Pops::Types::PHashType::DEFAULT => fmt}
@@ -743,17 +779,17 @@ describe 'The string converter' do
743
779
  end
744
780
  end
745
781
 
746
- { "%s" => '{1 => "hello", 2 => "world"}',
782
+ { "%s" => "{1 => 'hello', 2 => 'world'}",
747
783
 
748
784
  {'format' => '%(h',
749
785
  'separator2' => ' '
750
- } => '(1 "hello", 2 "world")',
786
+ } => "(1 'hello', 2 'world')",
751
787
 
752
- {'format' => '%(h',
753
- 'separator' => ' >>',
754
- 'separator2' => ' <=> ',
788
+ {'format' => '%(h',
789
+ 'separator' => ' >>',
790
+ 'separator2' => ' <=> ',
755
791
  'string_formats' => {Puppet::Pops::Types::PIntegerType::DEFAULT => '%#x'}
756
- } => '(0x1 <=> "hello" >> 0x2 <=> "world")',
792
+ } => "(0x1 <=> 'hello' >> 0x2 <=> 'world')",
757
793
  }.each do |fmt, result |
758
794
  it "the format #{fmt} produces #{result}" do
759
795
  string_formats = { Puppet::Pops::Types::PHashType::DEFAULT => fmt}
@@ -770,9 +806,9 @@ describe 'The string converter' do
770
806
  # formatting matters here
771
807
  result = [
772
808
  "{",
773
- " 1 => \"hello\",",
809
+ " 1 => 'hello',",
774
810
  " 2 => {",
775
- " 3 => \"world\"",
811
+ " 3 => 'world'",
776
812
  " }",
777
813
  "}"
778
814
  ].join("\n")
@@ -782,7 +818,7 @@ describe 'The string converter' do
782
818
 
783
819
  context "containing an array" do
784
820
  it 'the hash and array renders without breaks and indentation by default' do
785
- result = '{1 => [1, 2, 3]}'
821
+ result = "{1 => [1, 2, 3]}"
786
822
  formatted = converter.convert({ 1 => [1, 2, 3] }, :default)
787
823
  expect(formatted).to eq(result)
788
824
  end
@@ -353,7 +353,7 @@ describe 'Puppet Type System' do
353
353
  end
354
354
 
355
355
  it 'can be created with a runtime and, puppet name pattern, and runtime replacement' do
356
- expect(tf.runtime('ruby', [/^MyPackage::(.*)$/, 'MyModule::\1']).to_s).to eq("Runtime[ruby, [/^MyPackage::(.*)$/, 'MyModule::\\1']]")
356
+ expect(tf.runtime('ruby', [/^MyPackage::(.*)$/, 'MyModule::\1']).to_s).to eq("Runtime[ruby, [/^MyPackage::(.*)$/, \"MyModule::\\\\1\"]]")
357
357
  end
358
358
 
359
359
  it 'will map a Puppet name to a runtime type' do
@@ -74,6 +74,10 @@ describe Puppet::Type.type(:group).provider(:windows_adsi), :if => Puppet.featur
74
74
  resource[:auth_membership] = true
75
75
  end
76
76
 
77
+ it "should return true when current and should contain the same users in a different order" do
78
+ expect(provider.members_insync?(['user1', 'user2', 'user3'], ['user3', 'user1', 'user2'])).to be_truthy
79
+ end
80
+
77
81
  it "should return false when current is nil" do
78
82
  expect(provider.members_insync?(nil, ['user2'])).to be_falsey
79
83
  end
@@ -5,6 +5,26 @@ require 'spec_helper'
5
5
 
6
6
  provider_class = Puppet::Type.type(:package).provider(:dnf)
7
7
 
8
+ context 'default' do
9
+ [ 19, 20, 21 ].each do |ver|
10
+ it "should not be the default provider on fedora#{ver}" do
11
+ Facter.stubs(:value).with(:osfamily).returns(:redhat)
12
+ Facter.stubs(:value).with(:operatingsystem).returns(:fedora)
13
+ Facter.stubs(:value).with(:operatingsystemmajrelease).returns("#{ver}")
14
+ expect(provider_class).to_not be_default
15
+ end
16
+ end
17
+
18
+ [ 22, 23, 24 ].each do |ver|
19
+ it "should be the default provider on fedora#{ver}" do
20
+ Facter.stubs(:value).with(:osfamily).returns(:redhat)
21
+ Facter.stubs(:value).with(:operatingsystem).returns(:fedora)
22
+ Facter.stubs(:value).with(:operatingsystemmajrelease).returns("#{ver}")
23
+ expect(provider_class).to be_default
24
+ end
25
+ end
26
+ end
27
+
8
28
  describe provider_class do
9
29
  let(:name) { 'mypackage' }
10
30
  let(:resource) do
@@ -600,16 +600,6 @@ describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if
600
600
 
601
601
  expect(resource.provider.exists?).to eq(true)
602
602
  end
603
-
604
- it "uses SystemRoot in the path of Task folder" do
605
- Win32::TaskScheduler.unstub(:new)
606
- exists_test_task = Win32::TaskScheduler.new
607
- Dir.stubs(:foreach).with('D:/WinNT/Tasks').returns([])
608
-
609
- Puppet::Util.withenv('SystemRoot' => 'D:/WinNT') do
610
- exists_test_task.exists?('Test Task')
611
- end
612
- end
613
603
  end
614
604
 
615
605
  describe '#clear_task' do
@@ -1583,6 +1573,7 @@ describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if
1583
1573
  end
1584
1574
 
1585
1575
  it 'should save the task' do
1576
+ @mock_task.expects(:set_account_information).with(nil, nil)
1586
1577
  @mock_task.expects(:save)
1587
1578
 
1588
1579
  resource.provider.flush
@@ -1856,4 +1847,200 @@ describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if
1856
1847
  resource.provider.create
1857
1848
  end
1858
1849
  end
1850
+
1851
+ describe "Win32::TaskScheduler", :if => Puppet.features.microsoft_windows? do
1852
+
1853
+ let(:name) { SecureRandom.uuid }
1854
+
1855
+ describe 'sets appropriate generic trigger defaults' do
1856
+ before(:each) do
1857
+ @now = Time.now
1858
+ Time.stubs(:now).returns(@now)
1859
+ end
1860
+
1861
+ it 'for a ONCE schedule' do
1862
+ task = Win32::TaskScheduler.new(name, { 'trigger_type' => Win32::TaskScheduler::ONCE })
1863
+ expect(task.trigger(0)['start_year']).to eq(@now.year)
1864
+ expect(task.trigger(0)['start_month']).to eq(@now.month)
1865
+ expect(task.trigger(0)['start_day']).to eq(@now.day)
1866
+ end
1867
+
1868
+ it 'for a DAILY schedule' do
1869
+ trigger = {
1870
+ 'trigger_type' => Win32::TaskScheduler::DAILY,
1871
+ 'type' => { 'days_interval' => 1 }
1872
+ }
1873
+ task = Win32::TaskScheduler.new(name, trigger)
1874
+
1875
+ expect(task.trigger(0)['start_year']).to eq(@now.year)
1876
+ expect(task.trigger(0)['start_month']).to eq(@now.month)
1877
+ expect(task.trigger(0)['start_day']).to eq(@now.day)
1878
+ end
1879
+
1880
+ it 'for a WEEKLY schedule' do
1881
+ trigger = {
1882
+ 'trigger_type' => Win32::TaskScheduler::WEEKLY,
1883
+ 'type' => { 'weeks_interval' => 1, 'days_of_week' => 1 }
1884
+ }
1885
+ task = Win32::TaskScheduler.new(name, trigger)
1886
+
1887
+ expect(task.trigger(0)['start_year']).to eq(@now.year)
1888
+ expect(task.trigger(0)['start_month']).to eq(@now.month)
1889
+ expect(task.trigger(0)['start_day']).to eq(@now.day)
1890
+ end
1891
+
1892
+ it 'for a MONTHLYDATE schedule' do
1893
+ trigger = {
1894
+ 'trigger_type' => Win32::TaskScheduler::MONTHLYDATE,
1895
+ 'type' => { 'days' => 1, 'months' => 1 }
1896
+ }
1897
+ task = Win32::TaskScheduler.new(name, trigger)
1898
+
1899
+ expect(task.trigger(0)['start_year']).to eq(@now.year)
1900
+ expect(task.trigger(0)['start_month']).to eq(@now.month)
1901
+ expect(task.trigger(0)['start_day']).to eq(@now.day)
1902
+ end
1903
+
1904
+ it 'for a MONTHLYDOW schedule' do
1905
+ trigger = {
1906
+ 'trigger_type' => Win32::TaskScheduler::MONTHLYDOW,
1907
+ 'type' => { 'weeks' => 1, 'days_of_week' => 1, 'months' => 1 }
1908
+ }
1909
+ task = Win32::TaskScheduler.new(name, trigger)
1910
+
1911
+ expect(task.trigger(0)['start_year']).to eq(@now.year)
1912
+ expect(task.trigger(0)['start_month']).to eq(@now.month)
1913
+ expect(task.trigger(0)['start_day']).to eq(@now.day)
1914
+ end
1915
+ end
1916
+
1917
+ describe 'enforces maximum lengths' do
1918
+ let(:task) { Win32::TaskScheduler.new(name, { 'trigger_type' => Win32::TaskScheduler::ONCE }) }
1919
+
1920
+ it 'on account user name' do
1921
+ expect {
1922
+ task.set_account_information('a' * (Win32::TaskScheduler::MAX_ACCOUNT_LENGTH + 1), 'pass')
1923
+ }.to raise_error(Puppet::Error)
1924
+ end
1925
+
1926
+ it 'on application name' do
1927
+ expect {
1928
+ task.application_name = 'a' * (Win32::TaskScheduler::MAX_PATH + 1)
1929
+ }.to raise_error(Puppet::Error)
1930
+ end
1931
+
1932
+ it 'on parameters' do
1933
+ expect {
1934
+ task.parameters = 'a' * (Win32::TaskScheduler::MAX_PARAMETERS_LENGTH + 1)
1935
+ }.to raise_error(Puppet::Error)
1936
+ end
1937
+
1938
+ it 'on working directory' do
1939
+ expect {
1940
+ task.working_directory = 'a' * (Win32::TaskScheduler::MAX_PATH + 1)
1941
+ }.to raise_error(Puppet::Error)
1942
+ end
1943
+
1944
+ it 'on comment' do
1945
+ expect {
1946
+ task.comment = 'a' * (Win32::TaskScheduler::MAX_COMMENT_LENGTH + 1)
1947
+ }.to raise_error(Puppet::Error)
1948
+ end
1949
+
1950
+ it 'on creator' do
1951
+ expect {
1952
+ task.creator = 'a' * (Win32::TaskScheduler::MAX_ACCOUNT_LENGTH + 1)
1953
+ }.to raise_error(Puppet::Error)
1954
+ end
1955
+ end
1956
+
1957
+ describe '#exists?' do
1958
+ it 'works with Unicode task names' do
1959
+ task_name = name + "\u16A0\u16C7\u16BB" # ᚠᛇᚻ
1960
+
1961
+ begin
1962
+ task = Win32::TaskScheduler.new(task_name, { 'trigger_type' => Win32::TaskScheduler::ONCE })
1963
+ task.save()
1964
+
1965
+ expect(Puppet::FileSystem.exist?("C:\\Windows\\Tasks\\#{task_name}.job")).to be_truthy
1966
+ expect(task.exists?(task_name)).to be_truthy
1967
+ ensure
1968
+ task.delete(task_name) if Win32::TaskScheduler.new.exists?(task_name)
1969
+ end
1970
+ end
1971
+
1972
+ it 'is case insensitive' do
1973
+ task_name = name + 'abc' # name is a guid, but might not have alpha chars
1974
+
1975
+ begin
1976
+ task = Win32::TaskScheduler.new(task_name.upcase, { 'trigger_type' => Win32::TaskScheduler::ONCE })
1977
+ task.save()
1978
+
1979
+ expect(task.exists?(task_name.downcase)).to be_truthy
1980
+ ensure
1981
+ task.delete(task_name) if Win32::TaskScheduler.new.exists?(task_name)
1982
+ end
1983
+ end
1984
+ end
1985
+
1986
+ describe 'does not corrupt tasks' do
1987
+ it 'when setting maximum length values for all settings' do
1988
+ begin
1989
+ task = Win32::TaskScheduler.new(name, { 'trigger_type' => Win32::TaskScheduler::ONCE })
1990
+
1991
+ application_name = 'a' * Win32::TaskScheduler::MAX_PATH
1992
+ parameters = 'b' * Win32::TaskScheduler::MAX_PARAMETERS_LENGTH
1993
+ working_directory = 'c' * Win32::TaskScheduler::MAX_PATH
1994
+ comment = 'd' * Win32::TaskScheduler::MAX_COMMENT_LENGTH
1995
+ creator = 'e' * Win32::TaskScheduler::MAX_ACCOUNT_LENGTH
1996
+
1997
+ task.application_name = application_name
1998
+ task.parameters = parameters
1999
+ task.working_directory = working_directory
2000
+ task.comment = comment
2001
+ task.creator = creator
2002
+
2003
+ # saving and reloading (activating) can induce COM load errors when
2004
+ # file is corrupted, which can happen when the upper bounds of these lengths are set too high
2005
+ task.save()
2006
+ task.activate(name)
2007
+
2008
+ # furthermore, corrupted values may not necessarily be read back properly
2009
+ # note that SYSTEM is always returned as an empty string in account_information
2010
+ expect(task.account_information).to eq('')
2011
+ expect(task.application_name).to eq(application_name)
2012
+ expect(task.parameters).to eq(parameters)
2013
+ expect(task.working_directory).to eq(working_directory)
2014
+ expect(task.comment).to eq(comment)
2015
+ expect(task.creator).to eq(creator)
2016
+ ensure
2017
+ task.delete(name) if Win32::TaskScheduler.new.exists?(name)
2018
+ end
2019
+ end
2020
+
2021
+ it 'by preventing a save() not preceded by a set_account_information()' do
2022
+ begin
2023
+ # creates a default new task with SYSTEM user
2024
+ task = Win32::TaskScheduler.new(name, { 'trigger_type' => Win32::TaskScheduler::ONCE })
2025
+ # save automatically resets the current task
2026
+ task.save()
2027
+
2028
+ # re-activate named task, try to modify, and save
2029
+ task.activate(name)
2030
+ task.application_name = 'c:/windows/system32/notepad.exe'
2031
+
2032
+ expect { task.save() }.to raise_error(Puppet::Error, /Account information must be set on the current task to save it properly/)
2033
+
2034
+ # on a failed save, the current task is still active - add SYSTEM
2035
+ task.set_account_information('', nil)
2036
+ expect(task.save()).to be_instance_of(Win32::TaskScheduler::COM::Task)
2037
+
2038
+ # the most appropriate additional validation here would be to confirm settings with schtasks.exe
2039
+ # but that test can live inside a system-level acceptance test
2040
+ ensure
2041
+ task.delete(name) if Win32::TaskScheduler.new.exists?(name)
2042
+ end
2043
+ end
2044
+ end
2045
+ end
1859
2046
  end