Repository: spdx/tools-python

Issue:
Tags and format properties are case sensitive
The spec says: "Tags and format properties are case sensitive".

The document contains:
```
FilesAnalyzed: TRUE
```

pyspdxtools -i hello.spdx

ERROR:root:The document is invalid. The following issues have been found:
package must contain no elements if files_analyzed is False, but found [Relationship(spdx_element_id='SPDXRef-Package-hello', relationship_type=<RelationshipType.CONTAINS: 6>, related_spdx_element_id='SPDXRef-hello-binary', comment=None), Relationship(spdx_element_id='SPDXRef-Package-hello', relationship_type=<RelationshipType.CONTAINS: 6>, related_spdx_element_id='SPDXRef-Makefile', comment=None), Relationship(spdx_element_id='SPDXRef-Package-hello', relationship_type=<RelationshipType.CONTAINS: 6>, related_spdx_element_id='SPDXRef-hello-src', comment=None)]
license_info_from_files must be None if files_analyzed is False, but is: [LicenseSymbol('GPL-3.0-or-later', aliases=('GPL-3.0+', 'LicenseRef-GPL-3.0-or-later'), is_exception=False)]
verification_code must be None if files_analyzed is False, but is: PackageVerificationCode(value='9d20237bb72087e87069f96afb41c6ca2fa2a342', excluded_files=[])

The error message is misleading.

It should say that TRUE is not a correct value for FilesAnalyzed, instead of saying that FilesAnalyzed is False and outputing an error message assuming that FilesAnalyzed is false.

[hello.spdx.txt](https://github.com/spdx/tools-python/files/12538780/hello.spdx.txt)


Discussion:

Patch:
diff --git a/src/spdx_tools/spdx/parser/tagvalue/parser.py b/src/spdx_tools/spdx/parser/tagvalue/parser.py
index ec843cc..a11f086 100644
--- a/src/spdx_tools/spdx/parser/tagvalue/parser.py
+++ b/src/spdx_tools/spdx/parser/tagvalue/parser.py
@@ -427,7 +427,14 @@ class Parser:
         if "files_analyzed" in self.current_element:
             self.current_element["logger"].append(f"Multiple values for {p[1]} found. Line: {p.lineno(1)}")
             return
-        self.current_element["files_analyzed"] = p[2] in ["true", "True"]
+        if p[2] == "true":
+            self.current_element["files_analyzed"] = True
+        elif p[2] == "false":
+            self.current_element["files_analyzed"] = False
+        else:
+            self.current_element["logger"].append(
+                f'The value of FilesAnalyzed must be either "true" or "false", but is: {p[2]}'
+            )
 
     @grammar_rule("primary_package_purpose : PRIMARY_PACKAGE_PURPOSE LINE")
     def p_primary_package_purpose(self, p):
diff --git a/src/spdx_tools/spdx/writer/tagvalue/package_writer.py b/src/spdx_tools/spdx/writer/tagvalue/package_writer.py
index 9be4ec4..f61474f 100644
--- a/src/spdx_tools/spdx/writer/tagvalue/package_writer.py
+++ b/src/spdx_tools/spdx/writer/tagvalue/package_writer.py
@@ -33,7 +33,7 @@ def write_package(package: Package, text_output: TextIO):
     write_actor("PackageOriginator", package.originator, text_output)
     write_value("PackageDownloadLocation", package.download_location, text_output)
 
-    write_value("FilesAnalyzed", package.files_analyzed, text_output)
+    write_value("FilesAnalyzed", str(package.files_analyzed).lower(), text_output)
     if package.verification_code:
         package_verification_code = get_package_verification_code_string(package.verification_code)
         write_value("PackageVerificationCode", package_verification_code, text_output)


Test patch:
diff --git a/tests/spdx/parser/tagvalue/test_package_parser.py b/tests/spdx/parser/tagvalue/test_package_parser.py
index e38351b..470f1e2 100644
--- a/tests/spdx/parser/tagvalue/test_package_parser.py
+++ b/tests/spdx/parser/tagvalue/test_package_parser.py
@@ -22,7 +22,7 @@ def test_parse_package():
             "SPDXID: SPDXRef-Package",
             "PackageVersion: 1:22.36.1-8+deb11u1",
             "PackageDownloadLocation: http://example.com/test",
-            "FilesAnalyzed: True",
+            "FilesAnalyzed: true",
             "PackageSummary: <text>Test package</text>",
             "PackageSourceInfo: <text>Version 1.0 of test</text>",
             "PackageFileName: test-1.0.zip",
@@ -123,6 +123,12 @@ def test_parse_package():
             "match specified grammar rule. Line: 2', 'Error while parsing "
             "ValidUntilDate: Token did not match specified grammar rule. Line: 3']",
         ),
+        (
+            f"SPDXID:{DOCUMENT_SPDX_ID}\nPackageName: TestPackage\nSPDXID:SPDXRef-Package\n"
+            "PackageDownloadLocation: download.com\nFilesAnalyzed: FALSE",
+            "Error while parsing Package: "
+            '[\'The value of FilesAnalyzed must be either "true" or "false", but is: FALSE\']',
+        ),
     ],
 )
 def test_parse_invalid_package(package_str, expected_message):
diff --git a/tests/spdx/writer/tagvalue/test_package_writer.py b/tests/spdx/writer/tagvalue/test_package_writer.py
index 26c8f9a..994931e 100644
--- a/tests/spdx/writer/tagvalue/test_package_writer.py
+++ b/tests/spdx/writer/tagvalue/test_package_writer.py
@@ -27,7 +27,7 @@ def test_package_writer():
             call(f"PackageSupplier: Person: {package.supplier.name} ({package.supplier.email})\n"),
             call(f"PackageOriginator: Person: {package.originator.name} ({package.originator.email})\n"),
             call(f"PackageDownloadLocation: {package.download_location}\n"),
-            call("FilesAnalyzed: True\n"),
+            call("FilesAnalyzed: true\n"),
             call(f"PackageVerificationCode: {package.verification_code.value} (excludes: ./exclude.py)\n"),
             call("PackageChecksum: SHA1: 71c4025dd9897b364f3ebbb42c484ff43d00791c\n"),
             call(f"PackageHomePage: {package.homepage}\n"),
diff --git a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py
index 86e7a2f..487ee0d 100644
--- a/tests/spdx/writer/tagvalue/test_tagvalue_writer.py
+++ b/tests/spdx/writer/tagvalue/test_tagvalue_writer.py
@@ -114,7 +114,7 @@ def test_correct_order_of_elements():
             call("PackageName: Test Package A\n"),
             call("SPDXID: SPDXRef-Package-A\n"),
             call("PackageDownloadLocation: \n"),
-            call("FilesAnalyzed: True\n"),
+            call("FilesAnalyzed: true\n"),
             call("\n"),
             call("## File Information\n"),
             call("FileName: Test File B\n"),
@@ -130,7 +130,7 @@ def test_correct_order_of_elements():
             call("PackageName: Test Package B\n"),
             call("SPDXID: SPDXRef-Package-B\n"),
             call("PackageDownloadLocation: \n"),
-            call("FilesAnalyzed: True\n"),
+            call("FilesAnalyzed: true\n"),
             call("\n"),
             call("\n"),
         ]
@@ -199,7 +199,7 @@ def test_same_file_in_multiple_packages():
             call("PackageName: Example package A\n"),
             call("SPDXID: SPDXRef-Package-A\n"),
             call("PackageDownloadLocation: https://download.com\n"),
-            call("FilesAnalyzed: True\n"),
+            call("FilesAnalyzed: true\n"),
             call("\n"),
             call("## File Information\n"),
             call("FileName: Example file\n"),
@@ -210,7 +210,7 @@ def test_same_file_in_multiple_packages():
             call("PackageName: Example package B\n"),
             call("SPDXID: SPDXRef-Package-B\n"),
             call("PackageDownloadLocation: https://download.com\n"),
-            call("FilesAnalyzed: True\n"),
+            call("FilesAnalyzed: true\n"),
             call("\n"),
             call("## Relationships\n"),
             call("Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-A\n"),