< Summary

Information
Class: Spdx3.Model.Lite.LiteDomainComplianceVisitor
Assembly: Spdx3
File(s): /home/runner/work/Spdx3/Spdx3/Spdx3/Model/Lite/LiteDomainComplianceVisitor.cs
Line coverage
84%
Covered lines: 161
Uncovered lines: 30
Coverable lines: 191
Total lines: 320
Line coverage: 84.2%
Branch coverage
69%
Covered branches: 36
Total branches: 52
Branch coverage: 69.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Findings()100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%11100%
Visit(...)100%210%
Visit(...)37.5%8892.3%
MustHaveExactlyOneRelationshipOfType(...)50%14638.46%
Visit(...)100%11100%
CheckNotNullOrEmpty(...)100%66100%
CheckAtLeastOneMemberOfType(...)80%111081.81%
CheckContainsOnlyType(...)62.5%14855.55%
CheckMatchesPattern(...)75%6450%
CheckNotNullOrEmpty(...)100%44100%
CheckNotNullOrEmpty(...)66.66%7675%

File(s)

/home/runner/work/Spdx3/Spdx3/Spdx3/Model/Lite/LiteDomainComplianceVisitor.cs

#LineLine coverage
 1using System.Collections;
 2using System.Text.RegularExpressions;
 3using Spdx3.Exceptions;
 4using Spdx3.Model.Core.Classes;
 5using Spdx3.Model.Core.Enums;
 6using Spdx3.Model.SimpleLicensing.Classes;
 7using Spdx3.Model.Software.Classes;
 8
 9namespace Spdx3.Model.Lite;
 10
 11/// <summary>
 12/// Visitor that checks a catalog for compliance with the Spdx Lite domain requirements.
 13/// See https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-lite/
 14/// </summary>
 15internal class LiteDomainComplianceVisitor : ILiteDomainComplianceVisitor
 16{
 3717    internal List<LiteDomainComplianceFinding> Findings { get; } = [];
 18
 19    public void Visit(SpdxDocument spdxDocument)
 20    {
 321        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, spdxDocument, nameof(spdxDocument.CreationInfo),
 322            spdxDocument.CreationInfo);
 323        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, spdxDocument, nameof(spdxDocument.SpdxId),
 324            spdxDocument.SpdxId);
 325        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, spdxDocument, nameof(spdxDocument.Element),
 326            (IList)spdxDocument.Element);
 327        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, spdxDocument, nameof(spdxDocument.RootElement),
 328            (IList)spdxDocument.RootElement);
 329        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, spdxDocument, nameof(spdxDocument.Comment),
 330            spdxDocument.Comment);
 331        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, spdxDocument,
 332            nameof(spdxDocument.DataLicense), spdxDocument.DataLicense);
 333        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, spdxDocument, nameof(spdxDocument.Name),
 334            spdxDocument.Name);
 335        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, spdxDocument,
 336            nameof(spdxDocument.NamespaceMap), (IList)spdxDocument.NamespaceMap);
 337        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, spdxDocument,
 338            nameof(spdxDocument.VerifiedUsing), (IList)spdxDocument.VerifiedUsing);
 339        CheckAtLeastOneMemberOfType(LiteDomainComplianceFindingType.problem, spdxDocument, nameof(spdxDocument.Element),
 340            (IList)spdxDocument.Element, typeof(Sbom));
 341        CheckContainsOnlyType(LiteDomainComplianceFindingType.recommendation, spdxDocument,
 342            nameof(spdxDocument.Element), (IList)spdxDocument.Element, typeof(Sbom));
 343    }
 44
 45    public void Visit(CreationInfo creationInfo)
 46    {
 347        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, creationInfo, nameof(creationInfo.Created),
 348            creationInfo.Created);
 349        CheckMatchesPattern(LiteDomainComplianceFindingType.problem, creationInfo, nameof(creationInfo.SpecVersion),
 350            creationInfo.SpecVersion, @"^3\.0\.\d+$");
 351        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, creationInfo, nameof(creationInfo.CreatedBy),
 352            (IList)creationInfo.CreatedBy);
 353        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, creationInfo, nameof(creationInfo.Comment),
 354            creationInfo.Comment);
 355    }
 56
 57    public void Visit(Agent agent)
 58    {
 559        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, agent, nameof(agent.CreationInfo),
 560            agent.CreationInfo);
 561        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, agent, nameof(agent.SpdxId), agent.SpdxId);
 562        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, agent, nameof(agent.Name), agent.Name);
 563        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, agent, nameof(agent.ExternalIdentifier),
 564            (IList)agent.ExternalIdentifier);
 565    }
 66
 67    public void Visit(ExternalIdentifier externalIdentifier)
 68    {
 269        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, externalIdentifier,
 270            nameof(externalIdentifier.ExternalIdentifierType), externalIdentifier.ExternalIdentifierType);
 271        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, externalIdentifier,
 272            nameof(externalIdentifier.Identifier), externalIdentifier.Identifier);
 273    }
 74
 75    public void Visit(Hash hash)
 76    {
 377        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, hash, nameof(hash.Algorithm), hash.Algorithm);
 378        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, hash, nameof(hash.HashValue), hash.HashValue);
 379        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, hash, nameof(hash.Comment), hash.Comment);
 380    }
 81
 82    public void Visit(NamespaceMap namespaceMap)
 83    {
 184        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, namespaceMap, nameof(namespaceMap.Prefix),
 185            namespaceMap.Prefix);
 186        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, namespaceMap, nameof(namespaceMap.Namespace),
 187            namespaceMap.Namespace);
 188    }
 89
 90    public void Visit(Relationship relationship)
 91    {
 492        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, relationship, nameof(relationship.CreationInfo),
 493            relationship.CreationInfo);
 494        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, relationship, nameof(relationship.From),
 495            relationship.From);
 496        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, relationship,
 497            nameof(relationship.RelationshipType), relationship.RelationshipType);
 498        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, relationship, nameof(relationship.SpdxId),
 499            relationship.SpdxId);
 4100        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, relationship, nameof(relationship.To),
 4101            relationship.To);
 4102    }
 103
 104    public void Visit(LicenseExpression licenseExpression)
 105    {
 3106        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, licenseExpression,
 3107            nameof(licenseExpression.CreationInfo), licenseExpression.CreationInfo);
 3108        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, licenseExpression,
 3109            nameof(licenseExpression.LicenseExpressionText), licenseExpression.LicenseExpressionText);
 3110        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, licenseExpression,
 3111            nameof(licenseExpression.SpdxId), licenseExpression.SpdxId);
 3112        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, licenseExpression,
 3113            nameof(licenseExpression.LicenseListVersion), licenseExpression.LicenseListVersion);
 3114    }
 115
 116    public void Visit(SimpleLicensingText simpleLicensingText)
 117    {
 0118        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, simpleLicensingText,
 0119            nameof(simpleLicensingText.CreationInfo), simpleLicensingText.CreationInfo);
 0120        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, simpleLicensingText,
 0121            nameof(simpleLicensingText.LicenseText), simpleLicensingText.LicenseText);
 0122        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, simpleLicensingText,
 0123            nameof(simpleLicensingText.SpdxId), simpleLicensingText.SpdxId);
 0124        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, simpleLicensingText,
 0125            nameof(simpleLicensingText.Comment), simpleLicensingText.Comment);
 0126    }
 127
 128    public void Visit(Package package)
 129    {
 2130        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, package, nameof(package.CopyrightText),
 2131            package.CopyrightText);
 2132        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, package, nameof(package.CreationInfo),
 2133            package.CreationInfo);
 2134        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, package, nameof(package.Name), package.Name);
 2135        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, package, nameof(package.PackageVersion),
 2136            package.PackageVersion);
 2137        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, package, nameof(package.SpdxId), package.SpdxId);
 2138        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, package, nameof(package.SuppliedBy),
 2139            package.SuppliedBy);
 140
 2141        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.AttributionText),
 2142            (IList)package.AttributionText);
 2143        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.BuiltTime),
 2144            package.BuiltTime);
 2145        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.Comment),
 2146            package.Comment);
 2147        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.DownloadLocation),
 2148            package.DownloadLocation);
 2149        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.HomePage),
 2150            package.HomePage);
 2151        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.ReleaseTime),
 2152            package.ReleaseTime);
 2153        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.SupportLevel),
 2154            (IList)package.SupportLevel);
 2155        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.ValidUntilTime),
 2156            package.ValidUntilTime);
 2157        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, package, nameof(package.VerifiedUsing),
 2158            (IList)package.VerifiedUsing);
 2159        CheckContainsOnlyType(LiteDomainComplianceFindingType.recommendation, package, nameof(package.VerifiedUsing),
 2160            (IList)package.VerifiedUsing, typeof(Hash));
 161
 2162        if (string.IsNullOrWhiteSpace(package.DownloadLocation) &&
 2163            (package.PackageUrl is null || string.IsNullOrWhiteSpace(package.PackageUrl.ToString())))
 164        {
 0165            Findings.Add(new LiteDomainComplianceFinding(LiteDomainComplianceFindingType.problem, package,
 0166                $"Either {nameof(package.DownloadLocation)} or {nameof(package.PackageUrl)} is required"));
 167        }
 168
 2169        if (package.Catalog is null)
 170        {
 0171            throw new Spdx3Exception($"Cannot find catalog from Package '{package.SpdxId}'");
 172        }
 173
 2174        MustHaveExactlyOneRelationshipOfType(package, RelationshipType.hasDeclaredLicense);
 2175        MustHaveExactlyOneRelationshipOfType(package, RelationshipType.hasConcludedLicense);
 2176    }
 177
 178    private void MustHaveExactlyOneRelationshipOfType(Package package, RelationshipType relType)
 179    {
 4180        var relationshipsOfType = package.Catalog.GetRelationshipsOfType(relType);
 181
 4182        if (relationshipsOfType.Count() != 1)
 183        {
 0184            Findings.Add(new LiteDomainComplianceFinding(LiteDomainComplianceFindingType.problem, package,
 0185                $"Must have exactly one relationship from this package of type '{relType}'"));
 186        }
 187        else
 188        {
 4189            if (relationshipsOfType.First().To.Count() != 1)
 190            {
 0191                Findings.Add(new LiteDomainComplianceFinding(LiteDomainComplianceFindingType.problem, package,
 0192                    $"{nameof(Relationship)} from {nameof(Package)} of type '{relType}' " +
 0193                    $"must point to exactly one {nameof(AnyLicenseInfo)} object"));
 194            }
 195            else
 196            {
 4197                if (!relationshipsOfType.First().To.First().GetType().IsAssignableTo(typeof(AnyLicenseInfo)))
 198                {
 0199                    Findings.Add(new LiteDomainComplianceFinding(LiteDomainComplianceFindingType.problem, package,
 0200                        $"{nameof(Relationship)} from {nameof(Package)} of type '{RelationshipType.hasConcludedLicense}'
 0201                        $"is not pointing to an {nameof(AnyLicenseInfo)} object"));
 202                }
 203            }
 204        }
 4205    }
 206
 207    public void Visit(Sbom sbom)
 208    {
 2209        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, sbom, nameof(sbom.CreationInfo),
 2210            sbom.CreationInfo);
 2211        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, sbom, nameof(sbom.SpdxId), sbom.SpdxId);
 2212        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, sbom, nameof(sbom.Element), (IList)sbom.Element);
 2213        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.problem, sbom, nameof(sbom.RootElement),
 2214            (IList)sbom.RootElement);
 2215        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, sbom, nameof(sbom.Comment), sbom.Comment);
 2216        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, sbom, nameof(sbom.Name), sbom.Name);
 2217        CheckNotNullOrEmpty(LiteDomainComplianceFindingType.recommendation, sbom, nameof(sbom.VerifiedUsing),
 2218            (IList)sbom.VerifiedUsing);
 2219        CheckAtLeastOneMemberOfType(LiteDomainComplianceFindingType.problem, sbom, nameof(sbom.Element),
 2220            (IList)sbom.Element, typeof(Package));
 2221        CheckContainsOnlyType(LiteDomainComplianceFindingType.recommendation, sbom, nameof(sbom.Element),
 2222            (IList)sbom.Element, typeof(Package));
 2223    }
 224
 225    private void CheckNotNullOrEmpty(LiteDomainComplianceFindingType findingType, BaseModelClass obj,
 226        string propertyName, IList? listVal)
 227    {
 32228        var verb = findingType == LiteDomainComplianceFindingType.problem ? "requires" : "should have";
 229
 32230        if (listVal is null || listVal.Count < 1)
 231        {
 13232            Findings.Add(new LiteDomainComplianceFinding(findingType, obj, propertyName,
 13233                $"List {verb} at least one item"));
 234        }
 32235    }
 236
 237
 238    private void CheckAtLeastOneMemberOfType(LiteDomainComplianceFindingType findingType, BaseModelClass obj,
 239        string propertyName, IList? listVal, Type requiredType)
 240    {
 5241        var verb = findingType == LiteDomainComplianceFindingType.problem ? "requires" : "should have";
 242
 5243        if (listVal is null || listVal.Count < 1)
 244        {
 1245            Findings.Add(new LiteDomainComplianceFinding(findingType, obj, propertyName,
 1246                $"List {verb} at least one item of type {requiredType.Name}"));
 1247            return;
 248        }
 249
 12250        foreach (var member in listVal)
 251        {
 4252            if (member.GetType() == requiredType)
 253            {
 4254                return;
 255            }
 256        }
 257
 0258        Findings.Add(new LiteDomainComplianceFinding(findingType, obj, propertyName,
 0259            $"List {verb} at least one item of type {requiredType.Name}"));
 4260    }
 261
 262    private void CheckContainsOnlyType(LiteDomainComplianceFindingType findingType, BaseModelClass obj,
 263        string propertyName, IList? listVal, Type desiredType)
 264    {
 7265        var verb = findingType == LiteDomainComplianceFindingType.problem ? "must" : "should";
 266
 7267        if (listVal is null)
 268        {
 0269            return;
 270        }
 271
 24272        foreach (var member in listVal)
 273        {
 5274            if (member.GetType() != desiredType)
 275            {
 0276                Findings.Add(new LiteDomainComplianceFinding(findingType, obj, propertyName,
 0277                    $"List {verb} contain only items of type {desiredType.Name}"));
 0278                return;
 279            }
 280        }
 7281    }
 282
 283    private void CheckMatchesPattern(LiteDomainComplianceFindingType findingType, BaseModelClass obj,
 284        string propertyName, string? strVal, string regexPattern)
 285    {
 3286        if (strVal is not null && !Regex.Match(strVal, regexPattern).Success)
 287        {
 0288            Findings.Add(
 0289                new LiteDomainComplianceFinding(findingType, obj, propertyName, $"Value '{strVal}' is invalid"));
 290        }
 3291    }
 292
 293    private void CheckNotNullOrEmpty(LiteDomainComplianceFindingType findingType, BaseModelClass obj,
 294        string propertyName, object? objVal)
 295    {
 93296        var verb = findingType == LiteDomainComplianceFindingType.problem ? "required" : "recommended";
 297
 93298        if (objVal is null)
 299        {
 16300            Findings.Add(new LiteDomainComplianceFinding(findingType, obj, propertyName, $"Value {verb}"));
 301        }
 93302    }
 303
 304    private void CheckNotNullOrEmpty(LiteDomainComplianceFindingType findingType, BaseModelClass obj, string fieldName,
 305        Uri? uriVal)
 306    {
 22307        if (uriVal is null)
 308        {
 1309            var verb = findingType == LiteDomainComplianceFindingType.problem ? "required" : "recommended";
 1310            Findings.Add(new LiteDomainComplianceFinding(findingType, obj, fieldName, $"Value {verb}"));
 1311            return;
 312        }
 313
 21314        if (!uriVal.IsWellFormedOriginalString())
 315        {
 0316            Findings.Add(new LiteDomainComplianceFinding(findingType, obj, fieldName,
 0317                $"Value of '{uriVal.ToString()}' is not a well-formed URI"));
 318        }
 21319    }
 320}