modspec 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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 +67 -57
@@ -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