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.
- data/ext/project_data.yaml +12 -10
- data/lib/puppet/parser/scope.rb +17 -5
- data/lib/puppet/pops/evaluator/closure.rb +2 -2
- data/lib/puppet/pops/patterns.rb +4 -0
- data/lib/puppet/pops/types/string_converter.rb +79 -30
- data/lib/puppet/pops/types/type_formatter.rb +1 -2
- data/lib/puppet/pops/types/types.rb +20 -0
- data/lib/puppet/provider/group/windows_adsi.rb +5 -2
- data/lib/puppet/provider/package/dnf.rb +1 -1
- data/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb +7 -0
- data/lib/puppet/provider/user/windows_adsi.rb +7 -4
- data/lib/puppet/reference/configuration.rb +2 -0
- data/lib/puppet/resource/type.rb +1 -1
- data/lib/puppet/util/plist.rb +1 -1
- data/lib/puppet/util/windows/taskscheduler.rb +84 -28
- data/lib/puppet/version.rb +1 -1
- data/spec/integration/parser/parameter_defaults_spec.rb +1 -1
- data/spec/integration/util/windows/security_spec.rb +1 -0
- data/spec/unit/functions/new_spec.rb +56 -5
- data/spec/unit/pops/types/string_converter_spec.rb +91 -55
- data/spec/unit/pops/types/types_spec.rb +1 -1
- data/spec/unit/provider/group/windows_adsi_spec.rb +4 -0
- data/spec/unit/provider/package/dnf_spec.rb +20 -0
- data/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +197 -10
- data/spec/unit/provider/user/windows_adsi_spec.rb +4 -0
- data/spec/unit/resource/type_spec.rb +11 -2
- data/spec/unit/util/windows/sid_spec.rb +1 -0
- data/spec/unit/version_spec.rb +15 -0
- metadata +3556 -3556
data/lib/puppet/util/plist.rb
CHANGED
@@ -45,7 +45,7 @@ module Puppet::Util::Plist
|
|
45
45
|
{:failonfail => true, :combine => true})
|
46
46
|
return parse_plist(plist)
|
47
47
|
rescue Puppet::ExecutionFailure => detail
|
48
|
-
Puppet.warning("Cannot read file #{
|
48
|
+
Puppet.warning("Cannot read file #{file_path}; Puppet is skipping it.\n" + "Details: #{detail}")
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
@@ -158,6 +158,20 @@ module Win32
|
|
158
158
|
|
159
159
|
MAX_RUN_TIMES = TASK_MAX_RUN_TIMES
|
160
160
|
|
161
|
+
# unfortunately MSTask.h does not specify the limits for any settings
|
162
|
+
# so these were determined with some experimentation
|
163
|
+
# if values too large are written, its suspected there may be internal
|
164
|
+
# limits may be exceeded, corrupting the job
|
165
|
+
# used for max application name and path values
|
166
|
+
MAX_PATH = 260
|
167
|
+
# UNLEN from lmcons.h is 256
|
168
|
+
# https://technet.microsoft.com/it-it/library/bb726984(en-us).aspx specifies 104
|
169
|
+
MAX_ACCOUNT_LENGTH = 256
|
170
|
+
# command line max length is limited to 8191, choose something high but still enough that we don't blow out CLI
|
171
|
+
MAX_PARAMETERS_LENGTH = 4096
|
172
|
+
# in testing, this value could be set to a length of 99999, but saving / loading the task failed
|
173
|
+
MAX_COMMENT_LENGTH = 8192
|
174
|
+
|
161
175
|
# Returns a new TaskScheduler object. If a work_item (and possibly the
|
162
176
|
# the trigger) are passed as arguments then a new work item is created and
|
163
177
|
# associated with that trigger, although you can still activate other tasks
|
@@ -212,7 +226,7 @@ module Win32
|
|
212
226
|
name_ptr_ptr = FFI::Pointer.new(:pointer, names_array_ptr)
|
213
227
|
for i in 0 ... count
|
214
228
|
name_ptr_ptr[i].read_com_memory_pointer do |name_ptr|
|
215
|
-
array << name_ptr.read_arbitrary_wide_string_up_to(
|
229
|
+
array << name_ptr.read_arbitrary_wide_string_up_to(MAX_PATH)
|
216
230
|
end
|
217
231
|
end
|
218
232
|
end
|
@@ -269,12 +283,15 @@ module Win32
|
|
269
283
|
#
|
270
284
|
def save(file = nil)
|
271
285
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
286
|
+
raise Error.new('Account information must be set on the current task to save it properly.') if !@account_information_set
|
272
287
|
|
273
288
|
reset = true
|
274
289
|
|
275
290
|
begin
|
276
291
|
@pITask.QueryInstance(COM::PersistFile) do |pIPersistFile|
|
277
|
-
|
292
|
+
wide_file = wide_string(file)
|
293
|
+
pIPersistFile.Save(wide_file, 1)
|
294
|
+
pIPersistFile.SaveCompleted(wide_file)
|
278
295
|
end
|
279
296
|
rescue
|
280
297
|
reset = false
|
@@ -311,6 +328,15 @@ module Win32
|
|
311
328
|
# bad. In this case the task is created but a warning is generated and
|
312
329
|
# false is returned.
|
313
330
|
#
|
331
|
+
# Note that if intending to use SYSTEM, specify an empty user and nil password
|
332
|
+
#
|
333
|
+
# Calling task.set_account_information('SYSTEM', nil) will generally not
|
334
|
+
# work, except for one special case where flags are also set like:
|
335
|
+
# task.flags = Win32::TaskScheduler::TASK_FLAG_RUN_ONLY_IF_LOGGED_ON
|
336
|
+
#
|
337
|
+
# This must be done prior to the 1st save() call for the task to be
|
338
|
+
# properly registered and visible through the MMC snap-in / schtasks.exe
|
339
|
+
#
|
314
340
|
def set_account_information(user, password)
|
315
341
|
raise Error.new('No current task scheduler. ITaskScheduler is NULL.') if @pITS.nil?
|
316
342
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
@@ -321,11 +347,15 @@ module Win32
|
|
321
347
|
if (user.nil? || user=="") && (password.nil? || password=="")
|
322
348
|
@pITask.SetAccountInformation(wide_string(""), FFI::Pointer::NULL)
|
323
349
|
else
|
350
|
+
if user.length > MAX_ACCOUNT_LENGTH
|
351
|
+
raise Error.new("User has exceeded maximum allowed length #{MAX_ACCOUNT_LENGTH}")
|
352
|
+
end
|
324
353
|
user = wide_string(user)
|
325
354
|
password = wide_string(password)
|
326
355
|
@pITask.SetAccountInformation(user, password)
|
327
356
|
end
|
328
357
|
|
358
|
+
@account_information_set = true
|
329
359
|
bool = true
|
330
360
|
rescue Puppet::Util::Windows::Error => e
|
331
361
|
raise e unless e.code == SCHED_E_ACCOUNT_INFORMATION_NOT_SET
|
@@ -350,7 +380,7 @@ module Win32
|
|
350
380
|
FFI::MemoryPointer.new(:pointer) do |ptr|
|
351
381
|
@pITask.GetAccountInformation(ptr)
|
352
382
|
ptr.read_com_memory_pointer do |str_ptr|
|
353
|
-
user = str_ptr.read_arbitrary_wide_string_up_to(
|
383
|
+
user = str_ptr.read_arbitrary_wide_string_up_to(MAX_ACCOUNT_LENGTH) if ! str_ptr.null?
|
354
384
|
end
|
355
385
|
end
|
356
386
|
rescue Puppet::Util::Windows::Error => e
|
@@ -374,7 +404,7 @@ module Win32
|
|
374
404
|
@pITask.GetApplicationName(ptr)
|
375
405
|
|
376
406
|
ptr.read_com_memory_pointer do |str_ptr|
|
377
|
-
app = str_ptr.read_arbitrary_wide_string_up_to(
|
407
|
+
app = str_ptr.read_arbitrary_wide_string_up_to(MAX_PATH) if ! str_ptr.null?
|
378
408
|
end
|
379
409
|
end
|
380
410
|
|
@@ -388,6 +418,10 @@ module Win32
|
|
388
418
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
389
419
|
raise TypeError unless app.is_a?(String)
|
390
420
|
|
421
|
+
# the application name is written to a .job file on disk, so is subject to path limitations
|
422
|
+
if app.length > MAX_PATH
|
423
|
+
raise Error.new("Application name has exceeded maximum allowed length #{MAX_PATH}")
|
424
|
+
end
|
391
425
|
@pITask.SetApplicationName(wide_string(app))
|
392
426
|
|
393
427
|
app
|
@@ -405,7 +439,7 @@ module Win32
|
|
405
439
|
@pITask.GetParameters(ptr)
|
406
440
|
|
407
441
|
ptr.read_com_memory_pointer do |str_ptr|
|
408
|
-
param = str_ptr.read_arbitrary_wide_string_up_to(
|
442
|
+
param = str_ptr.read_arbitrary_wide_string_up_to(MAX_PARAMETERS_LENGTH) if ! str_ptr.null?
|
409
443
|
end
|
410
444
|
end
|
411
445
|
|
@@ -421,6 +455,10 @@ module Win32
|
|
421
455
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
422
456
|
raise TypeError unless param.is_a?(String)
|
423
457
|
|
458
|
+
if param.length > MAX_PARAMETERS_LENGTH
|
459
|
+
raise Error.new("Parameters has exceeded maximum allowed length #{MAX_PARAMETERS_LENGTH}")
|
460
|
+
end
|
461
|
+
|
424
462
|
@pITask.SetParameters(wide_string(param))
|
425
463
|
|
426
464
|
param
|
@@ -438,7 +476,7 @@ module Win32
|
|
438
476
|
@pITask.GetWorkingDirectory(ptr)
|
439
477
|
|
440
478
|
ptr.read_com_memory_pointer do |str_ptr|
|
441
|
-
dir = str_ptr.read_arbitrary_wide_string_up_to(
|
479
|
+
dir = str_ptr.read_arbitrary_wide_string_up_to(MAX_PATH) if ! str_ptr.null?
|
442
480
|
end
|
443
481
|
end
|
444
482
|
|
@@ -452,6 +490,10 @@ module Win32
|
|
452
490
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
453
491
|
raise TypeError unless dir.is_a?(String)
|
454
492
|
|
493
|
+
if dir.length > MAX_PATH
|
494
|
+
raise Error.new("Working directory has exceeded maximum allowed length #{MAX_PATH}")
|
495
|
+
end
|
496
|
+
|
455
497
|
@pITask.SetWorkingDirectory(wide_string(dir))
|
456
498
|
|
457
499
|
dir
|
@@ -465,28 +507,30 @@ module Win32
|
|
465
507
|
raise Error.new('No current task scheduler. ITaskScheduler is NULL.') if @pITS.nil?
|
466
508
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
467
509
|
|
510
|
+
priority_name = ''
|
511
|
+
|
468
512
|
FFI::MemoryPointer.new(:dword, 1) do |ptr|
|
469
513
|
@pITask.GetPriority(ptr)
|
470
514
|
|
471
515
|
pri = ptr.read_dword
|
472
516
|
if (pri & IDLE) != 0
|
473
|
-
|
517
|
+
priority_name = 'idle'
|
474
518
|
elsif (pri & NORMAL) != 0
|
475
|
-
|
519
|
+
priority_name = 'normal'
|
476
520
|
elsif (pri & HIGH) != 0
|
477
|
-
|
521
|
+
priority_name = 'high'
|
478
522
|
elsif (pri & REALTIME) != 0
|
479
|
-
|
523
|
+
priority_name = 'realtime'
|
480
524
|
elsif (pri & BELOW_NORMAL) != 0
|
481
|
-
|
525
|
+
priority_name = 'below_normal'
|
482
526
|
elsif (pri & ABOVE_NORMAL) != 0
|
483
|
-
|
527
|
+
priority_name = 'above_normal'
|
484
528
|
else
|
485
|
-
|
529
|
+
priority_name = 'unknown'
|
486
530
|
end
|
487
531
|
end
|
488
532
|
|
489
|
-
|
533
|
+
priority_name
|
490
534
|
end
|
491
535
|
|
492
536
|
# Sets the priority of the task. The +priority+ should be a numeric
|
@@ -534,6 +578,13 @@ module Win32
|
|
534
578
|
end
|
535
579
|
end
|
536
580
|
|
581
|
+
# preload task with the SYSTEM account
|
582
|
+
# empty string '' means 'SYSTEM' per MSDN, so default it
|
583
|
+
# given an account is necessary for creation of a task
|
584
|
+
# note that a user may set SYSTEM explicitly, but that has problems
|
585
|
+
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa381276(v=vs.85).aspx
|
586
|
+
set_account_information('', nil)
|
587
|
+
|
537
588
|
@pITask
|
538
589
|
end
|
539
590
|
|
@@ -699,7 +750,7 @@ module Win32
|
|
699
750
|
@pITask.GetComment(ptr)
|
700
751
|
|
701
752
|
ptr.read_com_memory_pointer do |str_ptr|
|
702
|
-
comment = str_ptr.read_arbitrary_wide_string_up_to(
|
753
|
+
comment = str_ptr.read_arbitrary_wide_string_up_to(MAX_COMMENT_LENGTH) if ! str_ptr.null?
|
703
754
|
end
|
704
755
|
end
|
705
756
|
|
@@ -712,6 +763,10 @@ module Win32
|
|
712
763
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
713
764
|
raise TypeError unless comment.is_a?(String)
|
714
765
|
|
766
|
+
if comment.length > MAX_COMMENT_LENGTH
|
767
|
+
raise Error.new("Comment has exceeded maximum allowed length #{MAX_COMMENT_LENGTH}")
|
768
|
+
end
|
769
|
+
|
715
770
|
@pITask.SetComment(wide_string(comment))
|
716
771
|
comment
|
717
772
|
end
|
@@ -727,7 +782,7 @@ module Win32
|
|
727
782
|
@pITask.GetCreator(ptr)
|
728
783
|
|
729
784
|
ptr.read_com_memory_pointer do |str_ptr|
|
730
|
-
creator = str_ptr.read_arbitrary_wide_string_up_to(
|
785
|
+
creator = str_ptr.read_arbitrary_wide_string_up_to(MAX_ACCOUNT_LENGTH) if ! str_ptr.null?
|
731
786
|
end
|
732
787
|
end
|
733
788
|
|
@@ -740,6 +795,11 @@ module Win32
|
|
740
795
|
raise Error.new('No currently active task. ITask is NULL.') if @pITask.nil?
|
741
796
|
raise TypeError unless creator.is_a?(String)
|
742
797
|
|
798
|
+
if creator.length > MAX_ACCOUNT_LENGTH
|
799
|
+
raise Error.new("Creator has exceeded maximum allowed length #{MAX_ACCOUNT_LENGTH}")
|
800
|
+
end
|
801
|
+
|
802
|
+
|
743
803
|
@pITask.SetCreator(wide_string(creator))
|
744
804
|
creator
|
745
805
|
end
|
@@ -808,14 +868,8 @@ module Win32
|
|
808
868
|
|
809
869
|
# Returns whether or not the scheduled task exists.
|
810
870
|
def exists?(job_name)
|
811
|
-
|
812
|
-
|
813
|
-
if File.basename(file, '.job') == job_name
|
814
|
-
bool = true
|
815
|
-
break
|
816
|
-
end
|
817
|
-
}
|
818
|
-
bool
|
871
|
+
# task name comparison is case insensitive
|
872
|
+
tasks.any? { |name| name.casecmp(job_name + '.job') == 0 }
|
819
873
|
end
|
820
874
|
|
821
875
|
private
|
@@ -887,6 +941,7 @@ module Win32
|
|
887
941
|
# Ensure that COM reference is decremented properly
|
888
942
|
@pITask.Release if @pITask && ! @pITask.null?
|
889
943
|
@pITask = nil
|
944
|
+
@account_information_set = false
|
890
945
|
end
|
891
946
|
|
892
947
|
def populate_trigger(task_trigger, trigger)
|
@@ -927,9 +982,10 @@ module Win32
|
|
927
982
|
|
928
983
|
trigger_struct = COM::TASK_TRIGGER.new(trigger_ptr)
|
929
984
|
trigger_struct[:cbTriggerSize] = COM::TASK_TRIGGER.size
|
930
|
-
|
931
|
-
trigger_struct[:
|
932
|
-
trigger_struct[:
|
985
|
+
now = Time.now
|
986
|
+
trigger_struct[:wBeginYear] = trigger['start_year'] || now.year
|
987
|
+
trigger_struct[:wBeginMonth] = trigger['start_month'] || now.month
|
988
|
+
trigger_struct[:wBeginDay] = trigger['start_day'] || now.day
|
933
989
|
trigger_struct[:wEndYear] = trigger['end_year'] || 0
|
934
990
|
trigger_struct[:wEndMonth] = trigger['end_month'] || 0
|
935
991
|
trigger_struct[:wEndDay] = trigger['end_day'] || 0
|
@@ -940,7 +996,7 @@ module Win32
|
|
940
996
|
trigger_struct[:rgFlags] = trigger['flags'] || 0
|
941
997
|
trigger_struct[:TriggerType] = trigger['trigger_type'] || :TASK_TIME_TRIGGER_ONCE
|
942
998
|
trigger_struct[:Type] = trigger_type_union
|
943
|
-
trigger_struct[:wRandomMinutesInterval] = trigger['random_minutes_interval']
|
999
|
+
trigger_struct[:wRandomMinutesInterval] = trigger['random_minutes_interval'] || 0
|
944
1000
|
|
945
1001
|
task_trigger.SetTrigger(trigger_struct)
|
946
1002
|
end
|
data/lib/puppet/version.rb
CHANGED
@@ -120,7 +120,7 @@ require 'puppet_spec/language'
|
|
120
120
|
end
|
121
121
|
|
122
122
|
it 'fails when no value is provided for required first parameter', :if => call_type == 'EPP' do
|
123
|
-
expect_fail(params, [], /
|
123
|
+
expect_fail(params, [], /expects a value for parameter \$a/)
|
124
124
|
end
|
125
125
|
|
126
126
|
it "will use the referenced parameter's given value" do
|
@@ -476,6 +476,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
|
|
476
476
|
it "should retrieve the user sid" do
|
477
477
|
sid = nil
|
478
478
|
user = Puppet::Util::Windows::ADSI::User.create("puppet#{rand(10000)}")
|
479
|
+
user.password = 'PUPPET_RULeZ_123!'
|
479
480
|
user.commit
|
480
481
|
begin
|
481
482
|
sid = Puppet::Util::Windows::ADSI::User.new(user.name).sid.sid
|
@@ -90,6 +90,26 @@ describe 'the new function' do
|
|
90
90
|
)).to have_resource('Notify[Integer, 1]')
|
91
91
|
end
|
92
92
|
|
93
|
+
context 'when prefixed by a sign' do
|
94
|
+
{ '+1' => 1,
|
95
|
+
'-1' => -1,
|
96
|
+
'+ 1' => 1,
|
97
|
+
'- 1' => -1,
|
98
|
+
'+0x10' => 16,
|
99
|
+
'+ 0x10' => 16,
|
100
|
+
'-0x10' => -16,
|
101
|
+
'- 0x10' => -16
|
102
|
+
}.each do |str, result|
|
103
|
+
it "produces #{result} from the string '#{str}'" do
|
104
|
+
expect(compile_to_catalog(<<-"MANIFEST"
|
105
|
+
$x = Integer.new("#{str}")
|
106
|
+
notify { "${type($x, generalized)}, $x": }
|
107
|
+
MANIFEST
|
108
|
+
)).to have_resource("Notify[Integer, #{result}]")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
93
113
|
context "when radix is not set it uses default and" do
|
94
114
|
{ "10" => 10,
|
95
115
|
"010" => 8,
|
@@ -131,7 +151,11 @@ describe 'the new function' do
|
|
131
151
|
"010" => 2,
|
132
152
|
"00010" => 2,
|
133
153
|
'0B111' => 7,
|
134
|
-
'0b111' => 7
|
154
|
+
'0b111' => 7,
|
155
|
+
'+0B111' => 7,
|
156
|
+
'-0b111' => -7,
|
157
|
+
'+ 0B111'=> 7,
|
158
|
+
'- 0b111'=> -7
|
135
159
|
}.each do |str, result|
|
136
160
|
it "produces #{result} from the string '#{str}'" do
|
137
161
|
expect(compile_to_catalog(<<-"MANIFEST"
|
@@ -142,8 +166,12 @@ describe 'the new function' do
|
|
142
166
|
end
|
143
167
|
end
|
144
168
|
|
145
|
-
{
|
146
|
-
'0X10' => :error
|
169
|
+
{ '0x10' => :error,
|
170
|
+
'0X10' => :error,
|
171
|
+
'+0X10' => :error,
|
172
|
+
'-0X10' => :error,
|
173
|
+
'+ 0X10'=> :error,
|
174
|
+
'- 0X10'=> :error
|
147
175
|
}.each do |str, result|
|
148
176
|
it "errors when given the non binary value compliant string '#{str}'" do
|
149
177
|
expect{compile_to_catalog(<<-"MANIFEST"
|
@@ -158,6 +186,10 @@ describe 'the new function' do
|
|
158
186
|
{ "10" => 8,
|
159
187
|
"010" => 8,
|
160
188
|
"00010" => 8,
|
189
|
+
'+00010' => 8,
|
190
|
+
'-00010' => -8,
|
191
|
+
'+ 00010'=> 8,
|
192
|
+
'- 00010'=> -8,
|
161
193
|
}.each do |str, result|
|
162
194
|
it "produces #{result} from the string '#{str}'" do
|
163
195
|
expect(compile_to_catalog(<<-"MANIFEST"
|
@@ -172,6 +204,10 @@ describe 'the new function' do
|
|
172
204
|
'0X10' => :error,
|
173
205
|
'0B10' => :error,
|
174
206
|
'0b10' => :error,
|
207
|
+
'+0b10' => :error,
|
208
|
+
'-0b10' => :error,
|
209
|
+
'+ 0b10'=> :error,
|
210
|
+
'- 0b10'=> :error,
|
175
211
|
}.each do |str, result|
|
176
212
|
it "errors when given the non octal value compliant string '#{str}'" do
|
177
213
|
expect{compile_to_catalog(<<-"MANIFEST"
|
@@ -190,6 +226,10 @@ describe 'the new function' do
|
|
190
226
|
"0X10" => 16,
|
191
227
|
"0b1" => 16*11+1,
|
192
228
|
"0B1" => 16*11+1,
|
229
|
+
'+0B1' => 16*11+1,
|
230
|
+
'-0B1' => -16*11-1,
|
231
|
+
'+ 0B1' => 16*11+1,
|
232
|
+
'- 0B1' => -16*11-1,
|
193
233
|
}.each do |str, result|
|
194
234
|
it "produces #{result} from the string '#{str}'" do
|
195
235
|
expect(compile_to_catalog(<<-"MANIFEST"
|
@@ -201,6 +241,10 @@ describe 'the new function' do
|
|
201
241
|
end
|
202
242
|
|
203
243
|
{ '0XGG' => :error,
|
244
|
+
'+0XGG' => :error,
|
245
|
+
'-0XGG' => :error,
|
246
|
+
'+ 0XGG'=> :error,
|
247
|
+
'- 0XGG'=> :error,
|
204
248
|
}.each do |str, result|
|
205
249
|
it "errors when given the non octal value compliant string '#{str}'" do
|
206
250
|
expect{compile_to_catalog(<<-"MANIFEST"
|
@@ -226,7 +270,6 @@ describe 'the new function' do
|
|
226
270
|
end
|
227
271
|
|
228
272
|
{ '0X10' => :error,
|
229
|
-
'0X10' => :error,
|
230
273
|
'0b10' => :error,
|
231
274
|
'0B10' => :error,
|
232
275
|
}.each do |str, result|
|
@@ -291,6 +334,10 @@ describe 'the new function' do
|
|
291
334
|
{ 42 => "Notify[Integer, 42]",
|
292
335
|
42.3 => "Notify[Float, 42.3]",
|
293
336
|
"42.0" => "Notify[Float, 42.0]",
|
337
|
+
"+42.0" => "Notify[Float, 42.0]",
|
338
|
+
"-42.0" => "Notify[Float, -42.0]",
|
339
|
+
"+ 42.0" => "Notify[Float, 42.0]",
|
340
|
+
"- 42.0" => "Notify[Float, -42.0]",
|
294
341
|
"42.3" => "Notify[Float, 42.3]",
|
295
342
|
"0x10" => "Notify[Integer, 16]",
|
296
343
|
"010" => "Notify[Integer, 8]",
|
@@ -321,6 +368,10 @@ describe 'the new function' do
|
|
321
368
|
{ 42 => "Notify[Float, 42.0]",
|
322
369
|
42.3 => "Notify[Float, 42.3]",
|
323
370
|
"42.0" => "Notify[Float, 42.0]",
|
371
|
+
"+42.0" => "Notify[Float, 42.0]",
|
372
|
+
"-42.0" => "Notify[Float, -42.0]",
|
373
|
+
"+ 42.0" => "Notify[Float, 42.0]",
|
374
|
+
"- 42.0" => "Notify[Float, -42.0]",
|
324
375
|
"42.3" => "Notify[Float, 42.3]",
|
325
376
|
"0x10" => "Notify[Float, 16.0]",
|
326
377
|
"010" => "Notify[Float, 10.0]",
|
@@ -514,7 +565,7 @@ describe 'the new function' do
|
|
514
565
|
context 'when invoked on String' do
|
515
566
|
{ {} => 'Notify[String, {}]',
|
516
567
|
[] => 'Notify[String, []]',
|
517
|
-
{'a'=>true} =>
|
568
|
+
{'a'=>true} => "Notify[String, {'a' => true}]",
|
518
569
|
[1,2,3,4] => 'Notify[String, [1, 2, 3, 4]]',
|
519
570
|
[[1,2],[3,4]] => 'Notify[String, [[1, 2], [3, 4]]]',
|
520
571
|
'abcd' => 'Notify[String, abcd]',
|