modspec 0.2.1 → 0.2.3
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 +4 -4
- data/.rubocop.yml +3 -0
- data/.rubocop_todo.yml +8 -80
- data/lib/modspec/child_container.rb +36 -0
- data/lib/modspec/conformance_class.rb +7 -21
- data/lib/modspec/normative_statement.rb +2 -2
- data/lib/modspec/normative_statements_class.rb +10 -24
- data/lib/modspec/suite.rb +42 -57
- data/lib/modspec/version.rb +1 -1
- data/lib/modspec.rb +1 -0
- data/spec/modspec/suite_spec.rb +228 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 280d4bf45944e0e451d2e1c3deee8075ca6cb78ea0b5fd1e6c1c24b3d31bb9c5
|
|
4
|
+
data.tar.gz: 95cf4c68c04904ca8bb028d2f1e9a1922078c903e5db93a6009aeade96fe21b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e6949a03cbf3aa75fd5b553729a20b81344c87bd1924a7422389a060e63b6bf9b1c681b661d118a1e83dff59711aa3483445685ce2604df2bd7265ed62a9603c
|
|
7
|
+
data.tar.gz: b14cd04d41b9750fc7cdcb05bce52453c658ced2f3cbaef62129bba800b42577de105561904ff546fa4ce1b202b65abd1abc8f190b80068e60b2b85b0717274e
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2026-05-06
|
|
3
|
+
# on 2026-05-06 04:01:19 UTC using RuboCop version 1.86.1.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -11,56 +11,13 @@ Gemspec/RequiredRubyVersion:
|
|
|
11
11
|
Exclude:
|
|
12
12
|
- 'modspec.gemspec'
|
|
13
13
|
|
|
14
|
-
# Offense count:
|
|
15
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
16
|
-
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
17
|
-
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
18
|
-
Layout/ArgumentAlignment:
|
|
19
|
-
Exclude:
|
|
20
|
-
- 'lib/modspec/suite.rb'
|
|
21
|
-
- 'spec/modspec/suite_spec.rb'
|
|
22
|
-
|
|
23
|
-
# Offense count: 1
|
|
24
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
25
|
-
# Configuration parameters: EnforcedStyleAlignWith.
|
|
26
|
-
# SupportedStylesAlignWith: either, start_of_block, start_of_line
|
|
27
|
-
Layout/BlockAlignment:
|
|
28
|
-
Exclude:
|
|
29
|
-
- 'lib/modspec/suite.rb'
|
|
30
|
-
|
|
31
|
-
# Offense count: 1
|
|
32
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
33
|
-
Layout/BlockEndNewline:
|
|
34
|
-
Exclude:
|
|
35
|
-
- 'lib/modspec/suite.rb'
|
|
36
|
-
|
|
37
|
-
# Offense count: 6
|
|
38
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
39
|
-
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
|
40
|
-
# SupportedHashRocketStyles: key, separator, table
|
|
41
|
-
# SupportedColonStyles: key, separator, table
|
|
42
|
-
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
|
43
|
-
Layout/HashAlignment:
|
|
44
|
-
Exclude:
|
|
45
|
-
- 'spec/modspec/suite_spec.rb'
|
|
46
|
-
|
|
47
|
-
# Offense count: 2
|
|
48
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
49
|
-
# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
|
|
50
|
-
# SupportedStylesAlignWith: start_of_line, relative_to_receiver
|
|
51
|
-
Layout/IndentationWidth:
|
|
52
|
-
Exclude:
|
|
53
|
-
- 'lib/modspec/suite.rb'
|
|
54
|
-
|
|
55
|
-
# Offense count: 36
|
|
14
|
+
# Offense count: 31
|
|
56
15
|
# This cop supports safe autocorrection (--autocorrect).
|
|
57
16
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
58
17
|
# URISchemes: http, https
|
|
59
18
|
Layout/LineLength:
|
|
60
19
|
Exclude:
|
|
61
|
-
- 'lib/modspec/conformance_class.rb'
|
|
62
20
|
- 'lib/modspec/conformance_test.rb'
|
|
63
|
-
- 'lib/modspec/normative_statements_class.rb'
|
|
64
21
|
- 'lib/modspec/suite.rb'
|
|
65
22
|
- 'spec/modspec/conformance_class_spec.rb'
|
|
66
23
|
- 'spec/modspec/conformance_test_spec.rb'
|
|
@@ -68,21 +25,14 @@ Layout/LineLength:
|
|
|
68
25
|
- 'spec/modspec/normative_statements_class_spec.rb'
|
|
69
26
|
- 'spec/modspec/suite_spec.rb'
|
|
70
27
|
|
|
71
|
-
# Offense count:
|
|
72
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
73
|
-
# Configuration parameters: AllowInHeredoc.
|
|
74
|
-
Layout/TrailingWhitespace:
|
|
75
|
-
Exclude:
|
|
76
|
-
- 'lib/modspec/suite.rb'
|
|
77
|
-
- 'spec/modspec/suite_spec.rb'
|
|
78
|
-
|
|
79
|
-
# Offense count: 5
|
|
28
|
+
# Offense count: 6
|
|
80
29
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
81
30
|
Metrics/AbcSize:
|
|
82
31
|
Exclude:
|
|
32
|
+
- 'lib/modspec/child_container.rb'
|
|
83
33
|
- 'lib/modspec/suite.rb'
|
|
84
34
|
|
|
85
|
-
# Offense count:
|
|
35
|
+
# Offense count: 4
|
|
86
36
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
87
37
|
Metrics/CyclomaticComplexity:
|
|
88
38
|
Exclude:
|
|
@@ -91,7 +41,7 @@ Metrics/CyclomaticComplexity:
|
|
|
91
41
|
# Offense count: 9
|
|
92
42
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
93
43
|
Metrics/MethodLength:
|
|
94
|
-
Max:
|
|
44
|
+
Max: 20
|
|
95
45
|
|
|
96
46
|
# Offense count: 2
|
|
97
47
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
@@ -99,7 +49,7 @@ Metrics/PerceivedComplexity:
|
|
|
99
49
|
Exclude:
|
|
100
50
|
- 'lib/modspec/suite.rb'
|
|
101
51
|
|
|
102
|
-
# Offense count:
|
|
52
|
+
# Offense count: 1
|
|
103
53
|
# Configuration parameters: MinSize.
|
|
104
54
|
Performance/CollectionLiteralInLoop:
|
|
105
55
|
Exclude:
|
|
@@ -118,33 +68,11 @@ RSpec/IndexedLet:
|
|
|
118
68
|
- 'spec/modspec/normative_statements_class_spec.rb'
|
|
119
69
|
- 'spec/modspec/suite_spec.rb'
|
|
120
70
|
|
|
121
|
-
# Offense count:
|
|
71
|
+
# Offense count: 13
|
|
122
72
|
RSpec/MultipleExpectations:
|
|
123
73
|
Max: 9
|
|
124
74
|
|
|
125
|
-
# Offense count: 7
|
|
126
|
-
# Configuration parameters: AllowSubject.
|
|
127
|
-
RSpec/MultipleMemoizedHelpers:
|
|
128
|
-
Max: 12
|
|
129
|
-
|
|
130
75
|
# Offense count: 2
|
|
131
76
|
RSpec/RepeatedExample:
|
|
132
77
|
Exclude:
|
|
133
78
|
- 'spec/modspec_spec.rb'
|
|
134
|
-
|
|
135
|
-
# Offense count: 1
|
|
136
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
137
|
-
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
|
|
138
|
-
# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
|
|
139
|
-
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
|
|
140
|
-
# FunctionalMethods: let, let!, subject, watch
|
|
141
|
-
# AllowedMethods: lambda, proc, it
|
|
142
|
-
Style/BlockDelimiters:
|
|
143
|
-
Exclude:
|
|
144
|
-
- 'lib/modspec/suite.rb'
|
|
145
|
-
|
|
146
|
-
# Offense count: 2
|
|
147
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
148
|
-
Style/MultilineIfModifier:
|
|
149
|
-
Exclude:
|
|
150
|
-
- 'lib/modspec/suite.rb'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Modspec
|
|
4
|
+
module ChildContainer
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def validates_children(collection_name, empty_label:, child_label:)
|
|
11
|
+
define_method(:validate_children_presence) do
|
|
12
|
+
children = send(collection_name)
|
|
13
|
+
if children.nil? || children.empty?
|
|
14
|
+
["#{empty_label} #{identifier} has no child #{child_label}"]
|
|
15
|
+
else
|
|
16
|
+
[]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
define_method(:validate_children_identifier_prefix) do
|
|
21
|
+
children = send(collection_name)
|
|
22
|
+
return [] unless children
|
|
23
|
+
|
|
24
|
+
expected_prefix = "#{identifier}/"
|
|
25
|
+
children.filter_map do |child|
|
|
26
|
+
unless child.identifier.to_s.start_with?(expected_prefix)
|
|
27
|
+
msg = "#{child_label} #{child.identifier} "
|
|
28
|
+
msg += "does not share the expected prefix #{expected_prefix}"
|
|
29
|
+
msg
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -4,6 +4,8 @@ require "lutaml/model"
|
|
|
4
4
|
|
|
5
5
|
module Modspec
|
|
6
6
|
class ConformanceClass < Lutaml::Model::Serializable
|
|
7
|
+
include ChildContainer
|
|
8
|
+
|
|
7
9
|
attribute :identifier, Identifier
|
|
8
10
|
attribute :name, :string
|
|
9
11
|
attribute :description, :string
|
|
@@ -15,6 +17,9 @@ module Modspec
|
|
|
15
17
|
attribute :belongs_to, Identifier, collection: true
|
|
16
18
|
attribute :reference, :string
|
|
17
19
|
|
|
20
|
+
validates_children :tests, empty_label: "Conformance class",
|
|
21
|
+
child_label: "conformance tests"
|
|
22
|
+
|
|
18
23
|
xml do
|
|
19
24
|
element "conformance-class"
|
|
20
25
|
map_attribute "identifier", to: :identifier
|
|
@@ -31,29 +36,10 @@ module Modspec
|
|
|
31
36
|
|
|
32
37
|
def validate
|
|
33
38
|
errors = super
|
|
34
|
-
errors.concat(
|
|
35
|
-
errors.concat(
|
|
39
|
+
errors.concat(validate_children_identifier_prefix)
|
|
40
|
+
errors.concat(validate_children_presence)
|
|
36
41
|
errors.concat(tests.flat_map(&:validate))
|
|
37
42
|
errors
|
|
38
43
|
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def validate_class_children_mapping
|
|
43
|
-
if tests.nil? || tests.empty?
|
|
44
|
-
["Conformance class #{identifier} has no child conformance tests"]
|
|
45
|
-
else
|
|
46
|
-
[]
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def validate_identifier_prefix
|
|
51
|
-
errors = []
|
|
52
|
-
expected_prefix = "#{identifier}/"
|
|
53
|
-
tests&.each do |test|
|
|
54
|
-
errors << "Conformance test #{test.identifier} does not share the expected prefix #{expected_prefix}" unless test.identifier.to_s.start_with?(expected_prefix)
|
|
55
|
-
end
|
|
56
|
-
errors
|
|
57
|
-
end
|
|
58
44
|
end
|
|
59
45
|
end
|
|
@@ -45,8 +45,8 @@ module Modspec
|
|
|
45
45
|
map_element "parts", to: :parts
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
def validate(
|
|
49
|
-
super
|
|
48
|
+
def validate(register: Lutaml::Model::Config.default_register)
|
|
49
|
+
super
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -4,6 +4,8 @@ require "lutaml/model"
|
|
|
4
4
|
|
|
5
5
|
module Modspec
|
|
6
6
|
class NormativeStatementsClass < Lutaml::Model::Serializable
|
|
7
|
+
include ChildContainer
|
|
8
|
+
|
|
7
9
|
attribute :identifier, Identifier
|
|
8
10
|
attribute :name, :string
|
|
9
11
|
attribute :description, :string
|
|
@@ -16,6 +18,9 @@ module Modspec
|
|
|
16
18
|
attribute :reference, :string
|
|
17
19
|
attribute :source, :string
|
|
18
20
|
|
|
21
|
+
validates_children :normative_statements, empty_label: "Requirement class",
|
|
22
|
+
child_label: "requirements"
|
|
23
|
+
|
|
19
24
|
xml do
|
|
20
25
|
element "normative-statements-class"
|
|
21
26
|
map_attribute "identifier", to: :identifier
|
|
@@ -31,31 +36,12 @@ module Modspec
|
|
|
31
36
|
map_element "source", to: :source
|
|
32
37
|
end
|
|
33
38
|
|
|
34
|
-
def validate
|
|
35
|
-
errors = super
|
|
36
|
-
errors.concat(
|
|
37
|
-
errors.concat(
|
|
38
|
-
errors.concat(normative_statements.flat_map
|
|
39
|
+
def validate
|
|
40
|
+
errors = super
|
|
41
|
+
errors.concat(validate_children_identifier_prefix)
|
|
42
|
+
errors.concat(validate_children_presence)
|
|
43
|
+
errors.concat(normative_statements.flat_map(&:validate))
|
|
39
44
|
errors
|
|
40
45
|
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def validate_identifier_prefix
|
|
45
|
-
return [] if normative_statements.nil? || normative_statements.empty?
|
|
46
|
-
|
|
47
|
-
expected_prefix = "#{identifier}/"
|
|
48
|
-
normative_statements.each_with_object([]) do |statement, errors|
|
|
49
|
-
errors << "Normative statement #{statement.identifier} does not share the expected prefix #{expected_prefix}" unless statement.identifier.to_s.start_with?(expected_prefix)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def validate_class_children_mapping
|
|
54
|
-
if normative_statements.nil? || normative_statements.empty?
|
|
55
|
-
["Requirement class #{identifier} has no child requirements"]
|
|
56
|
-
else
|
|
57
|
-
[]
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
46
|
end
|
|
61
47
|
end
|
data/lib/modspec/suite.rb
CHANGED
|
@@ -21,15 +21,13 @@ module Modspec
|
|
|
21
21
|
|
|
22
22
|
def validate
|
|
23
23
|
setup_relationships
|
|
24
|
-
|
|
24
|
+
reset_statement_index
|
|
25
25
|
errors = super
|
|
26
26
|
errors.concat(validate_cycles)
|
|
27
27
|
errors.concat(validate_label_uniqueness)
|
|
28
28
|
errors.concat(validate_dependencies)
|
|
29
29
|
unless normative_statements_classes.nil?
|
|
30
|
-
errors.concat(normative_statements_classes.flat_map
|
|
31
|
-
n.validate(self)
|
|
32
|
-
end)
|
|
30
|
+
errors.concat(normative_statements_classes.flat_map(&:validate))
|
|
33
31
|
end
|
|
34
32
|
errors.concat(conformance_classes.flat_map(&:validate)) unless conformance_classes.nil?
|
|
35
33
|
errors
|
|
@@ -42,7 +40,7 @@ module Modspec
|
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
combined_suite = dup
|
|
45
|
-
combined_suite.
|
|
43
|
+
combined_suite.reset_statement_index
|
|
46
44
|
if other_suite.normative_statements_classes
|
|
47
45
|
combined_suite.normative_statements_classes ||= []
|
|
48
46
|
combined_suite.normative_statements_classes += other_suite.normative_statements_classes
|
|
@@ -63,19 +61,17 @@ module Modspec
|
|
|
63
61
|
combined_suite
|
|
64
62
|
end
|
|
65
63
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
nsc = normative_statements_classes || []
|
|
70
|
-
cc = conformance_classes || []
|
|
64
|
+
def statement_index
|
|
65
|
+
@statement_index ||= build_statement_index
|
|
66
|
+
end
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
nsc +
|
|
75
|
-
cc).map(&:identifier)
|
|
68
|
+
def reset_statement_index
|
|
69
|
+
@statement_index = nil
|
|
76
70
|
end
|
|
77
71
|
|
|
78
|
-
|
|
72
|
+
def all_identifiers
|
|
73
|
+
statement_index.keys
|
|
74
|
+
end
|
|
79
75
|
|
|
80
76
|
def resolve_conflicts(other_suite)
|
|
81
77
|
resolve_conflicts_for(normative_statements_classes,
|
|
@@ -131,13 +127,16 @@ module Modspec
|
|
|
131
127
|
end
|
|
132
128
|
|
|
133
129
|
def merge_attributes(existing_item, other_item)
|
|
134
|
-
existing_item.class.
|
|
130
|
+
existing_item.class.attributes.each_key do |attr|
|
|
135
131
|
next if %i[identifier name].include?(attr)
|
|
136
132
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
existing_val = existing_item.send(attr)
|
|
134
|
+
other_val = other_item.send(attr)
|
|
135
|
+
|
|
136
|
+
if existing_val.is_a?(Array)
|
|
137
|
+
existing_item.send(attr).concat(other_val).uniq!
|
|
138
|
+
elsif existing_val.nil? && !other_val.nil?
|
|
139
|
+
existing_item.send("#{attr}=", other_val)
|
|
141
140
|
end
|
|
142
141
|
end
|
|
143
142
|
end
|
|
@@ -148,21 +147,20 @@ module Modspec
|
|
|
148
147
|
cycles.map { |cycle| "Cycle detected: #{cycle.join(' -> ')}" }
|
|
149
148
|
end
|
|
150
149
|
|
|
151
|
-
# Combine all statements into a single array
|
|
152
|
-
# This includes both normative statements and conformance tests
|
|
153
150
|
def all_statements
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
151
|
+
statement_index.values
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def each_statement(&block)
|
|
155
|
+
normative_statements_classes&.each do |nsc|
|
|
156
|
+
yield nsc
|
|
157
|
+
nsc.normative_statements.each(&block)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
conformance_classes&.each do |cc|
|
|
161
|
+
yield cc
|
|
162
|
+
cc.tests.each(&block)
|
|
163
|
+
end
|
|
166
164
|
end
|
|
167
165
|
|
|
168
166
|
def build_dependency_graph
|
|
@@ -219,20 +217,21 @@ module Modspec
|
|
|
219
217
|
end
|
|
220
218
|
|
|
221
219
|
def validate_label_uniqueness
|
|
222
|
-
|
|
220
|
+
seen = {}
|
|
223
221
|
errors = []
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
each_statement do |statement|
|
|
223
|
+
id = statement.identifier.to_s
|
|
224
|
+
if seen[id]
|
|
226
225
|
errors << "Duplicate identifier found: #{statement.identifier}"
|
|
227
226
|
else
|
|
228
|
-
|
|
227
|
+
seen[id] = true
|
|
229
228
|
end
|
|
230
229
|
end
|
|
231
230
|
errors
|
|
232
231
|
end
|
|
233
232
|
|
|
234
233
|
def validate_dependencies
|
|
235
|
-
all_ids =
|
|
234
|
+
all_ids = statement_index
|
|
236
235
|
|
|
237
236
|
errors = []
|
|
238
237
|
normative_statements_classes&.each do |nsc|
|
|
@@ -256,24 +255,10 @@ module Modspec
|
|
|
256
255
|
errors
|
|
257
256
|
end
|
|
258
257
|
|
|
259
|
-
def
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
identifiers[nsc.identifier.to_s] = nsc
|
|
264
|
-
nsc.normative_statements.each do |ns|
|
|
265
|
-
identifiers[ns.identifier.to_s] = ns
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
conformance_classes&.each do |cc|
|
|
270
|
-
identifiers[cc.identifier.to_s] = cc
|
|
271
|
-
cc.tests.each do |ct|
|
|
272
|
-
identifiers[ct.identifier.to_s] = ct
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
identifiers
|
|
258
|
+
def build_statement_index
|
|
259
|
+
index = {}
|
|
260
|
+
each_statement { |s| index[s.identifier.to_s] = s }
|
|
261
|
+
index
|
|
277
262
|
end
|
|
278
263
|
|
|
279
264
|
def validate_refs(obj, all_ids, property)
|
data/lib/modspec/version.rb
CHANGED
data/lib/modspec.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "lutaml/model"
|
|
|
5
5
|
require "set"
|
|
6
6
|
|
|
7
7
|
module Modspec
|
|
8
|
+
autoload :ChildContainer, "modspec/child_container"
|
|
8
9
|
autoload :Identifier, "modspec/identifier"
|
|
9
10
|
autoload :NormativeStatement, "modspec/normative_statement"
|
|
10
11
|
autoload :NormativeStatementPart, "modspec/normative_statement"
|
data/spec/modspec/suite_spec.rb
CHANGED
|
@@ -63,6 +63,36 @@ RSpec.describe Modspec::Suite do
|
|
|
63
63
|
expect(errors).to include(a_string_matching(/Duplicate identifier/))
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
it "detects cross-type identifier collisions" do
|
|
67
|
+
suite = described_class.new(
|
|
68
|
+
identifier: "/suite",
|
|
69
|
+
name: "Test",
|
|
70
|
+
normative_statements_classes: [
|
|
71
|
+
Modspec::NormativeStatementsClass.new(
|
|
72
|
+
identifier: "/req/test",
|
|
73
|
+
normative_statements: [
|
|
74
|
+
Modspec::NormativeStatement.new(identifier: "/req/test/a",
|
|
75
|
+
name: "A", statement: "a"),
|
|
76
|
+
],
|
|
77
|
+
),
|
|
78
|
+
],
|
|
79
|
+
conformance_classes: [
|
|
80
|
+
Modspec::ConformanceClass.new(
|
|
81
|
+
identifier: "/conf/test",
|
|
82
|
+
tests: [
|
|
83
|
+
Modspec::ConformanceTest.new(
|
|
84
|
+
identifier: "/req/test/a",
|
|
85
|
+
name: "CT collision",
|
|
86
|
+
),
|
|
87
|
+
],
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
errors = suite.validate
|
|
93
|
+
expect(errors).to include(a_string_matching(/Duplicate identifier.*\/req\/test\/a/))
|
|
94
|
+
end
|
|
95
|
+
|
|
66
96
|
it "detects dependency cycles" do
|
|
67
97
|
ns_a = Modspec::NormativeStatement.new(
|
|
68
98
|
identifier: "/req/test/a", name: "A", statement: "a",
|
|
@@ -267,4 +297,202 @@ RSpec.describe Modspec::Suite do
|
|
|
267
297
|
expect(errors).to be_empty
|
|
268
298
|
end
|
|
269
299
|
end
|
|
300
|
+
|
|
301
|
+
describe "#setup_relationships" do
|
|
302
|
+
it "links conformance tests to their target normative statements" do
|
|
303
|
+
suite = described_class.new(
|
|
304
|
+
identifier: "/suite",
|
|
305
|
+
name: "Test",
|
|
306
|
+
normative_statements_classes: [
|
|
307
|
+
Modspec::NormativeStatementsClass.new(
|
|
308
|
+
identifier: "/req/test",
|
|
309
|
+
normative_statements: [
|
|
310
|
+
Modspec::NormativeStatement.new(identifier: "/req/test/a",
|
|
311
|
+
name: "A", statement: "a"),
|
|
312
|
+
],
|
|
313
|
+
),
|
|
314
|
+
],
|
|
315
|
+
conformance_classes: [
|
|
316
|
+
Modspec::ConformanceClass.new(
|
|
317
|
+
identifier: "/conf/test",
|
|
318
|
+
tests: [
|
|
319
|
+
Modspec::ConformanceTest.new(
|
|
320
|
+
identifier: "/conf/test/a",
|
|
321
|
+
name: "CT-A",
|
|
322
|
+
targets: ["/req/test/a"],
|
|
323
|
+
),
|
|
324
|
+
],
|
|
325
|
+
),
|
|
326
|
+
],
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
suite.setup_relationships
|
|
330
|
+
ct = suite.conformance_classes.first.tests.first
|
|
331
|
+
expect(ct.corresponding_requirements.map(&:identifier)).to eq(["/req/test/a"])
|
|
332
|
+
expect(ct.parent_class).to eq(suite.conformance_classes.first)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "handles missing target gracefully (no matching requirement)" do
|
|
336
|
+
suite = described_class.new(
|
|
337
|
+
identifier: "/suite",
|
|
338
|
+
name: "Test",
|
|
339
|
+
normative_statements_classes: [
|
|
340
|
+
Modspec::NormativeStatementsClass.new(
|
|
341
|
+
identifier: "/req/test",
|
|
342
|
+
normative_statements: [
|
|
343
|
+
Modspec::NormativeStatement.new(identifier: "/req/test/a",
|
|
344
|
+
name: "A", statement: "a"),
|
|
345
|
+
],
|
|
346
|
+
),
|
|
347
|
+
],
|
|
348
|
+
conformance_classes: [
|
|
349
|
+
Modspec::ConformanceClass.new(
|
|
350
|
+
identifier: "/conf/test",
|
|
351
|
+
tests: [
|
|
352
|
+
Modspec::ConformanceTest.new(
|
|
353
|
+
identifier: "/conf/test/a",
|
|
354
|
+
name: "CT-A",
|
|
355
|
+
targets: ["/req/test/missing"],
|
|
356
|
+
),
|
|
357
|
+
],
|
|
358
|
+
),
|
|
359
|
+
],
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
suite.setup_relationships
|
|
363
|
+
ct = suite.conformance_classes.first.tests.first
|
|
364
|
+
expect(ct.corresponding_requirements).to be_empty
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
it "returns early when conformance_classes is nil" do
|
|
368
|
+
suite = described_class.new(
|
|
369
|
+
identifier: "/suite",
|
|
370
|
+
name: "Test",
|
|
371
|
+
normative_statements_classes: [
|
|
372
|
+
Modspec::NormativeStatementsClass.new(
|
|
373
|
+
identifier: "/req/test",
|
|
374
|
+
normative_statements: [],
|
|
375
|
+
),
|
|
376
|
+
],
|
|
377
|
+
conformance_classes: nil,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
expect { suite.setup_relationships }.not_to raise_error
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
describe "#resolve_conflicts" do
|
|
385
|
+
let(:nsc_with_data) do
|
|
386
|
+
Modspec::NormativeStatementsClass.new(
|
|
387
|
+
identifier: "/req/test",
|
|
388
|
+
name: "Original",
|
|
389
|
+
description: "First description",
|
|
390
|
+
normative_statements: [
|
|
391
|
+
Modspec::NormativeStatement.new(identifier: "/req/test/a",
|
|
392
|
+
name: "A", statement: "a"),
|
|
393
|
+
],
|
|
394
|
+
)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
let(:nsc_partial) do
|
|
398
|
+
Modspec::NormativeStatementsClass.new(
|
|
399
|
+
identifier: "/req/test",
|
|
400
|
+
name: "Original",
|
|
401
|
+
description: "Second description",
|
|
402
|
+
subject: "Added subject",
|
|
403
|
+
normative_statements: [],
|
|
404
|
+
)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
let(:nsc_other) do
|
|
408
|
+
Modspec::NormativeStatementsClass.new(
|
|
409
|
+
identifier: "/req/test2",
|
|
410
|
+
name: "Two",
|
|
411
|
+
normative_statements: [
|
|
412
|
+
Modspec::NormativeStatement.new(identifier: "/req/test2/b",
|
|
413
|
+
name: "B", statement: "b"),
|
|
414
|
+
],
|
|
415
|
+
)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
it "merges attributes of items with matching identifiers" do
|
|
419
|
+
suite1 = described_class.new(
|
|
420
|
+
identifier: "/suite", name: "Test1",
|
|
421
|
+
normative_statements_classes: [nsc_with_data],
|
|
422
|
+
conformance_classes: []
|
|
423
|
+
)
|
|
424
|
+
suite2 = described_class.new(
|
|
425
|
+
identifier: "/suite", name: "Test2",
|
|
426
|
+
normative_statements_classes: [nsc_partial],
|
|
427
|
+
conformance_classes: []
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
suite1.resolve_conflicts(suite2)
|
|
431
|
+
nsc = suite1.normative_statements_classes.first
|
|
432
|
+
expect(nsc.description).to eq("First description")
|
|
433
|
+
expect(nsc.subject).to eq("Added subject")
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
it "appends items with new identifiers" do
|
|
437
|
+
suite1 = described_class.new(
|
|
438
|
+
identifier: "/suite", name: "Test1",
|
|
439
|
+
normative_statements_classes: [nsc_with_data],
|
|
440
|
+
conformance_classes: []
|
|
441
|
+
)
|
|
442
|
+
suite2 = described_class.new(
|
|
443
|
+
identifier: "/suite", name: "Test2",
|
|
444
|
+
normative_statements_classes: [nsc_other],
|
|
445
|
+
conformance_classes: []
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
suite1.resolve_conflicts(suite2)
|
|
449
|
+
expect(suite1.normative_statements_classes.map(&:identifier)).to eq(
|
|
450
|
+
["/req/test", "/req/test2"],
|
|
451
|
+
)
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
describe "YAML round-trip" do
|
|
456
|
+
it "serializes and deserializes a normative statements class" do
|
|
457
|
+
original = described_class.from_yaml(rc_yaml)
|
|
458
|
+
yaml_out = original.to_yaml
|
|
459
|
+
round_tripped = described_class.from_yaml(yaml_out)
|
|
460
|
+
|
|
461
|
+
expect(round_tripped.identifier).to eq(original.identifier)
|
|
462
|
+
expect(round_tripped.name).to eq(original.name)
|
|
463
|
+
expect(round_tripped.normative_statements_classes.count).to eq(
|
|
464
|
+
original.normative_statements_classes.count,
|
|
465
|
+
)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
it "serializes and deserializes a conformance class" do
|
|
469
|
+
original = described_class.from_yaml(cc_yaml)
|
|
470
|
+
yaml_out = original.to_yaml
|
|
471
|
+
round_tripped = described_class.from_yaml(yaml_out)
|
|
472
|
+
|
|
473
|
+
expect(round_tripped.identifier).to eq(original.identifier)
|
|
474
|
+
expect(round_tripped.conformance_classes.count).to eq(
|
|
475
|
+
original.conformance_classes.count,
|
|
476
|
+
)
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
describe "JSON round-trip" do
|
|
481
|
+
let(:json_rc) { File.read("spec/fixtures/basic-ypr-json-rc.yaml") }
|
|
482
|
+
let(:json_cc) { File.read("spec/fixtures/basic-ypr-json-cc.yaml") }
|
|
483
|
+
|
|
484
|
+
it "parses JSON-format YAML fixtures" do
|
|
485
|
+
suite = described_class.from_yaml(json_rc)
|
|
486
|
+
expect(suite.normative_statements_classes).not_to be_empty
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it "round-trips through JSON serialization" do
|
|
490
|
+
original = described_class.from_yaml(rc_yaml)
|
|
491
|
+
json_out = original.to_json
|
|
492
|
+
round_tripped = described_class.from_json(json_out)
|
|
493
|
+
|
|
494
|
+
expect(round_tripped.identifier).to eq(original.identifier)
|
|
495
|
+
expect(round_tripped.name).to eq(original.name)
|
|
496
|
+
end
|
|
497
|
+
end
|
|
270
498
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: modspec
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
@@ -71,6 +71,7 @@ files:
|
|
|
71
71
|
- README.adoc
|
|
72
72
|
- Rakefile
|
|
73
73
|
- lib/modspec.rb
|
|
74
|
+
- lib/modspec/child_container.rb
|
|
74
75
|
- lib/modspec/conformance_class.rb
|
|
75
76
|
- lib/modspec/conformance_test.rb
|
|
76
77
|
- lib/modspec/identifier.rb
|