spdx 3.1.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|