Top Level Namespace

Defined Under Namespace

Modules: RSpec

Instance Method Summary collapse

Instance Method Details

#__apply_rspec_puppet_after(commands) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates an RSpec after {} entity with one or more global-scope method calls as its contents.

Parameters:

  • commands (Variant[String,Array[String]])

    Command or commands to call.



328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/rspec-puppet-yaml/parser.rb', line 328

def __apply_rspec_puppet_after(commands)
  if !commands.nil?
    if commands.kind_of?(Array)
      after do
        commands.each { |command| Object.send(command.to_s.to_sym) }
      end
    elsif !commands.is_a?(Hash)
      after { Object.send(commands.to_s.to_sym) }
    else
      raise ArgumentError, "__apply_rspec_puppet_after requires a command String or an Array of commands."
    end
  end
end

#__apply_rspec_puppet_before(commands) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates an RSpec before {} entity with one or more global-scope method calls as its contents.

Parameters:

  • commands (Variant[String,Array[String]])

    Command or commands to call.



307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/rspec-puppet-yaml/parser.rb', line 307

def __apply_rspec_puppet_before(commands)
  if !commands.nil?
    if commands.kind_of?(Array)
      before do
        commands.each { |command| Object.send(command.to_s.to_sym) }
      end
    elsif !commands.is_a?(Hash)
      before { Object.send(commands.to_s.to_sym) }
    else
      raise ArgumentError, "__apply_rspec_puppet_before requires a command String or an Array of commands."
    end
  end
end

#__apply_rspec_puppet_content(apply_data = {}, parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates all specified RSpec entities. This is assumed to be run within a valid RSpec container, like describe or context.

Parameters:

  • apply_data (Hash) (defaults to: {})

    The entities to generate.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent of the receiving entity.



422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/rspec-puppet-yaml/parser.rb', line 422

def __apply_rspec_puppet_content(apply_data = {}, parent_data = {})
  __apply_rspec_puppet_subject(
    RSpec::Puppet::Yaml::DataHelpers.get_named_value(
      'subject',
      apply_data
    )
  )
  __apply_rspec_puppet_lets(
    RSpec::Puppet::Yaml::DataHelpers.get_named_hash(
      'let',
      apply_data
    )
  )
  __apply_rspec_puppet_before(
    RSpec::Puppet::Yaml::DataHelpers.get_named_value(
      'before',
      apply_data
    )
  )
  __apply_rspec_puppet_after(
    RSpec::Puppet::Yaml::DataHelpers.get_named_value(
      'after',
      apply_data
    )
  )
  __apply_rspec_puppet_tests(
    RSpec::Puppet::Yaml::DataHelpers.get_named_hash(
      'tests',
      apply_data
    )
  )
  __apply_rspec_puppet_describes(
    RSpec::Puppet::Yaml::DataHelpers.get_array_of_named_hashes(
      'describe',
      apply_data
    ),
    apply_data
  )
  __apply_rspec_puppet_contexts(
    RSpec::Puppet::Yaml::DataHelpers.get_array_of_named_hashes(
      'context',
      apply_data
    ),
    apply_data
  )
  __apply_rspec_puppet_variants(
    RSpec::Puppet::Yaml::DataHelpers.get_array_of_named_hashes(
      'variants',
      apply_data
    ),
    apply_data
  )
end

#__apply_rspec_puppet_context(apply_attrs = {}, parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates an RSpec context {} and its contents.

Parameters:

  • apply_attrs (Hash) (defaults to: {})

    Definition of the entity and its contents.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent for this entity.



86
87
88
89
90
91
92
93
94
# File 'lib/rspec-puppet-yaml/parser.rb', line 86

def __apply_rspec_puppet_context(apply_attrs = {}, parent_data = {})
  context_name = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
    'name',
    apply_attrs
  )
  context(context_name) do
    __apply_rspec_puppet_content(apply_attrs, parent_data)
  end
end

#__apply_rspec_puppet_contexts(contexts = [], parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates a set of RSpec context entities.

Parameters:

  • contexts (Array[Hash]) (defaults to: [])

    Set of entities to generate. Each element must be a Hash that has a :name or 'name' attribute.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent for this entity.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/rspec-puppet-yaml/parser.rb', line 184

def __apply_rspec_puppet_contexts(contexts = [], parent_data = {})
  bad_input = false

  # Input must be an Array
  if !contexts.kind_of?(Array)
    bad_input = true
  end

  # Every element of the input must be a Hash, each with a :name attribute
  contexts.each do |container|
    if !container.is_a?(Hash)
      bad_input = true
    elsif !container.has_key?('name') && !container.has_key?(:name)
      bad_input = true
    end
  end

  if bad_input
    raise ArgumentError, "__apply_rspec_puppet_contexts requires an Array of Hashes, each with a :name attribute."
  end

  contexts.each do |container|
    __apply_rspec_puppet_context(container, parent_data)
  end
end

#__apply_rspec_puppet_describe(apply_attrs = {}, parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates an RSpec describe {} and its contents.

Parameters:

  • apply_attrs (Hash) (defaults to: {})

    Definition of the entity and its contents.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent for this entity.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rspec-puppet-yaml/parser.rb', line 58

def __apply_rspec_puppet_describe(apply_attrs = {}, parent_data = {})
  desc_name  = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
    'name',
    apply_attrs
  )
  desc_type  = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
    'type',
    apply_attrs
  )
  if desc_type.nil?
    describe(desc_name) do
      __apply_rspec_puppet_content(apply_attrs, parent_data)
    end
  else
    describe(desc_name, :type => desc_type) do
      __apply_rspec_puppet_content(apply_attrs, parent_data)
    end
  end
end

#__apply_rspec_puppet_describes(describes = [], parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates a set of RSpec describe entities.

Parameters:

  • describes (Array[Hash]) (defaults to: [])

    Set of entities to generate. Each element must be a Hash that has a :name or 'name' attribute.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent for this entity.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rspec-puppet-yaml/parser.rb', line 149

def __apply_rspec_puppet_describes(describes = [], parent_data = {})
  bad_input = false

  # Input must be an Array
  if !describes.kind_of?(Array)
    bad_input = true
  end

  # Every element of the input must be a Hash, each with a :name attribute
  describes.each do |container|
    if !container.is_a?(Hash)
      bad_input = true
    elsif !container.has_key?('name') && !container.has_key?(:name)
      bad_input = true
    end
  end

  if bad_input
    raise ArgumentError, "__apply_rspec_puppet_describes requires an Array of Hashes, each with a :name attribute."
  end

  describes.each do |container|
    __apply_rspec_puppet_describe(container, parent_data)
  end
end

#__apply_rspec_puppet_lets(lets = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Sets all let variables.

Examples:

As YAML

---
let:
  facts:
    kernel: Linux
    os:
      family: RedHat
      name: CentOS
      release:
        major: 7
        minor: 1
  params:
    require: '%{eval:ref("Package", "my-package")}'
    nodes:
      '%{eval:ref("Node", "dbnode")}': '%{eval:ref("Myapp::Mycomponent", "myapp")}'

Parameters:

  • lets (Hash) (defaults to: {})

    The data to scan for let variables



376
377
378
# File 'lib/rspec-puppet-yaml/parser.rb', line 376

def __apply_rspec_puppet_lets(lets = {})
  __expand_data_commands(lets).each { |k,v| let(k.to_sym) { v } }
end

#__apply_rspec_puppet_subject(subject) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates an RSpec subject {} entity.

Parameters:

  • subject (Any)

    The subject descriptor.



348
349
350
351
352
# File 'lib/rspec-puppet-yaml/parser.rb', line 348

def __apply_rspec_puppet_subject(subject)
  if !subject.nil?
    subject { __expand_data_commands(subject) }
  end
end

#__apply_rspec_puppet_tests(tests = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates a set of RSpec it {} "examples".

Parameters:

  • tests (Hash) (defaults to: {})

    Set of examples to build.



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/rspec-puppet-yaml/parser.rb', line 249

def __apply_rspec_puppet_tests(tests = {})
  tests.each do |method, props|
    # props must be split into args and tests based on method
    case method.to_s
    when /^(!)?((contain|create)_.+)$/
      # There can be only one beyond this point, so recurse as necessary
      if 1 < props.keys.count
        props.each { |k,v| __apply_rspec_puppet_tests({method => {k => v}})}
        return  # Avoid processing the first entry twice
      end

      positive_test = $1.nil?
      apply_method  = $2
      args          = [ props.keys.first ]
      calls         = props.values.first
    when /^(!)?(have_.+_count)$/
      positive_test = $1.nil?
      apply_method  = $2
      args          = props
      calls         = {}
    when /^(!)?(compile)$/
      positive_test = $1.nil?
      apply_method  = $2
      args          = []
      calls         = props
    when /^(!)?(run)$/
      positive_test = $1.nil?
      apply_method  = $2
      args          = []
      calls         = props
    when /^(!)?(be_valid_type)$/
      positive_test = $1.nil?
      apply_method  = $2
      args          = []
      calls         = props
    end

    matcher = RSpec::Puppet::MatcherHelpers.get_matcher_for(
      apply_method,
      args,
      calls
    )

    if positive_test
      it { is_expected.to matcher }
    else
      it { is_expected.not_to matcher }
    end
  end
end

#__apply_rspec_puppet_variant(apply_attrs = {}, parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

An extension for RSpec, variants are contexts that repeat all parent tests with specified tweaks to their inputs and expectations.

Parameters:

  • apply_attrs (Hash) (defaults to: {})

    Definition of the entity and its contents.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent for this entity.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/rspec-puppet-yaml/parser.rb', line 105

def __apply_rspec_puppet_variant(apply_attrs = {}, parent_data = {})
  variant_name = RSpec::Puppet::Yaml::DataHelpers.get_named_value(
    'name',
    apply_attrs
  )

  # The deep_merge gem's funtionality unfortunately changes the destination
  # Hash, even when you attempt to store the result to another variable and use
  # the non-bang method call.  This seems like a pretty serious bug, to me,
  # despite the gem's documentation implying that this will happen.  IMHO, the
  # gem's author made a very poor decision in how the bang and non-bang behavior
  # would differ (merely a difference in the default values of its options
  # rather than the Ruby-norm of affecting or not-affecting the calling Object).
  # To workaround this issue and protect the original destination against
  # unwanted change, a deep copy of the destination Hash must be taken and used.
  parent_dup   = Marshal.load(Marshal.dump(parent_data))
  context_data = parent_dup.select do |k,v|
    !['variants', 'before', 'after', 'subject'].include?(k.to_s)
  end
  context_data.deep_merge!(
    apply_attrs,
    { :extend_existing_arrays => false,
      :merge_hash_arrays      => true,
      :merge_nil_values       => false,
      :overwrite_arrays       => false,
      :preserve_unmergeables  => false,
      :sort_merged_arrays     => false
    }
  )

  context(variant_name) do
    __apply_rspec_puppet_content(context_data, parent_data)
  end
end

#__apply_rspec_puppet_variants(variants = [], parent_data = {}) ⇒ Object

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Generates a set of variants of RSpec entities.

Parameters:

  • variants (Array[Hash]) (defaults to: [])

    Set of entities to generate. Each element must be a Hash that has a :name or 'name' attribute.

  • parent_data (Hash) (defaults to: {})

    Used for recursion, this is the parent for this entity.



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/rspec-puppet-yaml/parser.rb', line 219

def __apply_rspec_puppet_variants(variants = [], parent_data = {})
  bad_input = false

  # Input must be an Array
  if !variants.kind_of?(Array)
    bad_input = true
  end

  # Every element of the input must be a Hash, each with a :name attribute
  variants.each do |variant|
    if !variant.is_a?(Hash)
      bad_input = true
    elsif !variant.has_key?('name') && !variant.has_key?(:name)
      bad_input = true
    end
  end

  if bad_input
    raise ArgumentError, "__apply_rspec_puppet_variants requires an Array of Hashes, each with a :name attribute."
  end

  variants.each { |variant| __apply_rspec_puppet_variant(variant, parent_data) }
end

#__expand_data_commands(serialized_data) ⇒ Any

Recursively expands specially-formatted commands with or without arguments to them found within serialized data.

Parameters:

  • serialized_data (Any)

    The data to check for expansion markers and expand them, when present.

Returns:

  • (Any)

    Expanded or original (when there are no expansion markers) data.



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/rspec-puppet-yaml/parser.rb', line 386

def __expand_data_commands(serialized_data)
  return nil if serialized_data.nil?
  if serialized_data.kind_of?(Array)
    expanded_data = []
    serialized_data.each { |elem| expanded_data << __expand_data_commands(elem) }
  elsif serialized_data.is_a?(Hash)
    expanded_data = {}
    serialized_data.each do |k,v|
      expanded_data[__expand_data_commands(k)] = __expand_data_commands(v)
    end
  else
    test_data = serialized_data.to_s
    if test_data =~ /^%{eval:(.+)}$/
      test_eval = $1
      begin
        test_value = eval test_eval
      rescue Exception => ex
        test_value = "#{ex.class}:  #{test_eval}.  #{ex.message}"
      end
    else
      test_value = serialized_data
    end
    expanded_data = test_value
  end
  expanded_data
end

#__get_eut_name(rspec_yaml_file_name, rspec_file_name) ⇒ String

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Identify the name of the entity under test.

Parameters:

  • rspec_yaml_file_name (String)

    YAML file name that describes tests.

  • rspec_file_name (String)

    Name of the *_spec.rb file that is requesting parsed results from rspec_yaml_file_name.

Returns:

  • (String)

    Name of the entity under test.



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/rspec-puppet-yaml/parser.rb', line 37

def __get_eut_name(rspec_yaml_file_name, rspec_file_name)
  base_yaml   = File.basename(rspec_yaml_file_name)
  base_caller = File.basename(rspec_file_name)

  if base_yaml =~ /^(.+)(_spec)?\.ya?ml$/
    $1.to_s
  elsif base_caller =~ /^(.+)_spec\.rb$/
    $1.to_s
  else
    'unknown'
  end
end

#__load_rspec_puppet_yaml_data(yaml_file) ⇒ Hash

Note:

The __ prefix denotes this as a "private" function. Do not call this directly.

Attempts to load the YAML test data and return its data.

Parameters:

  • yaml_file (String)

    Path to the YAML file to load.

Returns:

  • (Hash)

    The data from the YAML file.

Raises:

  • IOError when the source file is not valid YAML or does not contain a Hash.



486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/rspec-puppet-yaml/parser.rb', line 486

def __load_rspec_puppet_yaml_data(yaml_file)
  # The test data file must exist
  if !File.exists?(yaml_file)
    raise IOError, "#{yaml_file} does not exit."
  end

  begin
    yaml_data = YAML.load_file(yaml_file)
  rescue Psych::SyntaxError => ex
    raise IOError, "#{yaml_file} contains a YAML syntax error."
  rescue ArgumentError => ex
    raise IOError, "#{yaml_file} contains missing or undefined entities."
  rescue
    raise IOError, "#{yaml_file} could not be read or is not YAML."
  end

  # Must be a populated Hash
  if yaml_data.nil? || !yaml_data.is_a?(Hash)
    yaml_data = nil
    raise IOError, "#{yaml_file} is not a valid YAML Hash data structure."
  elsif yaml_data.empty?
    yaml_data = nil
    raise IOError, "#{yaml_file} contains no legible tests."
  end

  yaml_data
end

#guess_type_from_path(path) ⇒ Symbol

Attempts to identify the nature of an entity-under-test from the path of the spec file that defines its unit tests.

License: MIT

Parameters:

  • path (String)

    Fully-qualified path to the *_spec.rb file that defines RSpec (Puppet) tests.

Returns:

  • (Symbol)

    Presumed nature of the entity-under-test.

See Also:

Author:

  • Tim Sharpe



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rspec-puppet/support_copy.rb', line 30

def guess_type_from_path(path)
  case path
  when /spec\/defines/
    :define
  when /spec\/classes/
    :class
  when /spec\/functions/
    :function
  when /spec\/hosts/
    :host
  when /spec\/types/
    :type
  when /spec\/type_aliases/
    :type_alias
  when /spec\/provider/
    :provider
  when /spec\/applications/
    :application
  else
    :unknown
  end
end

#parse_rspec_puppet_yaml(yaml_file) ⇒ Object

Converts the supplied YAML data into rspec tests. When this is called from a *_spec.rb file from RSpec, testing begins immediately upon parsing.

Parameters:

  • yaml_file (String)

    Path to a YAML file containing rspec-puppet tests



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/rspec-puppet-yaml/parser.rb', line 5

def parse_rspec_puppet_yaml(yaml_file)
  test_data = __load_rspec_puppet_yaml_data(yaml_file)

  # The top-most entity must be a 'describe', which must have both name (which
  # must be identical to the entity-under-test) and type-of-entity (in case the
  # user failed to follow the prescribed directory structure for unit testing
  # Puppet modules).  RSpec docs often show more than one top-level describe,
  # so this function supports the same.
  rspec_file       = caller_locations.select {|e| e.path =~ /.+_spec.rb$/}
    .first
    .path
  default_describe = {
    'name' => __get_eut_name(yaml_file, rspec_file),
    'type' => guess_type_from_path(rspec_file)
  }
  describes        = []
  RSpec::Puppet::Yaml::DataHelpers.get_array_of_named_hashes(
    'describe',
    test_data,
  ).each { |desc| describes << default_describe.merge(desc)}
  __apply_rspec_puppet_describes(describes, test_data)
end

#parse_yaml_from_spec(rspec_file) ⇒ Object

Identifies a YAML data file based on the name of a *_spec.rb rspec file and passes it to parse_rspec_puppet_yaml to initiate parsing.

Examples:

Typical use

require 'spec_helper'
parse_yaml_from_spec(__FILE__)

Parameters:

  • rspec_file (String)

    Path to and name of the RSpec *spec.rb file. It is easiest to simply pass `FILE_` to this function from that file.

Since:

  • 0.1.0



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/rspec-puppet-yaml/extenders.rb', line 12

def parse_yaml_from_spec(rspec_file)
  if rspec_file =~ /^(.+)_spec\.rb$/
    [ "#{$1}_spec.yaml",
      "#{$1}_spec.yml",
      "#{$1}.yaml",
      "#{$1}.yml"
    ].each do |yaml_file|
      if File.exist?(yaml_file)
        parse_rspec_puppet_yaml(yaml_file)
      end
    end
  elsif rspec_file =~ /^(.+\.ya?ml)$/
    parse_rspec_puppet_yaml($1)
  else
    parse_rspec_puppet_yaml(rspec_file)
  end
end

#ref(type, title) ⇒ RSpec::Puppet::RawString

Helper to return a resource/node reference, so it gets translated in params to a raw string without quotes.

License: MIT

Parameters:

  • type (String)

    reference type

  • title (String)

    reference title

Returns:

  • (RSpec::Puppet::RawString)

    return a new RawString with the type/title populated correctly

See Also:

Author:

  • Tim Sharpe



64
65
66
# File 'lib/rspec-puppet/support_copy.rb', line 64

def ref(type, title)
  return RSpec::Puppet::RawString.new("#{type}['#{title}']")
end