spdx 3.1.0 → 4.0.0
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/README.md +66 -10
- data/lib/spdx.rb +49 -2
- data/lib/spdx/version.rb +1 -1
- data/lib/spdx_grammar.rb +42 -12
- data/lib/spdx_parser.rb +36 -8
- data/lib/spdx_parser.treetop +27 -18
- data/spec/spdx_spec.rb +83 -29
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57eb1d8fa593ee4d8502733dd5257f050a5a421e8e272036517f146a362f1eea
|
4
|
+
data.tar.gz: e6d33c4be673643a9e052df93fcac4d41a3c8c809fca6293482979f0ffb62fb1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29d8dd9dd27711bc077d2a6828cfa58a8f378a5bcd2e6a0b3915fc070e2561b03b412f7683027985d50624a013310d6e4bdd4f053e3646c4fb659d4675553fd6
|
7
|
+
data.tar.gz: 03ea3f99549c654a817c53077f584feaacd4a85200f008e987a7aebac684435974269ab6edac88a22d253d78f4dd9171ab12f6713c9241a5cf153966ae8b7863
|
data/README.md
CHANGED
@@ -20,25 +20,81 @@ Or install it yourself as:
|
|
20
20
|
## Usage
|
21
21
|
|
22
22
|
```ruby
|
23
|
-
Spdx.
|
23
|
+
Spdx.valid?("(MIT OR AGPL-3.0+)")
|
24
24
|
=> true
|
25
25
|
```
|
26
|
+
|
26
27
|
```ruby
|
27
|
-
Spdx.
|
28
|
-
=>
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
License+License0 offset=2, "MIT"
|
33
|
-
LogicalOr+Or0 offset=5, " OR " (space1,space2)
|
34
|
-
License+License0 offset=9, "AGPL-3.0+"
|
28
|
+
Spdx.parse("MIT OR AGPL-3.0+")
|
29
|
+
=> LogicalOr+OrExpression4 offset=0, "MIT OR AGPL-3.0+":
|
30
|
+
License+LicenseId0 offset=0, "MIT" (idstring)
|
31
|
+
LicensePlus+SimpleExpression0 offset=7, "AGPL-3.0+" (license_id):
|
32
|
+
License+LicenseId0 offset=7, "AGPL-3.0" (idstring)
|
35
33
|
```
|
36
34
|
|
35
|
+
```ruby
|
36
|
+
Spdx.normalize("Mit OR agpl-3.0+ AND APACHE-2.0")
|
37
|
+
=> "MIT OR (AGPL-3.0+ AND Apache-2.0)"
|
38
|
+
|
39
|
+
Spdx.normalize("Mit OR agpl-3.0+ AND APACHE-2.0", top_level_parens: true)
|
40
|
+
=> "(MIT OR (AGPL-3.0+ AND Apache-2.0))"
|
41
|
+
```
|
42
|
+
|
43
|
+
### Nodes
|
44
|
+
|
45
|
+
Parsed SPDX license expressions can be a number of various nodes. Each of these nodes share a few methods, most notably `text_value` which contains the text that spans that node, and `licenses` which contains an array of individual licenses used in that portion of the expression. Below are the nodes in more detail, and their additional methods.
|
46
|
+
|
47
|
+
#### `License`
|
48
|
+
|
49
|
+
This node represents a single license, and is the result of the most simple form of expression, e.g. `Spdx.parse("MIT")`. It can also be found as a child node of other nodes.
|
50
|
+
|
51
|
+
#### `LicensePlus`
|
52
|
+
|
53
|
+
This node represents the current version of a license or any later version, e.g. `Spdx.parse("CDDL-1.0+")`. The inner license node can be found via the `child` method.
|
54
|
+
|
55
|
+
#### `LicenseRef`
|
56
|
+
|
57
|
+
This node represents a reference to a license not defined in the SPDX license list, e.g. `Spdx.parse("LicenseRef-23")`.
|
58
|
+
|
59
|
+
#### `DocumentRef`
|
60
|
+
|
61
|
+
Similar to `LicenseRef`, this node also represents a reference to a license not defined in the SPDX license list, e.g. `Spdx.parse("DocumentRef-spdx-tool-1.2:LicenseRef-MIT-Style-2")`.
|
62
|
+
|
63
|
+
#### `LicenseException`
|
64
|
+
|
65
|
+
This node represents an exception to a license. See `With`.
|
66
|
+
|
67
|
+
#### `With`
|
68
|
+
|
69
|
+
This node represents a license with an SPDX-defined license exception, e.g. `Spdx.parse("GPL-2.0-or-later WITH Bison-exception-2.2")`. This node has two extra methods, `license` and `exception`, which return the nodes for the license portion of the expression and the exception portion of the expression, respectively.
|
70
|
+
|
71
|
+
#### `LogicalAnd`
|
72
|
+
|
73
|
+
This node represents an "AND" expression, e.g. `Spdx.parse("LGPL-2.1-only AND MIT")`. This node has two extra methods, `left` and `right`, which return the node for the left side of the expression and the node for the right side of the expression, respectively. Any node can be the child of `LogicalAnd`, including `LogicalAnd`/`LogicalOr`.
|
74
|
+
|
75
|
+
`Spdx.parse("(MIT AND GPL-1.0) OR MPL-2.0 AND Apache-2.0")` would result in the following tree:
|
76
|
+
|
77
|
+
```txt
|
78
|
+
LogicalOr
|
79
|
+
├── LogicalAnd
|
80
|
+
│ ├── License (MIT)
|
81
|
+
│ └── License (GPL-1.0)
|
82
|
+
└── LogicalAnd
|
83
|
+
├── License (MPL-2.0)
|
84
|
+
└── License Apache-2.0
|
85
|
+
```
|
86
|
+
|
87
|
+
#### `LogicalOr`
|
88
|
+
|
89
|
+
The same as `LogicalAnd`, but for "OR" expressions.
|
90
|
+
|
37
91
|
## Testing
|
38
92
|
|
39
93
|
Run the tests with:
|
40
94
|
|
41
|
-
|
95
|
+
```sh
|
96
|
+
bundle exec rspec
|
97
|
+
```
|
42
98
|
|
43
99
|
## Contributing
|
44
100
|
|
data/lib/spdx.rb
CHANGED
@@ -61,7 +61,54 @@ module Spdx
|
|
61
61
|
@exceptions_downcase
|
62
62
|
end
|
63
63
|
|
64
|
-
def self.
|
64
|
+
def self.normalize(spdx_string, top_level_parens: false)
|
65
|
+
normalize_tree(SpdxParser.parse(spdx_string), parens: top_level_parens)
|
66
|
+
end
|
67
|
+
|
68
|
+
private_class_method def self.normalize_tree(node, parens: true)
|
69
|
+
case node
|
70
|
+
when SpdxGrammar::LogicalAnd
|
71
|
+
left = normalize_tree(node.left)
|
72
|
+
right = normalize_tree(node.right)
|
73
|
+
if parens
|
74
|
+
"(#{left} AND #{right})"
|
75
|
+
else
|
76
|
+
"#{left} AND #{right}"
|
77
|
+
end
|
78
|
+
when SpdxGrammar::LogicalOr
|
79
|
+
left = normalize_tree(node.left)
|
80
|
+
right = normalize_tree(node.right)
|
81
|
+
if parens
|
82
|
+
"(#{left} OR #{right})"
|
83
|
+
else
|
84
|
+
"#{left} OR #{right}"
|
85
|
+
end
|
86
|
+
when SpdxGrammar::With
|
87
|
+
license = normalize_tree(node.license)
|
88
|
+
exception = normalize_tree(node.exception)
|
89
|
+
if parens
|
90
|
+
"(#{license} WITH #{exception})"
|
91
|
+
else
|
92
|
+
"#{license} WITH #{exception}"
|
93
|
+
end
|
94
|
+
when SpdxGrammar::None
|
95
|
+
"NONE"
|
96
|
+
when SpdxGrammar::NoAssertion
|
97
|
+
"NOASSERTION"
|
98
|
+
when SpdxGrammar::License
|
99
|
+
licenses_downcase[node.text_value.downcase]
|
100
|
+
when SpdxGrammar::LicensePlus
|
101
|
+
"#{normalize_tree(node.child)}+"
|
102
|
+
when SpdxGrammar::LicenseRef
|
103
|
+
node.text_value
|
104
|
+
when SpdxGrammar::DocumentRef
|
105
|
+
node.text_value
|
106
|
+
when SpdxGrammar::LicenseException
|
107
|
+
exceptions_downcase[node.text_value.downcase]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.valid?(spdx_string)
|
65
112
|
return false unless spdx_string.is_a?(String)
|
66
113
|
|
67
114
|
SpdxParser.parse(spdx_string)
|
@@ -70,7 +117,7 @@ module Spdx
|
|
70
117
|
false
|
71
118
|
end
|
72
119
|
|
73
|
-
def self.
|
120
|
+
def self.parse(spdx_string)
|
74
121
|
SpdxParser.parse(spdx_string)
|
75
122
|
end
|
76
123
|
end
|
data/lib/spdx/version.rb
CHANGED
data/lib/spdx_grammar.rb
CHANGED
@@ -1,19 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SpdxGrammar
|
4
|
-
class
|
4
|
+
class LogicalBinary < Treetop::Runtime::SyntaxNode
|
5
|
+
# Used internally
|
6
|
+
|
5
7
|
def licenses
|
6
|
-
|
8
|
+
(left.licenses + right.licenses).uniq
|
9
|
+
end
|
10
|
+
|
11
|
+
def left
|
12
|
+
elements[0]
|
13
|
+
end
|
14
|
+
|
15
|
+
def right
|
16
|
+
elements[1]
|
7
17
|
end
|
8
18
|
end
|
9
19
|
|
10
|
-
class
|
20
|
+
class LogicalAnd < LogicalBinary
|
11
21
|
end
|
12
22
|
|
13
|
-
class
|
23
|
+
class LogicalOr < LogicalBinary
|
14
24
|
end
|
15
25
|
|
16
26
|
class With < Treetop::Runtime::SyntaxNode
|
27
|
+
def licenses
|
28
|
+
license.licenses
|
29
|
+
end
|
30
|
+
|
31
|
+
def license
|
32
|
+
elements[0]
|
33
|
+
end
|
34
|
+
|
35
|
+
def exception
|
36
|
+
elements[1]
|
37
|
+
end
|
17
38
|
end
|
18
39
|
|
19
40
|
class None < Treetop::Runtime::SyntaxNode
|
@@ -30,22 +51,29 @@ module SpdxGrammar
|
|
30
51
|
|
31
52
|
class License < Treetop::Runtime::SyntaxNode
|
32
53
|
def licenses
|
33
|
-
text_value
|
54
|
+
[text_value]
|
34
55
|
end
|
35
56
|
end
|
36
57
|
|
37
|
-
class
|
58
|
+
class LicensePlus < Treetop::Runtime::SyntaxNode
|
59
|
+
def licenses
|
60
|
+
child.licenses
|
61
|
+
end
|
62
|
+
|
63
|
+
def child
|
64
|
+
elements[0]
|
65
|
+
end
|
38
66
|
end
|
39
67
|
|
40
68
|
class LicenseRef < Treetop::Runtime::SyntaxNode
|
41
69
|
def licenses
|
42
|
-
text_value
|
70
|
+
[text_value]
|
43
71
|
end
|
44
72
|
end
|
45
73
|
|
46
74
|
class DocumentRef < Treetop::Runtime::SyntaxNode
|
47
75
|
def licenses
|
48
|
-
text_value
|
76
|
+
[text_value]
|
49
77
|
end
|
50
78
|
end
|
51
79
|
|
@@ -53,10 +81,12 @@ module SpdxGrammar
|
|
53
81
|
# TODO: actually do license exceptions
|
54
82
|
end
|
55
83
|
|
56
|
-
class
|
57
|
-
|
58
|
-
|
59
|
-
|
84
|
+
class GroupedExpression < Treetop::Runtime::SyntaxNode
|
85
|
+
# Used internally
|
86
|
+
end
|
87
|
+
|
88
|
+
class Operand < Treetop::Runtime::SyntaxNode
|
89
|
+
# Used internally
|
60
90
|
end
|
61
91
|
|
62
92
|
class SpdxParseError < StandardError
|
data/lib/spdx_parser.rb
CHANGED
@@ -21,20 +21,48 @@ class SpdxParser
|
|
21
21
|
|
22
22
|
private_class_method def self.parse_tree(data)
|
23
23
|
parser = SpdxGrammarParser.new # The generated grammar parser is not thread safe
|
24
|
-
# Couldn't figure out treetop to make parens optional
|
25
|
-
data = "(#{data})" unless SKIP_PARENS.include?(data)
|
26
24
|
|
27
25
|
tree = parser.parse(data)
|
28
26
|
raise SpdxGrammar::SpdxParseError, "Unable to parse expression '#{data}'. Parse error at offset: #{parser.index}" if tree.nil?
|
29
27
|
|
30
|
-
|
31
|
-
tree
|
28
|
+
prune(tree)
|
32
29
|
end
|
33
30
|
|
34
|
-
private_class_method def self.
|
35
|
-
|
31
|
+
private_class_method def self.clean(root_node)
|
32
|
+
root_node.elements&.delete_if { |node| node.instance_of?(Treetop::Runtime::SyntaxNode) }
|
33
|
+
end
|
34
|
+
|
35
|
+
private_class_method def self.prune(root_node)
|
36
|
+
clean(root_node)
|
37
|
+
|
38
|
+
root_node.elements&.each_with_index do |node, i|
|
39
|
+
case node
|
40
|
+
when SpdxGrammar::GroupedExpression, SpdxGrammar::Operand
|
41
|
+
clean(node)
|
42
|
+
child = node.elements[0]
|
43
|
+
child.parent = root_node
|
44
|
+
root_node.elements[i] = child
|
45
|
+
|
46
|
+
case child
|
47
|
+
when SpdxGrammar::GroupedExpression, SpdxGrammar::Operand
|
48
|
+
# re-prune if child's child is a GroupedExpression or Operand
|
49
|
+
prune(root_node)
|
50
|
+
else
|
51
|
+
prune(child)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
prune(node)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
case root_node
|
59
|
+
when SpdxGrammar::GroupedExpression
|
60
|
+
child = root_node.elements[0]
|
61
|
+
child.parent = root_node.parent
|
62
|
+
|
63
|
+
return child
|
64
|
+
end
|
36
65
|
|
37
|
-
root_node
|
38
|
-
root_node.elements.each { |node| clean_tree(node) }
|
66
|
+
root_node
|
39
67
|
end
|
40
68
|
end
|
data/lib/spdx_parser.treetop
CHANGED
@@ -1,47 +1,56 @@
|
|
1
1
|
grammar SpdxGrammar
|
2
2
|
|
3
|
-
rule
|
3
|
+
rule license_expression
|
4
4
|
compound_expression / none / no_assertion
|
5
5
|
end
|
6
6
|
|
7
7
|
rule compound_expression
|
8
|
-
|
8
|
+
or_expression / and_expression / expression / grouped_expression
|
9
9
|
end
|
10
10
|
|
11
|
-
rule
|
12
|
-
(
|
11
|
+
rule and_expression
|
12
|
+
( expression space <Operand> / grouped_expression space? <Operand> ) "AND" ( space ( and_expression / expression ) <Operand> / space? grouped_expression <Operand> ) <LogicalAnd>
|
13
13
|
end
|
14
14
|
|
15
|
-
rule
|
16
|
-
space "
|
15
|
+
rule or_expression
|
16
|
+
( ( and_expression / expression ) space <Operand> / grouped_expression space? <Operand> ) "OR" ( space compound_expression <Operand> / space? grouped_expression <Operand> ) <LogicalOr>
|
17
17
|
end
|
18
18
|
|
19
|
-
rule
|
20
|
-
space "
|
19
|
+
rule expression
|
20
|
+
simple_expression space "WITH" space license_exception_id <With> /
|
21
|
+
simple_expression
|
21
22
|
end
|
22
23
|
|
23
|
-
rule
|
24
|
-
|
24
|
+
rule grouped_expression
|
25
|
+
'(' compound_expression ')' <GroupedExpression>
|
25
26
|
end
|
26
27
|
|
27
|
-
rule
|
28
|
-
|
28
|
+
rule simple_expression
|
29
|
+
license_id '+' <LicensePlus> / license_id / ref
|
30
|
+
end
|
31
|
+
|
32
|
+
rule ref
|
33
|
+
document_ref / license_ref
|
29
34
|
end
|
30
35
|
|
31
36
|
rule license_ref
|
32
|
-
"LicenseRef-"
|
37
|
+
"LicenseRef-" idstring <LicenseRef>
|
33
38
|
end
|
34
39
|
|
35
40
|
rule document_ref
|
36
|
-
"DocumentRef-"
|
41
|
+
"DocumentRef-" idstring ':' license_ref <DocumentRef>
|
42
|
+
end
|
43
|
+
|
44
|
+
rule license_id
|
45
|
+
idstring &{|seq| Spdx.license_exists?(seq.first.text_value) } <License>
|
37
46
|
end
|
38
47
|
|
39
|
-
rule
|
40
|
-
|
48
|
+
rule license_exception_id
|
49
|
+
idstring &{|seq| Spdx.exception_exists?(seq.first.text_value) } <LicenseException>
|
41
50
|
end
|
42
51
|
|
43
|
-
rule
|
44
|
-
|
52
|
+
rule idstring
|
53
|
+
[a-zA-Z0-9\-\.]+
|
45
54
|
end
|
46
55
|
|
47
56
|
rule none
|
data/spec/spdx_spec.rb
CHANGED
@@ -4,67 +4,121 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
describe Spdx do
|
6
6
|
context "spdx parsing" do
|
7
|
-
context "
|
7
|
+
context "valid?" do
|
8
8
|
it "returns false for invalid spdx" do
|
9
|
-
expect(Spdx.
|
10
|
-
expect(Spdx.
|
11
|
-
expect(Spdx.
|
12
|
-
expect(Spdx.
|
13
|
-
expect(Spdx.
|
14
|
-
expect(Spdx.
|
9
|
+
expect(Spdx.valid?("AND AND")).to be false
|
10
|
+
expect(Spdx.valid?(" AND ")).to be false
|
11
|
+
expect(Spdx.valid?(" WITH ")).to be false
|
12
|
+
expect(Spdx.valid?("MIT AND ")).to be false
|
13
|
+
expect(Spdx.valid?("MIT OR MIT AND OR")).to be false
|
14
|
+
expect(Spdx.valid?("MIT OR FAKEYLICENSE")).to be false
|
15
|
+
expect(Spdx.valid?(nil)).to be false
|
16
|
+
expect(Spdx.valid?("")).to be false
|
17
|
+
expect(Spdx.valid?("MIT (MIT)")).to be false
|
15
18
|
end
|
16
19
|
it "returns true for valid spdx" do
|
17
|
-
expect(Spdx.
|
18
|
-
expect(Spdx.
|
19
|
-
expect(Spdx.
|
20
|
-
expect(Spdx.
|
20
|
+
expect(Spdx.valid?("(MIT OR MPL-2.0)")).to be true
|
21
|
+
expect(Spdx.valid?("MIT")).to be true
|
22
|
+
expect(Spdx.valid?("MIT OR MPL-2.0 AND AGPL-1.0")).to be true
|
23
|
+
expect(Spdx.valid?("MIT OR (GPL-1.0 OR MPL-2.0) AND AGPL-1.0")).to be true
|
24
|
+
expect(Spdx.valid?("MIT AND MPL-2.0 OR AGPL-1.0")).to be true
|
25
|
+
expect(Spdx.valid?("MIT AND (GPL-1.0 OR MPL-2.0) OR AGPL-1.0")).to be true
|
26
|
+
expect(Spdx.valid?("MIT OR (DocumentRef-something-1:LicenseRef-MIT-style-1 OR MPL-2.0) AND AGPL-1.0")).to be true
|
27
|
+
expect(Spdx.valid?("((MIT OR AGPL-1.0) AND (MIT OR MPL-2.0))")).to be true
|
28
|
+
expect(Spdx.valid?("MIT OR (MIT)")).to be true
|
21
29
|
end
|
22
30
|
it "returns true for NONE and NOASSERTION" do
|
23
|
-
expect(Spdx.
|
24
|
-
expect(Spdx.
|
25
|
-
expect(Spdx.
|
26
|
-
expect(Spdx.
|
31
|
+
expect(Spdx.valid?("NONE")).to be true
|
32
|
+
expect(Spdx.valid?("(NONE)")).to be false
|
33
|
+
expect(Spdx.valid?("NOASSERTION")).to be true
|
34
|
+
expect(Spdx.valid?("MIT OR NONE")).to be false
|
27
35
|
end
|
28
36
|
it "returns true for + expression" do
|
29
|
-
expect(Spdx.
|
37
|
+
expect(Spdx.valid?("AGPL-3.0+")).to be true
|
30
38
|
end
|
31
39
|
it "is case insentive for license ids" do
|
32
|
-
expect(Spdx.
|
40
|
+
expect(Spdx.valid?("mit OR agpl-3.0+")).to be true
|
33
41
|
end
|
34
42
|
it "handles LicenseRef" do
|
35
|
-
expect(Spdx.
|
43
|
+
expect(Spdx.valid?("MIT OR LicenseRef-MIT-style-1")).to be true
|
36
44
|
end
|
37
45
|
it "handles DocumentRef" do
|
38
|
-
expect(Spdx.
|
39
|
-
expect(Spdx.
|
46
|
+
expect(Spdx.valid?("MIT OR DocumentRef-something-1:LicenseRef-MIT-style-1")).to be true
|
47
|
+
expect(Spdx.valid?("MIT OR DocumentRef-something-1")).to be false
|
40
48
|
end
|
41
49
|
end
|
42
50
|
end
|
51
|
+
context "normalize" do
|
52
|
+
it "normalizes simple licenses" do
|
53
|
+
expect(Spdx.normalize("MIT")).to eq "MIT"
|
54
|
+
expect(Spdx.normalize("mit")).to eq "MIT"
|
55
|
+
expect(Spdx.normalize("MiT")).to eq "MIT"
|
56
|
+
expect(Spdx.normalize("(MiT)")).to eq "MIT"
|
57
|
+
expect(Spdx.normalize("(((MiT)))")).to eq "MIT"
|
58
|
+
expect(Spdx.normalize("LicenseRef-MIT-style-1")).to eq "LicenseRef-MIT-style-1"
|
59
|
+
expect(Spdx.normalize("DocumentRef-something-1:LicenseRef-MIT-style-1")).to eq "DocumentRef-something-1:LicenseRef-MIT-style-1"
|
60
|
+
expect(Spdx.normalize("Apache-2.0+")).to eq "Apache-2.0+"
|
61
|
+
expect(Spdx.normalize("apache-2.0+")).to eq "Apache-2.0+"
|
62
|
+
end
|
63
|
+
it "normalizes NONE/NOASSERTION" do
|
64
|
+
expect(Spdx.normalize("NONE")).to eq "NONE"
|
65
|
+
expect(Spdx.normalize("NOASSERTION")).to eq "NOASSERTION"
|
66
|
+
end
|
67
|
+
it "normalizes boolean expressions" do
|
68
|
+
expect(Spdx.normalize("mit AND gPL-2.0")).to eq "MIT AND GPL-2.0"
|
69
|
+
expect(Spdx.normalize("mit OR gPL-2.0")).to eq "MIT OR GPL-2.0"
|
70
|
+
expect(Spdx.normalize("mit OR gPL-2.0")).to eq "MIT OR GPL-2.0"
|
71
|
+
|
72
|
+
# With top level parens
|
73
|
+
expect(Spdx.normalize("mit AND gPL-2.0", top_level_parens: true)).to eq "(MIT AND GPL-2.0)"
|
74
|
+
expect(Spdx.normalize("mit OR gPL-2.0", top_level_parens: true)).to eq "(MIT OR GPL-2.0)"
|
75
|
+
expect(Spdx.normalize("mit OR gPL-2.0", top_level_parens: true)).to eq "(MIT OR GPL-2.0)"
|
76
|
+
|
77
|
+
# Does semantic grouping
|
78
|
+
expect(Spdx.normalize("mit OR gPL-2.0 AND apAcHe-2.0+")).to eq "MIT OR (GPL-2.0 AND Apache-2.0+)"
|
79
|
+
|
80
|
+
# But also preserves original groups
|
81
|
+
expect(Spdx.normalize("(mit OR gPL-2.0) AND apAcHe-2.0+")).to eq "(MIT OR GPL-2.0) AND Apache-2.0+"
|
82
|
+
end
|
83
|
+
it "normalizes WITH expressions" do
|
84
|
+
expect(Spdx.normalize("GPL-2.0-only WITH Classpath-exception-2.0")).to eq "GPL-2.0-only WITH Classpath-exception-2.0"
|
85
|
+
expect(Spdx.normalize("Gpl-2.0-ONLY WITH ClassPath-exception-2.0")).to eq "GPL-2.0-only WITH Classpath-exception-2.0"
|
86
|
+
|
87
|
+
# With top level parens
|
88
|
+
expect(Spdx.normalize("GPL-2.0-only WITH Classpath-exception-2.0", top_level_parens: true)).to eq "(GPL-2.0-only WITH Classpath-exception-2.0)"
|
89
|
+
expect(Spdx.normalize("Gpl-2.0-ONLY WITH ClassPath-exception-2.0", top_level_parens: true)).to eq "(GPL-2.0-only WITH Classpath-exception-2.0)"
|
90
|
+
|
91
|
+
expect(Spdx.normalize("EPL-2.0 OR (GPL-2.0-only WITH Classpath-exception-2.0)")).to eq "EPL-2.0 OR (GPL-2.0-only WITH Classpath-exception-2.0)"
|
92
|
+
expect(Spdx.normalize("epl-2.0 OR (gpl-2.0-only WITH classpath-exception-2.0)")).to eq "EPL-2.0 OR (GPL-2.0-only WITH Classpath-exception-2.0)"
|
93
|
+
expect(Spdx.normalize("epl-2.0 OR gpl-2.0-only WITH classpath-exception-2.0")).to eq "EPL-2.0 OR (GPL-2.0-only WITH Classpath-exception-2.0)"
|
94
|
+
expect(Spdx.normalize("epl-2.0 OR gpl-2.0-only WITH classpath-exception-2.0 AND mpl-2.0+")).to eq "EPL-2.0 OR ((GPL-2.0-only WITH Classpath-exception-2.0) AND MPL-2.0+)"
|
95
|
+
end
|
96
|
+
end
|
43
97
|
context "licenses" do
|
44
98
|
it "returns a list of possible licenses" do
|
45
|
-
expect(Spdx.
|
99
|
+
expect(Spdx.parse("MIT OR MPL-2.0").licenses).to eq ["MIT", "MPL-2.0"]
|
46
100
|
end
|
47
101
|
it "returns empty array for NONE or NOASSERTION" do
|
48
|
-
expect(Spdx.
|
49
|
-
expect(Spdx.
|
102
|
+
expect(Spdx.parse("NONE").licenses).to eq []
|
103
|
+
expect(Spdx.parse("NOASSERTION").licenses).to eq []
|
50
104
|
end
|
51
105
|
it "returns LicenseRefs" do
|
52
|
-
expect(Spdx.
|
106
|
+
expect(Spdx.parse("MIT OR LicenseRef-MIT-style-1").licenses).to eq %w[MIT LicenseRef-MIT-style-1]
|
53
107
|
end
|
54
108
|
it "returns DocumentRefs" do
|
55
|
-
expect(Spdx.
|
109
|
+
expect(Spdx.parse("MIT OR DocumentRef-something-1:LicenseRef-MIT-style-1").licenses).to eq %w[MIT DocumentRef-something-1:LicenseRef-MIT-style-1]
|
56
110
|
end
|
57
111
|
end
|
58
112
|
|
59
113
|
context "exceptions" do
|
60
|
-
it "parses a valid spdx
|
61
|
-
expect(Spdx.
|
114
|
+
it "parses a valid spdx WITH expression" do
|
115
|
+
expect(Spdx.valid?("EPL-2.0 OR (GPL-2.0-only WITH Classpath-exception-2.0)")).to be true
|
62
116
|
end
|
63
117
|
it "returns false for a license in the exception spot" do
|
64
|
-
expect(Spdx.
|
118
|
+
expect(Spdx.valid?("EPL-2.0 OR (GPL-2.0-only WITH AGPL-3.0)")).to be false
|
65
119
|
end
|
66
120
|
it "provides full details for a parse error" do
|
67
|
-
expect { Spdx.
|
121
|
+
expect { Spdx.parse("MIT OR ((WHAT)") }.to raise_error(SpdxGrammar::SpdxParseError, "Unable to parse expression 'MIT OR ((WHAT)'. Parse error at offset: 3")
|
68
122
|
end
|
69
123
|
end
|
70
124
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spdx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tidelift, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|