metasploit-yard 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ #
2
+ # Gems
3
+ #
4
+
5
+ require 'active_support/core_ext/hash/keys'
6
+
7
+ #
8
+ # Project
9
+ #
10
+
11
+ require 'metasploit/yard/aruba/rvm_env/variable'
12
+
13
+ # Recognizes `export`s of a variable
14
+ class Metasploit::Yard::Aruba::RvmEnv::Export < Metasploit::Yard::Aruba::RvmEnv::Variable
15
+ #
16
+ # CONSTANTS
17
+ #
18
+
19
+ # Matches line with format `export <name>=<quote><value><quote>`
20
+ REGEXP = /\Aexport (?<name>.*?)=(?<quote>"|')(?<value>.*?)\k<quote>\Z/
21
+
22
+ #
23
+ # Attributes
24
+ #
25
+
26
+ # @!attribute value
27
+ # The value to which {Metasploit::Yard::Aruba::RvmEnv::Variable#name} should be set
28
+ #
29
+ # @return [String]
30
+ attr_accessor :value
31
+
32
+ #
33
+ # Class Methods
34
+ #
35
+
36
+ # Parses line of `rvm env` output into an {Export} if it matches {REGEXP}.
37
+ #
38
+ # @param line [String] a line of `rvm env` output
39
+ # @return [Export] if line contains `export`.
40
+ # @return [nil] otherwise
41
+ def self.parse(line)
42
+ # use self:: so subclasses can override
43
+ match = self::REGEXP.match(line)
44
+
45
+ if match
46
+ new(
47
+ name: match[:name],
48
+ value: match[:value]
49
+ )
50
+ end
51
+ end
52
+
53
+ #
54
+ # Instance Methods
55
+ #
56
+
57
+ # @param attributes [Hash{Symbol=>String}]
58
+ # @option attributes [String] :name (see Metasploit::Yard::Aruba::RvmEnv::Variable#name})
59
+ # @option attributes [String] :value (see #value)
60
+ def initialize(attributes={})
61
+ attributes.assert_valid_keys(:name, :value)
62
+
63
+ super(name: attributes[:name])
64
+ @value = attributes[:value]
65
+ end
66
+
67
+ # Whether this export is the same class and has the same {#value} as `other`.
68
+ #
69
+ # @return [true] if `other.class` is `Metasploit::Yard::Aruba::RvmEnv::Export` and `other.value` is {#value}.
70
+ # @return [false] otherwise
71
+ def ==(other)
72
+ super(other) && other.value == self.value
73
+ end
74
+
75
+ # Set {Metasploit::Yard::Aruba::RvmEnv::Variable#name} to {#value}.
76
+ #
77
+ # @param options [Hash{Symbol => Object}]
78
+ # @option options [Metasploit::Yard::Aruba::RvmEnv::Unset] :from the old state of this variable
79
+ # @option options [Object] :world the cucumber world instance for the current scenario
80
+ def change(options={})
81
+ options.assert_valid_keys(:from, :world)
82
+
83
+ world = options.fetch(:world)
84
+
85
+ world.set_env(name, value)
86
+ end
87
+ end
@@ -0,0 +1,47 @@
1
+ #
2
+ # Gems
3
+ #
4
+
5
+ require 'active_support/core_ext/hash/keys'
6
+
7
+ #
8
+ # Project
9
+ #
10
+
11
+ require 'metasploit/yard/aruba/rvm_env'
12
+
13
+ # Recognizes `export`s that are actually prepending values to the pre-existing value as is the case with `PATH`.
14
+ class Metasploit::Yard::Aruba::RvmEnv::Prepend < Metasploit::Yard::Aruba::RvmEnv::Export
15
+ #
16
+ # CONSTANTS
17
+ #
18
+
19
+ # Matches line with format `export <name>=<quote><value>$<name><quote>`. `<value>` will contain trailing
20
+ # `File::PATH_SEPARATOR`, so it can be directly prepended to the current value of `<name>` to get the value to set
21
+ # the environment variable.
22
+ REGEXP = /\Aexport (?<name>.*?)=(?<quote>"|')(?<value>.*?#{File::PATH_SEPARATOR})\$\k<name>\k<quote>\Z/
23
+
24
+ # Replaces {Metasploit::Yard::Aruba::RvmEnv::Export#value `:from` value} with
25
+ # {Metasploit::Yard::Aruba::RvmEnv::Export#value this variable's value}.
26
+ #
27
+ # @param options [Hash{Symbol => Object}]
28
+ # @option options [Metasploit::Yard::Aruba::RvmEnv::Unset] :from the old state of this variable.
29
+ # @option options [Object] :world the cucumber world instance for the current scenario
30
+ def change(options={})
31
+ options.assert_valid_keys(:from, :world)
32
+
33
+ from = options.fetch(:from)
34
+ world = options.fetch(:world)
35
+
36
+ from_directories = from.value.split(File::PATH_SEPARATOR)
37
+ to_directories = value.split(File::PATH_SEPARATOR)
38
+
39
+ path = ENV[name]
40
+
41
+ to_directories.zip(from_directories) do |to_directory, from_directory|
42
+ path = path.gsub(from_directory, to_directory)
43
+ end
44
+
45
+ world.set_env(name, path)
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ #
2
+ # Gems
3
+ #
4
+
5
+ require 'active_support/core_ext/hash/keys'
6
+
7
+ #
8
+ # Project
9
+ #
10
+
11
+ require 'metasploit/yard/aruba/rvm_env/variable'
12
+
13
+ # Recognizes `unset`s of environment variables.
14
+ class Metasploit::Yard::Aruba::RvmEnv::Unset < Metasploit::Yard::Aruba::RvmEnv::Variable
15
+ #
16
+ # CONSTANTS
17
+ #
18
+
19
+ # Matches line with format `unset <name>`
20
+ REGEXP = /\Aunset (?<name>.*?)\Z/
21
+
22
+ #
23
+ # Class Methods
24
+ #
25
+
26
+ # Parses lines of `rvm env` output into a {Prepend} if it matches {REGEXP}
27
+ #
28
+ # @param line [String] a line of `rvm env` output.
29
+ # @return [Unset] if line contains `unset`.
30
+ # @return [nil] otherwise
31
+ def self.parse(line)
32
+ match = REGEXP.match(line)
33
+
34
+ if match
35
+ new(
36
+ name: match[:name]
37
+ )
38
+ end
39
+ end
40
+
41
+ #
42
+ # Instance Methods
43
+ #
44
+
45
+ # Unsets {Metasploit::Yard::Aruba::RvmEnv::Variable#name}.
46
+ #
47
+ # @param options [Hash{Symbol => Object}]
48
+ # @option options [Metasploit::Yard::Aruba::RvmEnv::Unset] :from the old state of this variable
49
+ # @option options [Object] :world the cucumber world instance for the current scenario
50
+ def change(options={})
51
+ options.assert_valid_keys(:from , :world)
52
+
53
+ world = options.fetch(:world)
54
+ world.unset_env(name)
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # Gems
3
+ #
4
+
5
+ require 'active_support/core_ext/hash/keys'
6
+
7
+ #
8
+ # Project
9
+ #
10
+
11
+ require 'metasploit/yard/aruba/rvm_env'
12
+
13
+ # An environment variable in `rvm env`.
14
+ class Metasploit::Yard::Aruba::RvmEnv::Variable
15
+ #
16
+ # Attributes
17
+ #
18
+
19
+ # @!attribute name
20
+ # The name of variable being manipulated in `rvm env`
21
+ #
22
+ # @return [String]
23
+ attr_accessor :name
24
+
25
+ #
26
+ # Instance Methods
27
+ #
28
+
29
+ # @param attributes [Hash{Symbol=>String}]
30
+ # @option attributes [String] :name (see #name)
31
+ def initialize(attributes={})
32
+ attributes.assert_valid_keys(:name)
33
+
34
+ @name = attributes[:name]
35
+ end
36
+
37
+ # This variable is the same class and has the same {#name} as `other`.
38
+ #
39
+ # @return [true] if `other.class` is `Metasploit::Yard::Aruba::RvmEnv::Variable` and `other.name` is {#name}.
40
+ # @return [false] otherwise
41
+ def ==(other)
42
+ other.class == self.class && other.name == self.name
43
+ end
44
+ end
@@ -0,0 +1,103 @@
1
+ #
2
+ # Gems
3
+ #
4
+
5
+ require 'active_support/core_ext/hash/keys'
6
+
7
+ #
8
+ # Project
9
+ #
10
+
11
+ require 'metasploit/yard/aruba'
12
+
13
+ # Helpers for parsing `rvm env` output
14
+ module Metasploit::Yard::Aruba::RvmEnv
15
+ require 'metasploit/yard/aruba/rvm_env/export'
16
+ require 'metasploit/yard/aruba/rvm_env/prepend'
17
+ require 'metasploit/yard/aruba/rvm_env/unset'
18
+
19
+ #
20
+ # CONSTANTS
21
+ #
22
+
23
+ # Class for parsing lines for `rvm env` in order of precedence
24
+ LINE_PARSER_PRECEDENCE = [
25
+ # must be before `Metasploit::Yard::Aruba::RvmEnv::Export` because
26
+ # `Metasploit::Yard::Aruba::RvmEnv::Export::REGEXP` matches a superset of
27
+ # `Metasploit::Yard::Aruba::RvmEnv::Prepend::REGEXP`
28
+ Metasploit::Yard::Aruba::RvmEnv::Prepend,
29
+ Metasploit::Yard::Aruba::RvmEnv::Export,
30
+ Metasploit::Yard::Aruba::RvmEnv::Unset
31
+ ]
32
+
33
+ # Changes from one `rvm env [@<gemset>]` to another.
34
+ #
35
+ # @param options [Hash{Symbol => Array<Metasploit::Yard::Aruba::RvmEnv::Variable>}]
36
+ # @option options :from [Array<Metasploit::Yard::Aruba::RvmEnv::Variable>] the current rvm env; usually the output of
37
+ # parsing `rvm env`.
38
+ # @option options :to [Array<Metasploit::Yard::Aruba::RvmEnv::Variable>] the new rvm env; usualy the output of parsing
39
+ # `rvm env @<gemset>`
40
+ # @option options [Object] :world the cucumber World instance for the current scenario.
41
+ def self.change(options={})
42
+ options.assert_valid_keys(:from, :to, :world)
43
+
44
+ from_by_name = parsed_to_hash(options.fetch(:from))
45
+ to_by_name = parsed_to_hash(options.fetch(:to))
46
+ world = options.fetch(:world)
47
+
48
+ to_by_name.each do |name, to_variable|
49
+ from_variable = from_by_name[name]
50
+
51
+ # ignore variables that didn't change
52
+ unless to_variable == from_variable
53
+ to_variable.change(
54
+ from: from_variable,
55
+ world: world
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ # Parses the output of `rvm env`.
62
+ #
63
+ # @return [Array<Metasploit::Yard::Aruba::RvmEnv::Variable, #apply>]
64
+ def self.parse(rvm_env)
65
+ rvm_env.each_line.map { |line|
66
+ parse_line(line)
67
+ }
68
+ end
69
+
70
+ # Parses an individual line of `rvm env`
71
+ #
72
+ # @return [Metasploit::Yard::Aruba::RvmEnv::Variable, #apply]
73
+ # @raise [ArgumentError] if no `Class` in {LINE_PARSER_PRECEDENCE} returns a parsed instance.
74
+ def self.parse_line(line)
75
+ parsed = nil
76
+
77
+ LINE_PARSER_PRECEDENCE.each do |line_parser|
78
+ parsed = line_parser.parse(line)
79
+
80
+ if parsed
81
+ break
82
+ end
83
+ end
84
+
85
+ unless parsed
86
+ raise ArgumentError, "No line parser could parse #{line.inspect}"
87
+ end
88
+
89
+ parsed
90
+ end
91
+
92
+ # Converts output of {parse} into Hash that maps {Metasploit::Yard::Aruba::RvmEnv::Variable#name} to the
93
+ # {Metasploit::Yard::Aruba::RvmEnv::Variable}.
94
+ #
95
+ # @param parsed [Array<Metasploit::Yard::Aruba::RvmEnv::Variable>] output of {parse}.
96
+ # @return [Hash{String => Metasploit::Yard::Aruba::RvmEnv::Variable}] Map of
97
+ # {Metasploit::Yard::Aruba::RvmEnv::Variable#name} to the {Metasploit::Yard::Aruba::RvmEnv::Variable}.
98
+ def self.parsed_to_hash(parsed)
99
+ parsed.each_with_object({}) { |variable, variable_by_name|
100
+ variable_by_name[variable.name] = variable
101
+ }
102
+ end
103
+ end
@@ -0,0 +1,6 @@
1
+ require 'metasploit/yard'
2
+
3
+ # Helpers for aruba step definitions for testing `rake yard`
4
+ module Metasploit::Yard::Aruba
5
+
6
+ end
@@ -0,0 +1,12 @@
1
+ require 'rails/railtie'
2
+
3
+ module Metasploit
4
+ module Yard
5
+ # Automatically loads `yard.rake` for any Rails project.
6
+ class Railtie < Rails::Railtie
7
+ rake_tasks do
8
+ load 'tasks/yard.rake'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ module Metasploit
2
+ module Yard
3
+ # Holds components of {VERSION} as defined by {http://semver.org/spec/v2.0.0.html semantic versioning v2.0.0}.
4
+ module Version
5
+ # The major version number.
6
+ MAJOR = 1
7
+ # The minor version number, scoped to the {MAJOR} version number.
8
+ MINOR = 0
9
+ # The patch number, scoped to the {MINOR} version number.
10
+ PATCH = 0
11
+
12
+ # The full version string, including the {MAJOR}, {MINOR}, {PATCH}, and optionally, the {PRERELEASE} in the
13
+ # {http://semver.org/spec/v2.0.0.html semantic versioning v2.0.0} format.
14
+ #
15
+ # @return [String] '{MAJOR}.{MINOR}.{PATCH}' on master. '{MAJOR}.{MINOR}.{PATCH}-{PRERELEASE}' on any branch
16
+ # other than master.
17
+ def self.full
18
+ version = "#{MAJOR}.#{MINOR}.#{PATCH}"
19
+
20
+ if defined? PRERELEASE
21
+ version = "#{version}-#{PRERELEASE}"
22
+ end
23
+
24
+ version
25
+ end
26
+
27
+ # The full gem version string, including the {MAJOR}, {MINOR}, {PATCH}, and optionally, the {PRERELEASE} in the
28
+ # {http://guides.rubygems.org/specification-reference/#version RubyGems versioning} format.
29
+ #
30
+ # @return [String] '{MAJOR}.{MINOR}.{PATCH}' on master. '{MAJOR}.{MINOR}.{PATCH}.{PRERELEASE}' on any branch
31
+ # other than master.
32
+ def self.gem
33
+ full.gsub('-', '.pre.')
34
+ end
35
+ end
36
+
37
+ # @see Version.gem
38
+ GEM_VERSION = Version.gem
39
+
40
+ # @see Version.full
41
+ VERSION = Version.full
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+
2
+ #
3
+ # Project
4
+ #
5
+
6
+ require 'metasploit/yard/version'
7
+
8
+ # The namespace shared between all gems related to
9
+ # [Metasploit Framework](https://github.com/rapid7/metasploit-framework) and
10
+ # [Metasploit Commercial Editions](https://metasploit.com)
11
+ module Metasploit
12
+ # Namespace for this gem.
13
+ module Yard
14
+ if defined?(Rails)
15
+ require 'metasploit/yard/railtie'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,58 @@
1
+ # @note All options not specific to any given rake task should go in the .yardopts file so they are available to both
2
+ # the below rake tasks and when invoking `yard` from the command line
3
+
4
+ #
5
+ # Gems
6
+ #
7
+ # gems must load explicitly any gem declared in gemspec
8
+ # @see https://github.com/bundler/bundler/issues/2018#issuecomment-6819359
9
+ #
10
+ #
11
+
12
+ require 'yard'
13
+
14
+ namespace :yard do
15
+ YARD::Rake::YardocTask.new(:doc) do |t|
16
+ # --no-stats here as 'stats' task called after will print fuller stats
17
+ t.options = ['--no-stats']
18
+
19
+ t.after = Proc.new {
20
+ Rake::Task['yard:stats'].execute
21
+ }
22
+ end
23
+
24
+ desc "Shows stats for YARD Documentation including listing undocumented modules, classes, constants, and methods"
25
+ task :stats => :environment do
26
+ stats = YARD::CLI::Stats.new
27
+ stats.run('--compact', '--list-undoc')
28
+
29
+ threshold = 100.0
30
+ threshold_path = 'config/yard-stats-threshold'
31
+
32
+ if File.exist?(threshold_path)
33
+ threshold = File.read(threshold_path).to_f
34
+ end
35
+
36
+ # duplicates end of YARD::CLI::Stats#print_statistics
37
+ # @see https://github.com/lsegal/yard/blob/76c7525f46df38f7b24d4b3cb9daeef512fe58e8/lib/yard/cli/stats.rb#L63-L69
38
+ total = stats.instance_eval {
39
+ if @undocumented == 0
40
+ 100
41
+ elsif @total == 0
42
+ 0
43
+ else
44
+ (@total - @undocumented).to_f / @total.to_f * 100
45
+ end
46
+ }
47
+
48
+ if total < threshold
49
+ $stderr.puts "Documentation percentage (%<total>.2f%%) below threshold (%<threshold>.2f%%)" % { total: total, threshold: threshold}
50
+ exit 1
51
+ end
52
+ end
53
+ end
54
+
55
+ # @todo Figure out how to just clone description from yard:doc
56
+ desc "Generate YARD documentation"
57
+ # allow calling namespace to as a task that goes to default task for namespace
58
+ task :yard => ['yard:doc']
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'metasploit/yard/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "metasploit-yard"
8
+ spec.version = Metasploit::Yard::VERSION
9
+ spec.authors = ["Luke Imhoff"]
10
+ spec.email = ["luke_imhoff@rapid7.com"]
11
+ spec.summary = "yard rake tasks"
12
+ spec.description = "YARD rake tasks used through the metasploit-* gem namespace"
13
+ spec.homepage = "https://github.com/rapid7/"
14
+ spec.license = "BSD-3-Clause"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ # assert_valid_keys
22
+ spec.add_development_dependency 'activesupport'
23
+ spec.add_development_dependency 'aruba'
24
+ spec.add_development_dependency 'bundler', '~> 1.5'
25
+ spec.add_development_dependency 'codeclimate-test-reporter'
26
+ spec.add_development_dependency 'coveralls'
27
+ spec.add_development_dependency 'cucumber'
28
+ spec.add_development_dependency 'rspec', '~> 3.0'
29
+ spec.add_development_dependency 'simplecov'
30
+
31
+ spec.add_runtime_dependency 'rake'
32
+ spec.add_runtime_dependency 'yard'
33
+
34
+ # Ruby 1.9 JSON Hash syntax is used
35
+ spec.required_ruby_version = '>= 1.9'
36
+ end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Metasploit::Yard::Version do
4
+ context 'CONSTANTS' do
5
+ context 'MAJOR' do
6
+ subject(:major) do
7
+ described_class::MAJOR
8
+ end
9
+
10
+ it { is_expected.to be_a Integer }
11
+ end
12
+
13
+ context 'MINOR' do
14
+ subject(:minor) do
15
+ described_class::MINOR
16
+ end
17
+
18
+ it { is_expected.to be_a Integer }
19
+ end
20
+
21
+ context 'PATCH' do
22
+ subject(:patch) do
23
+ described_class::PATCH
24
+ end
25
+
26
+ it { is_expected.to be_a Integer }
27
+ end
28
+
29
+ pull_request = ENV['TRAVIS_PULL_REQUEST']
30
+
31
+ # a pull request cannot check PRERELEASE because it will be tested in the target branch, but the source itself
32
+ # is from the source branch and so has the source branch PRERELEASE.
33
+ #
34
+ # PRERELEASE can only be set appropriately for a merge by merging to the target branch and then updating PRERELEASE
35
+ # on the target branch before committing and/or pushing to github and travis-ci.
36
+ if pull_request.nil? || pull_request == 'false'
37
+ context 'PREPRELEASE' do
38
+ subject(:prerelease) do
39
+ described_class::PRERELEASE
40
+ end
41
+
42
+ branch = ENV['TRAVIS_BRANCH']
43
+
44
+ if branch.nil? || branch.empty?
45
+ branch = `git rev-parse --abbrev-ref HEAD`.strip
46
+ end
47
+
48
+ if branch == 'master'
49
+ it 'does not have a PRERELEASE' do
50
+ expect(defined? described_class::PRERELEASE).to be_nil
51
+ end
52
+ else
53
+ branch_regex = %r{\A(?:refs/remotes/)?(?<type>bug|chore|feature|staging)(/(?<story>[^/]+))?/(?<prerelease>[^\/]+)\z}
54
+ match = branch.match(branch_regex)
55
+
56
+ if match
57
+ it 'matches the branch relative name' do
58
+ expected_prerelease = match[:prerelease]
59
+
60
+ expect(defined? described_class::PRERELEASE).not_to be_nil,
61
+ "PRERELEASE should be defined as #{expected_prerelease.inspect}"
62
+ expect(prerelease).to eq(expected_prerelease)
63
+ end
64
+ else
65
+ tag_regex = /\Av(?<major>\d+).(?<minor>\d+).(?<patch>\d+)(\.pre\.(?<prerelease>.*))?\z/
66
+ # travis-ci sets TRAVIS_BRANCH to the tag name for tag builds
67
+ match = branch.match(tag_regex)
68
+
69
+ if match
70
+ tag_prerelease = match[:prerelease]
71
+
72
+ if tag_prerelease
73
+ it 'matches the tag prerelease converted from a gem version to a VERSION' do
74
+ expect(prerelease).to eq(tag_prerelease.gsub('.pre.', '-'))
75
+ end
76
+ else
77
+ it 'does not have a PRERELEASE' do
78
+ expect(defined? described_class::PRERELEASE).to be_nil
79
+ end
80
+ end
81
+ else
82
+ it 'has a abbreviated reference that can be parsed for prerelease' do
83
+ fail "Do not know how to parse #{branch.inspect} for PRERELEASE"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ context 'full' do
93
+ subject(:full) do
94
+ described_class.full
95
+ end
96
+
97
+ #
98
+ # lets
99
+ #
100
+
101
+ let(:major) do
102
+ 1
103
+ end
104
+
105
+ let(:minor) do
106
+ 2
107
+ end
108
+
109
+ let(:patch) do
110
+ 3
111
+ end
112
+
113
+ before(:each) do
114
+ stub_const("#{described_class}::MAJOR", major)
115
+ stub_const("#{described_class}::MINOR", minor)
116
+ stub_const("#{described_class}::PATCH", patch)
117
+ end
118
+
119
+ context 'with PRERELEASE' do
120
+ let(:prerelease) do
121
+ 'prerelease'
122
+ end
123
+
124
+ before(:each) do
125
+ stub_const("#{described_class}::PRERELEASE", prerelease)
126
+ end
127
+
128
+ it 'is <major>.<minor>.<patch>-<prerelease>' do
129
+ expect(full).to eq("#{major}.#{minor}.#{patch}-#{prerelease}")
130
+ end
131
+ end
132
+
133
+ context 'without PRERELEASE' do
134
+ before(:each) do
135
+ hide_const("#{described_class}::PRERELEASE")
136
+ end
137
+
138
+ it 'is <major>.<minor>.<patch>' do
139
+ expect(full).to eq("#{major}.#{minor}.#{patch}")
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Metasploit::Yard do
4
+ context 'CONSTANTS' do
5
+ context 'VERSION' do
6
+ subject(:version) {
7
+ described_class::VERSION
8
+ }
9
+
10
+ it 'is Metasploit::Yard::Version.full' do
11
+ expect(version).to eq(Metasploit::Yard::Version.full)
12
+ end
13
+ end
14
+ end
15
+ end