modspec 0.1.0 → 0.1.2

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.rubocop_todo.yml +118 -0
  4. data/Gemfile +4 -2
  5. data/README.adoc +508 -17
  6. data/lib/modspec/conformance_class.rb +39 -10
  7. data/lib/modspec/conformance_test.rb +40 -13
  8. data/lib/modspec/identifier.rb +5 -3
  9. data/lib/modspec/normative_statement.rb +49 -14
  10. data/lib/modspec/normative_statements_class.rb +39 -12
  11. data/lib/modspec/suite.rb +242 -5
  12. data/lib/modspec/version.rb +1 -1
  13. data/lib/modspec.rb +14 -4
  14. data/modspec.gemspec +8 -14
  15. data/spec/conformance_class.liquid +98 -0
  16. data/spec/fixtures/advanced-cc.yaml +52 -0
  17. data/spec/fixtures/advanced-json-cc.yaml +24 -0
  18. data/spec/fixtures/advanced-json-rc.yaml +16 -0
  19. data/spec/fixtures/advanced-rc.yaml +43 -0
  20. data/spec/fixtures/basic-quaternion-cc.yaml +44 -0
  21. data/spec/fixtures/basic-quaternion-json-cc.yaml +24 -0
  22. data/spec/fixtures/basic-quaternion-json-rc.yaml +17 -0
  23. data/spec/fixtures/basic-quaternion-json-strict-cc.yaml +22 -0
  24. data/spec/fixtures/basic-quaternion-json-strict-rc.yaml +19 -0
  25. data/spec/fixtures/basic-quaternion-rc.yaml +34 -0
  26. data/spec/fixtures/basic-ypr-cc.yaml +39 -0
  27. data/spec/fixtures/basic-ypr-json-cc.yaml +23 -0
  28. data/spec/fixtures/basic-ypr-json-rc.yaml +21 -0
  29. data/spec/fixtures/basic-ypr-rc.yaml +32 -0
  30. data/spec/fixtures/chain-cc.yaml +50 -0
  31. data/spec/fixtures/chain-json-cc.yaml +24 -0
  32. data/spec/fixtures/chain-json-rc.yaml +20 -0
  33. data/spec/fixtures/chain-rc.yaml +36 -0
  34. data/spec/fixtures/frame-spec-cc.yaml +43 -0
  35. data/spec/fixtures/frame-spec-rc.yaml +27 -0
  36. data/spec/fixtures/global-cc.yaml +38 -0
  37. data/spec/fixtures/global-rc.yaml +23 -0
  38. data/spec/fixtures/graph-cc.yaml +48 -0
  39. data/spec/fixtures/graph-json-cc.yaml +24 -0
  40. data/spec/fixtures/graph-json-rc.yaml +20 -0
  41. data/spec/fixtures/graph-rc.yaml +38 -0
  42. data/spec/fixtures/series-irregular-cc.yaml +58 -0
  43. data/spec/fixtures/series-irregular-json-cc.yaml +24 -0
  44. data/spec/fixtures/series-irregular-json-rc.yaml +20 -0
  45. data/spec/fixtures/series-irregular-rc.yaml +41 -0
  46. data/spec/fixtures/series-regular-cc.yaml +63 -0
  47. data/spec/fixtures/series-regular-json-cc.yaml +24 -0
  48. data/spec/fixtures/series-regular-json-rc.yaml +20 -0
  49. data/spec/fixtures/series-regular-rc.yaml +46 -0
  50. data/spec/fixtures/stream-cc.yaml +49 -0
  51. data/spec/fixtures/stream-json-cc.yaml +48 -0
  52. data/spec/fixtures/stream-json-rc.yaml +32 -0
  53. data/spec/fixtures/stream-rc.yaml +36 -0
  54. data/spec/fixtures/tangent-point-cc.yaml +43 -0
  55. data/spec/fixtures/tangent-point-rc.yaml +38 -0
  56. data/spec/fixtures/time-cc.yaml +32 -0
  57. data/spec/fixtures/time-rc.yaml +20 -0
  58. data/spec/modspec/conformance_class_spec.rb +154 -0
  59. data/spec/modspec/conformance_test_spec.rb +76 -0
  60. data/spec/modspec/normative_statement_spec.rb +81 -0
  61. data/spec/modspec/normative_statements_class_spec.rb +61 -0
  62. data/spec/modspec/suite_spec.rb +109 -0
  63. data/spec/modspec_spec.rb +25 -0
  64. data/spec/requirements_class.liquid +93 -0
  65. data/spec/spec_helper.rb +16 -0
  66. metadata +71 -61
@@ -0,0 +1,32 @@
1
+ ---
2
+ normative_statements_classes:
3
+ - name: JSON encoding of Stream SDUs
4
+ identifier: /req/stream-encoding-json
5
+ description: |
6
+ Requirements for the JSON encoding of Stream SDUs.
7
+ implements:
8
+ - /req/stream
9
+
10
+ normative_statements:
11
+
12
+ - name: Stream Element specification as JSON schema
13
+ identifier: /req/stream-encoding-json/element
14
+ statement: |
15
+ A JSON-encoded GeoPose Stream Element SHALL conform to the GeoPose Stream Element
16
+ JSON-Schema 2019-9 definition (<<streamelement_json_schema>>).
17
+
18
+ - name: Stream Header specification as JSON schema
19
+ identifier: /req/stream-encoding-json/header
20
+ statement: |
21
+ A JSON-encoded GeoPose Stream Element SHALL conform to the GeoPose Stream Header
22
+ JSON-Schema 2019-9 definition (<<streamheader_json_schema>>).
23
+ guidance:
24
+ - |
25
+ This JSON encoding is extensible because the JSON-Schema
26
+ "additionalProperties" property is set to the default value of *true*.
27
+
28
+ - name: Stream Record specification as JSON schema
29
+ identifier: /req/stream-encoding-json/record
30
+ statement: |
31
+ A JSON-encoded GeoPose Stream Record (a recorded Stream) SHALL conform to the GeoPose Stream Record
32
+ JSON-Schema 2019-9 definition (<<streamrecord_json_schema>>).
@@ -0,0 +1,36 @@
1
+ ---
2
+ normative_statements_classes:
3
+ - name: StreamHeader and StreamElement logical model SDUs
4
+ identifier: /req/stream
5
+ description: |
6
+ The Stream target supports a network of object relative poses. The graph is a
7
+ directed acyclic graph, each node must either be an Extrinsic Frame or
8
+ reachable from an Extrinsic Frame.
9
+
10
+ dependencies:
11
+ - /req/global
12
+ - /req/frame-spec
13
+
14
+ normative_statements:
15
+
16
+ - name: Expression of outer frame in StreamHeader
17
+ identifier: /req/stream/header-initial-frame
18
+ statement: |
19
+ The `StreamHeader.outerFrame` attribute shall represent the initial frame
20
+ of the stream with a value that is an instant of the `ExplicitFrameSpec`
21
+ object.
22
+
23
+ - name: Expression of transition model in StreamHeader
24
+ identifier: /req/stream/header-transition-model
25
+ statement: |
26
+ The `StreamHeader.transitionModel` attribute shall have a value that is
27
+ an instance of the `TransitionModel` enumeration.
28
+
29
+ - name: Expression of stream elements in StreamElement
30
+ identifier: /req/stream/element
31
+ dependencies:
32
+ - /req/time/instant
33
+ statement: |
34
+ The `StreamElement.streamElement` attribute shall be implemented as an
35
+ array of `FrameAndTimeElement` objects, each of which is a pair of
36
+ `ExplicitFrameSpec` and `GeoPoseInstant` objects.
@@ -0,0 +1,43 @@
1
+ ---
2
+ conformance_classes:
3
+ - name: Tangent point conformance
4
+ identifier: /conf/tangent-point
5
+ target:
6
+ - /req/tangent-point
7
+ classification: "Target Type: SDU"
8
+ description: Conformance with tangent point requirements
9
+
10
+ tests:
11
+ - name: Verify tangent point height value meets specification
12
+ identifier: /conf/tangent-point/height
13
+ targets:
14
+ - /req/tangent-point/height
15
+ description: |
16
+ To confirm that a GeoPose `tangentPoint.h` attribute is expressed as a
17
+ height in meters above the WGS-84 ellipsoid and represented as a signed
18
+ real number.
19
+ purpose: |
20
+ Verify that this requirement is satisfied.
21
+ method: Inspection
22
+
23
+ - name: Verify tangent point latitude value meets specification
24
+ identifier: /conf/tangent-point/latitude
25
+ targets:
26
+ - /req/tangent-point/latitude
27
+ description: |
28
+ To confirm that a GeoPose `tangentPoint.latitude` attribute is expressed
29
+ as an angle in decimal degrees.
30
+ purpose: |
31
+ Verify that this requirement is satisfied
32
+ method: Inspection
33
+
34
+ - name: Verify tangent point longitude value meets specification
35
+ identifier: /conf/tangent-point/longitude
36
+ targets:
37
+ - /req/tangent-point/longitude
38
+ description: |
39
+ To confirm that a GeoPose `tangentPoint.longitude` attribute is expressed
40
+ as an angle in decimal degrees.
41
+ purpose: |
42
+ Verify that this requirement is satisfied
43
+ method: Inspection
@@ -0,0 +1,38 @@
1
+ ---
2
+ normative_statements_classes:
3
+ - name: Tangent point requirements
4
+ identifier: /req/tangent-point
5
+ description: |
6
+ Common tangent point requirements for SDUs that include tangent points.
7
+ guidance: |
8
+ The tangent plane `longitude`, `latitude`, and `h` parameters are
9
+ specified without any conditions or constraints on precision to be used in
10
+ an implementation. Any such constraints would be found as requirements on a
11
+ specific implementation as an encoding.
12
+
13
+ normative_statements:
14
+
15
+ - name: Tangent point height value specification
16
+ identifier: /req/tangent-point/height
17
+ statement: |
18
+ An instance of a GeoPose `tangentPoint.h` attribute SHALL be expressed as
19
+ a height in meters above the WGS-84 ellipsoid, represented as a signed as
20
+ a signed floating point value conforming to IEEE 754. If the tangent point
21
+ is above the WGS-84 ellipsoid, the value SHALL be positive. If the tangent
22
+ point is below the WGS-84 ellipsoid, the value SHALL be negative.
23
+
24
+ - name: Tangent point latitude value specification
25
+ identifier: /req/tangent-point/latitude
26
+ statement: |
27
+ An instance of GeoPose tangentPoint.latitude attribute SHALL be expressed
28
+ as decimal degrees and represented as a signed floating point
29
+ value conforming to IEEE 754.. The minimum value shall be 90.0 degrees
30
+ and the maximum value shall be 90.0 degrees.
31
+
32
+ - name: Tangent point longitude value specification
33
+ identifier: /req/tangent-point/longitude
34
+ statement: |
35
+ An instance of a GeoPose tangentPoint.longitude attribute SHALL be
36
+ expressed as decimal degrees and represented as a signed floating point
37
+ value conforming to IEEE 754. The minimum value shall be -180.0 degrees
38
+ and the maximum value shall be 180.0 degrees.
@@ -0,0 +1,32 @@
1
+ ---
2
+ conformance_classes:
3
+ - name: Time specification requirements
4
+ identifier: /conf/time
5
+ target:
6
+ - /req/time
7
+ description: Conformance with GeoPose time-related requirements
8
+
9
+ tests:
10
+ - name: Verify values of GeoPose_Instant
11
+ identifier: /conf/time/instant
12
+ targets:
13
+ - /req/time/instant
14
+ description: |
15
+ Verify that a `GeoPose_Instant` object expresses Unix time in seconds multiplied
16
+ by 1,000, with the unit of measure in milliseconds.
17
+ purpose: |
18
+ To confirm the correct properties of a GeoPose_Instant object.
19
+ method: Inspection
20
+
21
+ - name: Verify values of GeoPose_Duration
22
+ identifier: /conf/time/duration
23
+ targets:
24
+ - /req/time/duration
25
+ description: |
26
+ Verify that a `GeoPose_Duration` object expresses time in seconds multiplied
27
+ by 1,000, with the unit of measure in milliseconds.
28
+ purpose: |
29
+ To confirm that a FrameSpecification.id attribute contains a string
30
+ uniquely specifying the identity of a reference frame specification as
31
+ defined by that authority.
32
+ method: Inspection
@@ -0,0 +1,20 @@
1
+ ---
2
+ normative_statements_classes:
3
+ - name: Time specification requirements
4
+ identifier: /req/time
5
+ description: |
6
+ Requirements on GeoPose time-related objects.
7
+
8
+ normative_statements:
9
+
10
+ - name: Implementation of GeoPose_Instant
11
+ identifier: /req/time/instant
12
+ statement: |
13
+ The `GeoPose_Instant` object shall express Unix Time in seconds multiplied
14
+ by 1,000, with the unit of measure in milliseconds.
15
+
16
+ - name: Implementation of GeoPose_Duration
17
+ identifier: /req/time/duration
18
+ statement: |
19
+ The `GeoPose_Duration` object shall express time in seconds multiplied by
20
+ 1,000, with the unit of measure in milliseconds.
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Modspec::ConformanceClass do
4
+ let(:normative_statements_class) do
5
+ Modspec::NormativeStatementsClass.new(
6
+ identifier: "/req/basic-ypr",
7
+ name: "Basic-YPR logical model SDU",
8
+ description: "The Basic-YPR Target has a simple structure with no options.",
9
+ dependencies: ["/req/global", "/req/tangent-point"],
10
+ normative_statements: [
11
+ Modspec::NormativeStatement.new(
12
+ identifier: "/req/basic-ypr/position",
13
+ name: "Expression of outer frame",
14
+ statement: "The `Basic_YPR.position` attribute shall represent the outer frame, specified by an implicit WGS-84 CRS and an implicit EPSG 4461-CS (LTP-ENU) coordinate system and explicit parameters to define the tangent point."
15
+ ),
16
+ Modspec::NormativeStatement.new(
17
+ identifier: "/req/basic-ypr/angles",
18
+ name: "Expression of inner frame",
19
+ statement: "The `Basic_YPR.angles` attribute shall represent the inner frame, which is a rotation-only transformation with Yaw, Pitch, and Roll (YPR) angles."
20
+ )
21
+ ]
22
+ )
23
+ end
24
+
25
+ let(:conformance_class) do
26
+ Modspec::ConformanceClass.new(
27
+ identifier: "/conf/basic-ypr",
28
+ name: "Basic-YPR logical model SDU conformance",
29
+ target: ["/req/basic-ypr"],
30
+ classification: "Target Type: SDU",
31
+ description: "Conformance with Basic-YPR logical model SDU",
32
+ dependencies: ["/conf/global", "/conf/tangent-point"],
33
+ tests: [
34
+ Modspec::ConformanceTest.new(
35
+ identifier: "/conf/basic-ypr/position",
36
+ name: "Verify expression of outer frame",
37
+ targets: ["/req/basic-ypr/position"],
38
+ description: "To confirm that an implementation of a Basic-YPR consists of an Outer Frame specified by an implicit WGS-84 CRS and an implicit EPSG 4461-CS (LTP-ENU) coordinate system and explicit parameters to define the tangent point.",
39
+ purpose: "Verify that this requirement is satisfied.",
40
+ method: "Inspection"
41
+ ),
42
+ Modspec::ConformanceTest.new(
43
+ identifier: "/conf/basic-ypr/angles",
44
+ name: "Verify expression of inner frame",
45
+ targets: ["/req/basic-ypr/angles"],
46
+ description: "To confirm that the Inner Frame is expressed as a rotation-only transformation using Yaw, Pitch, and Roll angles.",
47
+ purpose: "Verify that this requirement is satisfied.",
48
+ method: "Inspection"
49
+ )
50
+ ]
51
+ )
52
+ end
53
+
54
+ let(:global_class) do
55
+ Modspec::NormativeStatementsClass.new(
56
+ identifier: "/req/global",
57
+ name: "Global requirements",
58
+ normative_statements: [
59
+ Modspec::NormativeStatement.new(
60
+ identifier: "/req/global/sdu",
61
+ name: "SDU conformance",
62
+ statement: "SDUs shall conform to the logical model."
63
+ )
64
+ ]
65
+ )
66
+ end
67
+
68
+ let(:tangent_point_class) do
69
+ Modspec::NormativeStatementsClass.new(
70
+ identifier: "/req/tangent-point",
71
+ name: "Tangent point requirements",
72
+ normative_statements: [
73
+ Modspec::NormativeStatement.new(
74
+ identifier: "/req/tangent-point/height",
75
+ name: "Tangent point height",
76
+ statement: "Tangent point height shall be specified."
77
+ )
78
+ ]
79
+ )
80
+ end
81
+
82
+ let(:global_conf_class) do
83
+ Modspec::ConformanceClass.new(
84
+ identifier: "/conf/global",
85
+ name: "Global conformance",
86
+ tests: [
87
+ Modspec::ConformanceTest.new(
88
+ identifier: "/conf/global/sdu",
89
+ name: "Verify SDU conformance",
90
+ targets: ["/req/global/sdu"],
91
+ description: "To confirm that an implementation of an SDU conforms to the logical model.",
92
+ purpose: "Verify that this requirement is satisfied.",
93
+ method: "Inspection"
94
+ )
95
+ ]
96
+ )
97
+ end
98
+
99
+ let(:tangent_point_conf_class) do
100
+ Modspec::ConformanceClass.new(
101
+ identifier: "/conf/tangent-point",
102
+ name: "Tangent point conformance",
103
+ tests: [
104
+ Modspec::ConformanceTest.new(
105
+ identifier: "/conf/tangent-point/height",
106
+ name: "Verify tangent point height",
107
+ targets: ["/req/tangent-point/height"],
108
+ description: "To confirm that an implementation of a Tangent Point specifies the height of the Tangent Point.",
109
+ purpose: "Verify that this requirement is satisfied.",
110
+ method: "Inspection"
111
+ )
112
+
113
+ ]
114
+ )
115
+ end
116
+
117
+ let(:suite) do
118
+ suite = Modspec::Suite.new
119
+ suite.conformance_classes = [conformance_class, global_conf_class, tangent_point_conf_class]
120
+ suite.normative_statements_classes = [normative_statements_class, global_class, tangent_point_class]
121
+ suite.setup_relationships
122
+ suite
123
+ end
124
+
125
+ it "has an identifier" do
126
+ expect(conformance_class.identifier).to eq("/conf/basic-ypr")
127
+ end
128
+
129
+ it "has a name" do
130
+ expect(conformance_class.name).to eq("Basic-YPR logical model SDU conformance")
131
+ end
132
+
133
+ it "has conformance tests" do
134
+ expect(conformance_class.tests).not_to be_empty
135
+ expect(conformance_class.tests.length).to eq(2)
136
+ end
137
+
138
+ describe "#validate" do
139
+ it "returns no errors for a valid conformance class" do
140
+ errors = suite.validate
141
+ if errors.any?
142
+ puts "Validation errors:"
143
+ errors.each { |error| puts " #{error}" }
144
+ end
145
+ expect(errors).to be_empty
146
+ end
147
+
148
+ it "returns errors if there are no conformance tests" do
149
+ conformance_class.tests = []
150
+ errors = conformance_class.validate
151
+ expect(errors).not_to be_empty
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Modspec::ConformanceTest do
4
+ let(:normative_statement) do
5
+ Modspec::NormativeStatement.new(
6
+ identifier: "/req/basic-ypr/position",
7
+ name: "Expression of outer frame",
8
+ statement: "The `Basic_YPR.position` attribute shall represent the outer frame, specified by an implicit WGS-84 CRS and an implicit EPSG 4461-CS (LTP-ENU) coordinate system and explicit parameters to define the tangent point."
9
+ )
10
+ end
11
+
12
+ let(:normative_statements_class) do
13
+ Modspec::NormativeStatementsClass.new(
14
+ identifier: "/req/basic-ypr",
15
+ name: "Basic-YPR logical model SDU",
16
+ normative_statements: [normative_statement]
17
+ )
18
+ end
19
+
20
+ let(:conformance_test) do
21
+ Modspec::ConformanceTest.new(
22
+ identifier: "/conf/basic-ypr/position",
23
+ name: "Verify expression of outer frame",
24
+ targets: ["/req/basic-ypr/position"],
25
+ description: "To confirm that an implementation of a Basic-YPR consists of an Outer Frame specified by an implicit WGS-84 CRS and an implicit EPSG 4461-CS (LTP-ENU) coordinate system and explicit parameters to define the tangent point.",
26
+ purpose: "Verify that this requirement is satisfied.",
27
+ method: "Inspection"
28
+ )
29
+ end
30
+
31
+ let(:conformance_class) do
32
+ Modspec::ConformanceClass.new(
33
+ identifier: "/conf/basic-ypr",
34
+ name: "Basic-YPR logical model SDU conformance",
35
+ tests: [conformance_test]
36
+ )
37
+ end
38
+
39
+ let(:suite) do
40
+ suite = Modspec::Suite.new
41
+ suite.normative_statements_classes = [normative_statements_class]
42
+ suite.conformance_classes = [conformance_class]
43
+ suite.setup_relationships
44
+ suite
45
+ end
46
+
47
+ before do
48
+ suite # Ensure the suite is created and relationships are set up
49
+ end
50
+
51
+ it "has an identifier" do
52
+ expect(conformance_test.identifier).to eq("/conf/basic-ypr/position")
53
+ end
54
+
55
+ it "has a description" do
56
+ expect(conformance_test.description).not_to be_nil
57
+ end
58
+
59
+ it "has targets" do
60
+ expect(conformance_test.targets).to eq(["/req/basic-ypr/position"])
61
+ end
62
+
63
+ describe "#validate" do
64
+ it "returns no errors for a valid conformance test" do
65
+ errors = conformance_test.validate
66
+ expect(errors).to be_empty
67
+ end
68
+ end
69
+ it "has a corresponding requirement" do
70
+ expect(conformance_test.corresponding_requirements).to include(normative_statement)
71
+ end
72
+
73
+ it "belongs to a conformance class" do
74
+ expect(conformance_test.parent_class).to eq(conformance_class)
75
+ end
76
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Modspec::NormativeStatement do
4
+ let(:global_sdu_statement) do
5
+ Modspec::NormativeStatement.new(
6
+ identifier: "/req/global/sdu",
7
+ name: "SDU conforms to the 'Structural Data Unit - SDU' stereotype",
8
+ statement: "Implementations using encoded SDUs SHALL conform to the logical description of the Logical Model elements with the 'Structural Data Unit - SDU' stereotype."
9
+ )
10
+ end
11
+
12
+ let(:tangent_point_statement) do
13
+ Modspec::NormativeStatement.new(
14
+ identifier: "/req/tangent-point",
15
+ name: "Tangent point requirements",
16
+ statement: "Common tangent point requirements for SDUs that include tangent points."
17
+ )
18
+ end
19
+
20
+ let(:normative_statement) do
21
+ Modspec::NormativeStatement.new(
22
+ identifier: "/req/basic-ypr/position",
23
+ name: "Expression of outer frame",
24
+ statement: "The `Basic_YPR.position` attribute shall represent the outer frame, specified by an implicit WGS-84 CRS and an implicit EPSG 4461-CS (LTP-ENU) coordinate system and explicit parameters to define the tangent point.",
25
+ obligation: "requirement",
26
+ subject: "Basic_YPR.position",
27
+ inherit: ["/req/global/sdu"],
28
+ dependencies: ["/req/tangent-point"],
29
+ guidance: ["Ensure the coordinate system is correctly specified."]
30
+ )
31
+ end
32
+
33
+ let(:suite) do
34
+ suite = Modspec::Suite.new
35
+ global_class = Modspec::NormativeStatementsClass.new(
36
+ identifier: "/req/global",
37
+ normative_statements: [global_sdu_statement]
38
+ )
39
+ tangent_point_class = Modspec::NormativeStatementsClass.new(
40
+ identifier: "/req/tangent-point",
41
+ normative_statements: [tangent_point_statement]
42
+ )
43
+ basic_ypr_class = Modspec::NormativeStatementsClass.new(
44
+ identifier: "/req/basic-ypr",
45
+ normative_statements: [normative_statement]
46
+ )
47
+ suite.normative_statements_classes = [global_class, tangent_point_class, basic_ypr_class]
48
+ suite
49
+ end
50
+
51
+ it "has an identifier" do
52
+ expect(normative_statement.identifier).to eq("/req/basic-ypr/position")
53
+ end
54
+
55
+ it "has a name" do
56
+ expect(normative_statement.name).to eq("Expression of outer frame")
57
+ end
58
+
59
+ it "has a statement" do
60
+ expect(normative_statement.statement).not_to be_nil
61
+ end
62
+
63
+ it "has a valid obligation" do
64
+ expect(%w[requirement recommendation permission]).to include(normative_statement.obligation)
65
+ end
66
+
67
+ describe "#validate" do
68
+ it "returns no errors for a valid normative statement" do
69
+ errors = normative_statement.validate
70
+ expect(errors).to be_empty
71
+ end
72
+
73
+ it "returns errors for an invalid obligation" do
74
+ normative_statement.obligation = "invalid"
75
+ expect { normative_statement.validate! }.to raise_error(Lutaml::Model::ValidationError) do |error|
76
+ expect(error).to include(Lutaml::Model::InvalidValueError)
77
+ expect(error.error_messages).to include("obligation is `invalid`, must be one of the following [recommendation, permission, requirement]")
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Modspec::NormativeStatementsClass do
4
+ let(:normative_statement1) do
5
+ Modspec::NormativeStatement.new(
6
+ identifier: "/req/basic-ypr/position",
7
+ name: "Expression of outer frame",
8
+ statement: "The `Basic_YPR.position` attribute shall represent the outer frame, specified by an implicit WGS-84 CRS and an implicit EPSG 4461-CS (LTP-ENU) coordinate system and explicit parameters to define the tangent point."
9
+ )
10
+ end
11
+
12
+ let(:normative_statement2) do
13
+ Modspec::NormativeStatement.new(
14
+ identifier: "/req/basic-ypr/angles",
15
+ name: "Expression of inner frame",
16
+ statement: "The `Basic_YPR.angles` attribute shall represent the inner frame, which is a rotation-only transformation with Yaw, Pitch, and Roll (YPR) angles."
17
+ )
18
+ end
19
+
20
+ let(:normative_statements_class) do
21
+ Modspec::NormativeStatementsClass.new(
22
+ identifier: "/req/basic-ypr",
23
+ name: "Basic-YPR logical model SDU",
24
+ description: "The Basic-YPR Target has a simple structure with no options. Position is specified as a point in an LTP-ENU frame and rotation is specified by yaw, pitch, and roll angles specified in decimal degrees.",
25
+ dependencies: ["/req/global", "/req/tangent-point"],
26
+ normative_statements: [normative_statement1, normative_statement2]
27
+ )
28
+ end
29
+
30
+ let(:suite) do
31
+ suite = Modspec::Suite.new
32
+ suite.normative_statements_classes = [normative_statements_class]
33
+ suite
34
+ end
35
+
36
+ it "has an identifier" do
37
+ expect(normative_statements_class.identifier).to eq("/req/basic-ypr")
38
+ end
39
+
40
+ it "has a name" do
41
+ expect(normative_statements_class.name).to eq("Basic-YPR logical model SDU")
42
+ end
43
+
44
+ it "has normative statements" do
45
+ expect(normative_statements_class.normative_statements).not_to be_empty
46
+ expect(normative_statements_class.normative_statements.length).to eq(2)
47
+ end
48
+
49
+ describe "#validate" do
50
+ it "returns no errors for a valid normative statements class" do
51
+ errors = normative_statements_class.validate
52
+ expect(errors).to be_empty
53
+ end
54
+
55
+ it "returns errors if there are no normative statements" do
56
+ normative_statements_class.normative_statements = []
57
+ errors = normative_statements_class.validate
58
+ expect(errors).not_to be_empty
59
+ end
60
+ end
61
+ end