From 62f47c9ce861a4f60fa16087eed439266e9d7ee7 Mon Sep 17 00:00:00 2001 From: Claudio Benghi Date: Sun, 8 Oct 2023 00:18:56 +0100 Subject: [PATCH] Addressing issues. - #7: documentation fixed for correct capitalisation of `Pset_` - #8: added test case with regex error and fixed reporting. - #9: Provided new error code and prevented exception. - #11: the code was already ok, but added a test case. --- README.md | 10 +- ids-lib/Audit.cs | 134 ++++++++++-------- ids-lib/AuditHelper.cs | 4 +- ids-lib/ErrorCodes.md | 8 +- ids-lib/IdsSchema/XsNodes/XsPattern.cs | 14 +- ids-lib/LibraryInformation.cs | 2 +- ids-lib/Messages/IdsErrorMessages.cs | 12 +- ids-lib/Messages/IdsToolMessages.cs | 26 +++- ids-lib/SchemaProviders/SchemaProvider.cs | 2 +- ids-lib/ids-lib.csproj | 2 +- ids-tool.tests/AuditTests.cs | 4 +- .../IssueFiles/Issue 08 - Regex pattern.ids | 41 ++++++ .../IssueFiles/Issue 09 - XML structure.ids | 29 ++++ .../IssueFiles/Issue 11 - IfcLogical.ids | 28 ++++ ids-tool.tests/IssueTests.cs | 42 ++++++ ids-tool.tests/ids-tool.tests.csproj | 9 ++ ids-tool/ids-tool.csproj | 2 +- 17 files changed, 283 insertions(+), 86 deletions(-) create mode 100644 ids-tool.tests/IssueFiles/Issue 08 - Regex pattern.ids create mode 100644 ids-tool.tests/IssueFiles/Issue 09 - XML structure.ids create mode 100644 ids-tool.tests/IssueFiles/Issue 11 - IfcLogical.ids create mode 100644 ids-tool.tests/IssueTests.cs diff --git a/README.md b/README.md index 8309f60..b3419d7 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ We are planning to implement a number of audits (the ones with a check-mark are - [x] Multiple values allowed - [x] Properties - [x] Prepare metadata infrastructure - - [x] Prop and PSet names are treated as case sensitive - - [x] no extensions of standard PSets + - [x] Prop and PropertySet names are treated as case sensitive + - [x] no extensions of standard `Psets` - [x] properties are limited to the agreed set - [x] Includes IFC type inheritance - [x] Reserved prefix - - [x] No custom PSet can mandate a property starting with "PSet_" this prefix is reserved for the standard + - [x] No custom PropertySet can mandate a property starting with "Pset_" this prefix is reserved for the standard - [x] SimpleValues - [x] Enumerations - [x] Property Measures @@ -61,7 +61,7 @@ We are planning to implement a number of audits (the ones with a check-mark are - [ ] Discuss the scope of the list, currently only measures, IFCTEXT and IFCLABEL (but it's now datatype, so we should probably expand). - [ ] The attribute is required, but it can be empty, is that valid? - [x] Test cases added - - [x] Constrain IfcMeasure for identified properties in standard PSets + - [x] Constrain datatype for properties identified in standard Psets - [x] Cardinality for facets (in requirements) - [x] partOf - [x] classification @@ -113,7 +113,7 @@ We are planning to implement a number of audits (the ones with a check-mark are - [ ] perhaps just consider this a warning for user-defined property sets - [ ] warning or error as configuration (strict mode?) - [ ] Reserved prefix - - [ ] can the regex match any string starting with "PSet_". + - [ ] can the regex match any string starting with "Pset_". - [ ] we have a working solution to be included, but it may raise the technical complexity for implementers considerably - [ ] Perhaps one for the strict mode? - [ ] Measures diff --git a/ids-lib/Audit.cs b/ids-lib/Audit.cs index 2fc3a28..b99b137 100644 --- a/ids-lib/Audit.cs +++ b/ids-lib/Audit.cs @@ -242,81 +242,91 @@ private static async Task AuditStreamAsync(Stream theStream, AuditHelper IdsXmlNode? current = null; var prevSchemaStatus = auditSettings.SchemaStatus; - while (await reader.ReadAsync()) // the loop reads the entire file to trigger validation events. + try { - cntRead++; - - switch (reader.NodeType) + while (await reader.ReadAsync()) // the loop reads the entire file to trigger validation events. { - // audits are performed on closing the element end, so that all the children are available for evaluation. - // but empty elements (e.g., ) are audited upon opening, as there are no children to evaluate - // - case XmlNodeType.Element: - IdsXmlNode? parent = null; + cntRead++; + + switch (reader.NodeType) + { + // audits are performed on closing the element end, so that all the children are available for evaluation. + // but empty elements (e.g., ) are audited upon opening, as there are no children to evaluate + // + case XmlNodeType.Element: + IdsXmlNode? parent = null; #if NETSTANDARD2_0 if (elementsStack.Count > 0) parent = elementsStack.Peek(); #else - if (elementsStack.TryPeek(out var peeked)) - parent = peeked; + if (elementsStack.TryPeek(out var peeked)) + parent = peeked; #endif - var newContext = IdsXmlHelpers.GetContextFromElement(reader, parent, logger); // this is always not null - if (newContext is IdsSpecification spec) - // parents of IdsSpecification do not retain children for Garbage Collection purposes - // so we need to set the positional index manually - spec.PositionalIndex = iSpecification++; - while (auditSettings.BufferedValidationIssues.Any()) - { - var queuedIssue = auditSettings.BufferedValidationIssues.Dequeue(); - if ( - newContext.type == "attribute" - && - (queuedIssue.Message.Contains("minOccurs") || queuedIssue.Message.Contains("maxOccurs")) - ) + var newContext = IdsXmlHelpers.GetContextFromElement(reader, parent, logger); // this is always not null + if (newContext is IdsSpecification spec) + // parents of IdsSpecification do not retain children for Garbage Collection purposes + // so we need to set the positional index manually + spec.PositionalIndex = iSpecification++; + while (auditSettings.BufferedValidationIssues.Any()) { - // this could fail under some circumstances, but it's a temporary workaround - auditSettings.SchemaStatus = prevSchemaStatus; - continue; - } - queuedIssue.Notify(logger, newContext); - } + var queuedIssue = auditSettings.BufferedValidationIssues.Dequeue(); + if ( + newContext.type == "attribute" + && + (queuedIssue.Message.Contains("minOccurs") || queuedIssue.Message.Contains("maxOccurs")) + ) + { + // this could fail under some circumstances, but it's a temporary workaround + auditSettings.SchemaStatus = prevSchemaStatus; + continue; + } + queuedIssue.Notify(logger, newContext); + } - // we only push on the stack if it's not empty, e.g.: does not go on the stack - if (!reader.IsEmptyElement) - elementsStack.Push(newContext); - else - { - if (!auditSettings.Options.OmitIdsContentAudit) - contentStatus |= newContext.PerformAudit(logger); // invoking audit on empty element happens immediately - } - current = newContext; - break; + // we only push on the stack if it's not empty, e.g.: does not go on the stack + if (!reader.IsEmptyElement) + elementsStack.Push(newContext); + else + { + if (!auditSettings.Options.OmitIdsContentAudit) + contentStatus |= newContext.PerformAudit(logger); // invoking audit on empty element happens immediately + } + current = newContext; + break; - case XmlNodeType.Text: - // Debug.WriteLine($" Text Node: {reader.GetValueAsync().Result}"); - current!.SetContent(reader.GetValueAsync().Result); - break; - case XmlNodeType.EndElement: - // Debug.WriteLine($"End Element {reader.LocalName}"); - var closing = elementsStack.Pop(); - // Debug.WriteLine($" auditing {closing.type} on end element"); - if (!auditSettings.Options.OmitIdsContentAudit) - { - contentStatus |= closing.PerformAudit(logger); // invoking audit on end of element - } - break; - default: - // Debug.WriteLine("Other node {0} with value '{1}'.", reader.NodeType, reader.Value); - break; + case XmlNodeType.Text: + // Debug.WriteLine($" Text Node: {reader.GetValueAsync().Result}"); + current!.SetContent(reader.GetValueAsync().Result); + break; + case XmlNodeType.EndElement: + // Debug.WriteLine($"End Element {reader.LocalName}"); + var closing = elementsStack.Pop(); + // Debug.WriteLine($" auditing {closing.type} on end element"); + if (!auditSettings.Options.OmitIdsContentAudit) + { + contentStatus |= closing.PerformAudit(logger); // invoking audit on end of element + } + break; + default: + // Debug.WriteLine("Other node {0} with value '{1}'.", reader.NodeType, reader.Value); + break; + } + prevSchemaStatus = auditSettings.SchemaStatus; } - prevSchemaStatus = auditSettings.SchemaStatus; + IdsToolMessages.InformReadCount(logger, cntRead); } - - reader.Dispose(); + catch (XmlException ex_xml) + { + contentStatus = contentStatus | IdsToolMessages.Report502XmlSchemaException(logger, ex_xml); + } + catch (Exception ex) + { + contentStatus = contentStatus | IdsToolMessages.Report503Exception(logger, ex); + } + reader.Dispose(); if (!auditSettings.Options.OmitIdsSchemaAudit) rSettings.ValidationEventHandler -= new ValidationEventHandler(auditSettings.ValidationReporter); - IdsToolMessages.ReportReadCount(logger, cntRead); return auditSettings.SchemaStatus | contentStatus; } @@ -348,7 +358,7 @@ private static Status PopulateSchema(Stream vrs, ISchemaProvider schemaProvider, private static Status ProcessSingleFile(FileInfo theFile, IBatchAuditOptions batchOptions, ILogger? logger) { Status ret = Status.Ok; - IdsToolMessages.ReportFileProcessingStarted(logger, theFile.FullName); + IdsToolMessages.InformFileProcessingStarted(logger, theFile.FullName); ret |= AuditIdsComplianceAsync(batchOptions, theFile, logger).Result; return ret; } @@ -369,7 +379,7 @@ private static Status ProcessFolder(DirectoryInfo directoryInfo, IBatchAuditOpti ret |= sgl; tally++; } - IdsToolMessages.ReportFileProcessingEnded(logger, tally); + IdsToolMessages.InformFileProcessingEnded(logger, tally); return ret; } diff --git a/ids-lib/AuditHelper.cs b/ids-lib/AuditHelper.cs index 5d94d94..e087c71 100644 --- a/ids-lib/AuditHelper.cs +++ b/ids-lib/AuditHelper.cs @@ -42,9 +42,9 @@ internal void Notify(ILogger? logger, IdsSchema.IdsXmlNode newContext) loc.StartLinePosition = Position; loc.NodeType = newContext.type; if (Schema == Original.SchemaError) - IdsErrorMessages.ReportSchema306ComplianceError(logger, loc, Message); + IdsErrorMessages.Report306SchemaComplianceError(logger, loc, Message); else - IdsErrorMessages.ReportSchema307ComplianceWarning(logger, Level, loc, Message); + IdsErrorMessages.Report307SchemaComplianceWarning(logger, Level, loc, Message); } } diff --git a/ids-lib/ErrorCodes.md b/ids-lib/ErrorCodes.md index eebf7e6..ba3647e 100644 --- a/ids-lib/ErrorCodes.md +++ b/ids-lib/ErrorCodes.md @@ -1,4 +1,4 @@ -# List of error codes for Ids-Audit-Tool +# List of error codes for Ids-Audit ## Errors @@ -12,6 +12,8 @@ | 105 | No valid option exist | | 106 | Invalid empty attribute | | 107 | Invalid IFC schema identification | +| 108 | Unsupported Ids Schema | +| 109 | Invalid regex | | 200 | Integration issues | | 201 | Incompatible clauses | | 300 | Schema clauses | @@ -26,3 +28,7 @@ | 401 | Reserved keyword | | 500 | Application errors | | 501 | Unexpected scenario | +| 502 | XML Schema Exception | +| 503 | Processing Exception | +| 504 | IDS Schema version not Embedded | + diff --git a/ids-lib/IdsSchema/XsNodes/XsPattern.cs b/ids-lib/IdsSchema/XsNodes/XsPattern.cs index 4198127..7b7f85d 100644 --- a/ids-lib/IdsSchema/XsNodes/XsPattern.cs +++ b/ids-lib/IdsSchema/XsNodes/XsPattern.cs @@ -29,7 +29,17 @@ public Audit.Status DoesMatch(IEnumerable candidateStrings, bool ignoreC : IdsErrorMessages.Report103InvalidListMatcher(this, pattern, logger, listToMatchName, schemaContext, candidateStrings); } - public bool TryMatch(IEnumerable candidateStrings, bool ignoreCase, out IEnumerable matches) + protected internal override Audit.Status PerformAudit(ILogger? logger) + { + var ret = Audit.Status.Ok; + if (!EnsureRegex(out var _, false)) + { + ret = IdsErrorMessages.Report109InvalidRegex(this, pattern, logger); + } + return base.PerformAudit(logger) | ret; + } + + public bool TryMatch(IEnumerable candidateStrings, bool ignoreCase, out IEnumerable matches) { if (!EnsureRegex(out var _, ignoreCase)) { @@ -58,7 +68,7 @@ private bool EnsureRegex(out string errorMessage, bool ignoreCase) return true; try { - var preProcess = XmlRegex.Preprocess(pattern); + var preProcess = XmlRegex.Preprocess(pattern, false); if (!ignoreCase) compiledCaseSensitiveRegex = new Regex(preProcess, RegexOptions.Compiled | RegexOptions.Singleline); else diff --git a/ids-lib/LibraryInformation.cs b/ids-lib/LibraryInformation.cs index 2c0339a..ea51ae6 100644 --- a/ids-lib/LibraryInformation.cs +++ b/ids-lib/LibraryInformation.cs @@ -32,6 +32,6 @@ public static class LibraryInformation /// /// Static field with hardcoded DLL version number. /// - public static string AssemblyVersion => "1.0.44"; + public static string AssemblyVersion => "1.0.45"; } } diff --git a/ids-lib/Messages/IdsErrorMessages.cs b/ids-lib/Messages/IdsErrorMessages.cs index 6edd279..65de6e8 100644 --- a/ids-lib/Messages/IdsErrorMessages.cs +++ b/ids-lib/Messages/IdsErrorMessages.cs @@ -84,6 +84,12 @@ internal static void Report108UnsupportedIdsSchema(ILogger? logger, string versi logger?.LogError("Error {errorCode}: Unsupported schema version '{vers}' on `ids` element, please use 'http://standards.buildingsmart.org/IDS/0.9.6/ids.xsd' instead.", 108, version); } + internal static Audit.Status Report109InvalidRegex(IdsXmlNode locationContext, string pattern, ILogger? logger) + { + logger?.LogError("Error {errorCode}: Ivalid pattern string '{pattern}' on {location}.", 109, pattern, locationContext.GetNodeIdentification()); + return Audit.Status.IdsContentError; + } + internal static Audit.Status Report201IncompatibleClauses(ILogger? logger, IdsXmlNode locationContext, SchemaInfo schemaInfo, string scenarioMessage) { logger?.LogError("Error {errorCode}: Inconsistent clauses for {ifcSchema} : {message} on {location}.", 201, schemaInfo.Version, scenarioMessage, locationContext.GetNodeIdentification()); @@ -131,12 +137,12 @@ internal static Audit.Status Report305BadConstraintValue(ILogger? logger, IdsXml return Audit.Status.IdsContentError; } - internal static void ReportSchema306ComplianceError(ILogger? logger, NodeIdentification location, string message) + internal static void Report306SchemaComplianceError(ILogger? logger, NodeIdentification location, string message) { logger?.LogError("Error {errorCode}: Schema compliance error on {location}; {message}", 306, location, message); } - internal static void ReportSchema307ComplianceWarning(ILogger? logger, LogLevel level, NodeIdentification location, string message) + internal static void Report307SchemaComplianceWarning(ILogger? logger, LogLevel level, NodeIdentification location, string message) { logger?.Log(level, "Error {errorCode}: Schema compliance warning on {location}; {message}", 307, location, message); } @@ -152,4 +158,6 @@ internal static Audit.Status Report501UnexpectedScenario(ILogger? logger, string logger?.LogCritical("Error {errorCode}: Unhandled scenario: {message} on {location}.", 501, scenarioMessage, context.GetNodeIdentification()); return Audit.Status.NotImplementedError; } + + } diff --git a/ids-lib/Messages/IdsToolMessages.cs b/ids-lib/Messages/IdsToolMessages.cs index 91181ac..11f35c2 100644 --- a/ids-lib/Messages/IdsToolMessages.cs +++ b/ids-lib/Messages/IdsToolMessages.cs @@ -1,6 +1,8 @@ using IdsLib.IdsSchema; using IdsLib.IdsSchema.IdsNodes; using Microsoft.Extensions.Logging; +using System; +using System.Xml; using static IdsLib.Audit; namespace IdsLib.Messages @@ -42,17 +44,17 @@ internal static Status ReportInvalidSource(ILogger? logger, string inputSource) return Status.NotFoundError; } - internal static void ReportReadCount(ILogger? logger, int cntRead) + internal static void InformReadCount(ILogger? logger, int cntRead) { - logger?.LogDebug("Completed reading {cntRead} xml elements.", cntRead); + logger?.LogInformation("Completed reading {cntRead} xml elements.", cntRead); } - internal static void ReportFileProcessingStarted(ILogger? logger, string fullName) + internal static void InformFileProcessingStarted(ILogger? logger, string fullName) { logger?.LogInformation("Auditing file: `{filename}`.", fullName); } - internal static void ReportFileProcessingEnded(ILogger? logger, int tally) + internal static void InformFileProcessingEnded(ILogger? logger, int tally) { var fileCardinality = tally != 1 ? "files" : "file"; logger?.LogInformation("{tally} {fileCardinality} processed.", tally, fileCardinality); @@ -88,9 +90,21 @@ internal static Status ReportInvalidXsdSource(ILogger? logger, string diskSchema return Status.XsdSchemaError; } - internal static Status ReportInvalidSchemaVersion(ILogger? logger, IdsVersion vrs) + internal static Status Report502XmlSchemaException(ILogger? logger, XmlException ex_xml) { - logger?.LogError("Embedded schema for version {vrs} not implemented.", vrs); + logger?.LogError("Error {errorCode}: XML Exception. Error message: {}", 502, ex_xml); + return Status.IdsStructureError; + } + + internal static Status Report503Exception(ILogger? logger, Exception ex) + { + logger?.LogError("Error {errorCode}: Generic Exception. Error message: {}", 503, ex); + return Status.UnhandledError; + } + + internal static Status Report504NotImplementedIdsSchemaVersion(ILogger? logger, IdsVersion vrs) + { + logger?.LogError("Error {errorCode}: Embedded schema for version {vrs} not implemented.", 504, vrs); return Status.NotImplementedError; } } diff --git a/ids-lib/SchemaProviders/SchemaProvider.cs b/ids-lib/SchemaProviders/SchemaProvider.cs index ef04e12..8e28060 100644 --- a/ids-lib/SchemaProviders/SchemaProvider.cs +++ b/ids-lib/SchemaProviders/SchemaProvider.cs @@ -29,7 +29,7 @@ protected static Audit.Status GetResourceSchemasByVersion(IdsVersion vrs, ILogge break; case IdsVersion.Invalid: default: - ret |= IdsToolMessages.ReportInvalidSchemaVersion(logger, vrs); + ret |= IdsToolMessages.Report504NotImplementedIdsSchemaVersion(logger, vrs); break; } schemas = tmpResources.Select(x => GetSchema(x)).ToList(); diff --git a/ids-lib/ids-lib.csproj b/ids-lib/ids-lib.csproj index 3bcbf95..3b8e773 100644 --- a/ids-lib/ids-lib.csproj +++ b/ids-lib/ids-lib.csproj @@ -20,7 +20,7 @@ First implementation. README.md - 1.0.44 + 1.0.45 $(AssemblyVersion) $(AssemblyVersion) true diff --git a/ids-tool.tests/AuditTests.cs b/ids-tool.tests/AuditTests.cs index ab696d8..0e2bfe6 100644 --- a/ids-tool.tests/AuditTests.cs +++ b/ids-tool.tests/AuditTests.cs @@ -115,7 +115,7 @@ public static IEnumerable GetInvalidCases() yield return new object[] { "InvalidFiles/InvalidEntityNames.ids", 3, Audit.Status.IdsContentError }; yield return new object[] { "InvalidFiles/InvalidAttributeNames.ids", 2, Audit.Status.IdsContentError }; yield return new object[] { "InvalidFiles/InvalidAttributeForClass.ids", 1, Audit.Status.IdsContentError }; - yield return new object[] { "InvalidFiles/InvalidIfcEntityPattern.ids", 4, Audit.Status.IdsContentError }; + yield return new object[] { "InvalidFiles/InvalidIfcEntityPattern.ids", 5, Audit.Status.IdsContentError }; yield return new object[] { "InvalidFiles/InvalidIfcEntityPredefinedType.ids", 5, Audit.Status.IdsContentError }; yield return new object[] { "InvalidFiles/invalidPropertyMeasures.ids", 3, Audit.Status.IdsContentError }; yield return new object[] { "InvalidFiles/EntityImpossible.ids", 1, Audit.Status.IdsContentError }; @@ -165,7 +165,7 @@ public void FullAuditFailWithStream(string path, int numErr, Audit.Status status { var f = new FileInfo(path); if (!f.Exists) - return; // when a case matchiing error happens in linux, we can exit gracefully + return; // when a case matching error happens in linux, we can exit gracefully var d = f.Directory!; // if the file exists, the directory must also var t = d.GetFiles(Path.ChangeExtension(f.Name, "*")).Single(); diff --git a/ids-tool.tests/IssueFiles/Issue 08 - Regex pattern.ids b/ids-tool.tests/IssueFiles/Issue 08 - Regex pattern.ids new file mode 100644 index 0000000..1880763 --- /dev/null +++ b/ids-tool.tests/IssueFiles/Issue 08 - Regex pattern.ids @@ -0,0 +1,41 @@ + + + Ifc logical is an acceptable datatype in properties + + + + + + + IFCWALL + + + + + + + Foo_Bar + + + + + + + + + + + Foo_Bar + + + + + + + + + + + \ No newline at end of file diff --git a/ids-tool.tests/IssueFiles/Issue 09 - XML structure.ids b/ids-tool.tests/IssueFiles/Issue 09 - XML structure.ids new file mode 100644 index 0000000..4496818 --- /dev/null +++ b/ids-tool.tests/IssueFiles/Issue 09 - XML structure.ids @@ -0,0 +1,29 @@ + + + + Example of property + Claudio Benghi + 2023-04-12 + + + + + + + IFCWALL + + + + + + + Pset_WallCommon + + + AcousticRating + + + + + + diff --git a/ids-tool.tests/IssueFiles/Issue 11 - IfcLogical.ids b/ids-tool.tests/IssueFiles/Issue 11 - IfcLogical.ids new file mode 100644 index 0000000..6d20669 --- /dev/null +++ b/ids-tool.tests/IssueFiles/Issue 11 - IfcLogical.ids @@ -0,0 +1,28 @@ + + + Ifc logical is an acceptable datatype in properties + + + + + + + IFCWALL + + + + + + + Foo_Bar + + + Foo + + + + + + \ No newline at end of file diff --git a/ids-tool.tests/IssueTests.cs b/ids-tool.tests/IssueTests.cs new file mode 100644 index 0000000..6afcd01 --- /dev/null +++ b/ids-tool.tests/IssueTests.cs @@ -0,0 +1,42 @@ +using idsTool.tests.Helpers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace idsTool.tests +{ + public class IssueTests + { + public IssueTests(ITestOutputHelper outputHelper) + { + XunitOutputHelper = outputHelper; + } + private ITestOutputHelper XunitOutputHelper { get; } + + [Fact] + public void Issue08_RegexPattern() + { + var f = new FileInfo("IssueFiles\\Issue 08 - Regex pattern.ids"); + LoggerAndAuditHelpers.FullAudit(f, XunitOutputHelper, IdsLib.Audit.Status.IdsContentError, 1); + } + + [Fact] + public void Issue09_XmlStructure() + { + var f = new FileInfo("IssueFiles\\Issue 09 - XML structure.ids"); + LoggerAndAuditHelpers.FullAudit(f, XunitOutputHelper, IdsLib.Audit.Status.IdsStructureError, 1); + } + + [Fact] + public void Issue11_IfcLogicalIsValidDatatype() + { + var f = new FileInfo("IssueFiles\\Issue 11 - IfcLogical.ids"); + LoggerAndAuditHelpers.FullAudit(f, XunitOutputHelper, IdsLib.Audit.Status.Ok); + } + } +} diff --git a/ids-tool.tests/ids-tool.tests.csproj b/ids-tool.tests/ids-tool.tests.csproj index 21b5835..2ce34b7 100644 --- a/ids-tool.tests/ids-tool.tests.csproj +++ b/ids-tool.tests/ids-tool.tests.csproj @@ -144,9 +144,18 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/ids-tool/ids-tool.csproj b/ids-tool/ids-tool.csproj index 7d83a39..3ad501c 100644 --- a/ids-tool/ids-tool.csproj +++ b/ids-tool/ids-tool.csproj @@ -16,7 +16,7 @@ icon.png IDS, buildingSmart - 1.0.44 + 1.0.45 $(AssemblyVersion) $(AssemblyVersion) https://github.com/buildingSMART/IDS-Audit-tool.git