Skip to content

Commit 8d11a80

Browse files
[Improve] Code Coverage
1 parent eb66a73 commit 8d11a80

File tree

4 files changed

+201
-3
lines changed

4 files changed

+201
-3
lines changed

ReqIFSharp.Tests/AttributeValueTests/AttributeValueXHTMLTestFixture.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,20 @@ public void Verify_that_raw_text_can_be_extracted_from_xtml_value()
150150

151151
Assert.That(unformattedText, Is.EqualTo("Description of the SpecObject that includes formatted tables and/or style: Element 1 Element 2"));
152152
}
153+
154+
[Test]
155+
public void AttributeValueXhtml_ExtractsPlainTextAndDetectsExternalObjects()
156+
{
157+
var attributeValue = new AttributeValueXHTML();
158+
attributeValue.TheValue = "<div>plain <b>text</b><object data=\"file%20name.bin\" type=\"application/octet-stream\"></object></div>";
159+
160+
var plain = attributeValue.ExtractUnformattedTextFromValue();
161+
162+
Assert.That(plain, Is.EqualTo("plain text"));
163+
164+
var objects = attributeValue.ExternalObjects;
165+
Assert.That(objects, Has.Count.EqualTo(1));
166+
Assert.That(objects[0].Uri, Is.EqualTo("file name.bin"));
167+
}
153168
}
154169
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// <copyright file="ReqIFTextFixture.cs" company="Starion Group S.A.">
3+
//
4+
// Copyright 2017-2025 Starion Group S.A.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// </copyright>
19+
// -------------------------------------------------------------------------------------------------
20+
21+
namespace ReqIFSharp.Tests
22+
{
23+
using System;
24+
using System.IO;
25+
using System.Text;
26+
using System.Threading;
27+
using System.Threading.Tasks;
28+
using System.Xml;
29+
30+
using NUnit.Framework;
31+
32+
using ReqIFSharp;
33+
34+
[TestFixture]
35+
public class ReqIFTextFixture
36+
{
37+
[Test]
38+
public async Task ReqIF_ReadXmlAsync_RestoresHeaderAndToolExtensions()
39+
{
40+
var reqIf = CreateReqIfDocument();
41+
42+
var builder = new StringBuilder();
43+
using (var writer = XmlWriter.Create(builder, new XmlWriterSettings { OmitXmlDeclaration = true }))
44+
{
45+
writer.WriteStartElement("REQ-IF");
46+
reqIf.WriteXml(writer);
47+
writer.WriteEndElement();
48+
}
49+
50+
using var reader = XmlReader.Create(new StringReader(builder.ToString()), new XmlReaderSettings { Async = true });
51+
await reader.MoveToContentAsync();
52+
53+
var roundTrip = new ReqIF();
54+
await roundTrip.ReadXmlAsync(reader, CancellationToken.None);
55+
56+
Assert.Multiple(() =>
57+
{
58+
Assert.That(roundTrip.Lang, Is.EqualTo(reqIf.Lang));
59+
Assert.That(roundTrip.TheHeader.Title, Is.EqualTo(reqIf.TheHeader.Title));
60+
Assert.That(roundTrip.ToolExtension, Has.Count.EqualTo(1));
61+
Assert.That(roundTrip.CoreContent.DataTypes.Count, Is.EqualTo(2));
62+
});
63+
}
64+
65+
private static ReqIF CreateReqIfDocument()
66+
{
67+
var reqIf = new ReqIF
68+
{
69+
Lang = "en",
70+
TheHeader = new ReqIFHeader
71+
{
72+
Identifier = "header",
73+
Comment = "comment",
74+
CreationTime = new DateTime(2024, 01, 01, 12, 00, 00, DateTimeKind.Utc),
75+
RepositoryId = "repo",
76+
ReqIFToolId = "tool",
77+
ReqIFVersion = "1.0",
78+
SourceToolId = "source",
79+
Title = "title"
80+
},
81+
CoreContent = new ReqIFContent()
82+
};
83+
84+
var enumeration = new DatatypeDefinitionEnumeration(reqIf.CoreContent, null)
85+
{
86+
Identifier = "enum"
87+
};
88+
89+
var enumValue = new EnumValue(enumeration, null)
90+
{
91+
Identifier = "enum-value"
92+
};
93+
94+
_ = new EmbeddedValue(enumValue, null)
95+
{
96+
Key = 5,
97+
OtherContent = "red"
98+
};
99+
100+
var xhtmlDatatype = new DatatypeDefinitionXHTML(reqIf.CoreContent, null)
101+
{
102+
Identifier = "xhtml"
103+
};
104+
105+
reqIf.ToolExtension.Add(new ReqIFToolExtension { InnerXml = "<extension />" });
106+
107+
return reqIf;
108+
}
109+
}
110+
}

ReqIFSharp.Tests/SpecElementWithAttributesTests/SpecHierarchyTestFixture.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace ReqIFSharp.Tests.SpecElementWithAttributesTests
2323
using System.IO;
2424
using System.Runtime.Serialization;
2525
using System.Threading;
26+
using System.Threading.Tasks;
2627
using System.Xml;
2728

2829
using NUnit.Framework;
@@ -80,5 +81,47 @@ public void Verify_that_When_Object_is_null_WriteXmlAsync_throws_exception()
8081
Throws.Exception.TypeOf<SerializationException>()
8182
.With.Message.Contains("The Object property of SpecHierarchy identifier:longname may not be null"));
8283
}
84+
85+
[Test]
86+
public async Task SpecHierarchy_ReadXmlAsync_InitialisesContainerAndChildren()
87+
{
88+
var content = new ReqIFContent();
89+
var rootSpecification = new Specification(content, null) { Identifier = "root" };
90+
var existingObject = new SpecObject(content, null) { Identifier = "existing-object" };
91+
92+
var specHierarchy = new SpecHierarchy(rootSpecification, content, null)
93+
{
94+
Identifier = "hierarchy"
95+
};
96+
97+
var xml = """
98+
<SPEC-HIERARCHY IDENTIFIER="hierarchy" IS-TABLE-INTERNAL="true">
99+
<OBJECT>
100+
<SPEC-OBJECT-REF>missing-object</SPEC-OBJECT-REF>
101+
</OBJECT>
102+
<CHILDREN>
103+
<SPEC-HIERARCHY IDENTIFIER="child">
104+
<OBJECT>
105+
<SPEC-OBJECT-REF>existing-object</SPEC-OBJECT-REF>
106+
</OBJECT>
107+
</SPEC-HIERARCHY>
108+
</CHILDREN>
109+
</SPEC-HIERARCHY>
110+
""";
111+
112+
using var reader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { Async = true });
113+
await reader.MoveToContentAsync();
114+
115+
await specHierarchy.ReadXmlAsync(reader, CancellationToken.None);
116+
117+
Assert.Multiple(() =>
118+
{
119+
Assert.That(specHierarchy.IsTableInternal, Is.True);
120+
Assert.That(specHierarchy.Object, Is.Null, "Missing objects should yield null reference");
121+
Assert.That(specHierarchy.Children, Has.Count.EqualTo(1));
122+
Assert.That(specHierarchy.Children[0].Object, Is.SameAs(existingObject));
123+
Assert.That(specHierarchy.Children[0].Container, Is.SameAs(specHierarchy));
124+
});
125+
}
83126
}
84127
}

ReqIFSharp/SpecElementWithAttributes/SpecHierarchy.cs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@
2020

2121
namespace ReqIFSharp
2222
{
23+
using Microsoft.Extensions.Logging;
24+
using Microsoft.Extensions.Logging.Abstractions;
25+
using System;
2326
using System.Collections.Generic;
2427
using System.Linq;
2528
using System.Runtime.Serialization;
2629
using System.Threading;
2730
using System.Threading.Tasks;
2831
using System.Xml;
2932

30-
using Microsoft.Extensions.Logging;
31-
using Microsoft.Extensions.Logging.Abstractions;
32-
3333
/// <summary>
3434
/// The <see cref="SpecHierarchy"/> class represents a node in a hierarchically structured requirements specification.
3535
/// </summary>
@@ -172,6 +172,8 @@ internal override void ReadXml(XmlReader reader)
172172
{
173173
base.ReadXml(reader);
174174

175+
this.ReadXmlAttributes(reader);
176+
175177
using (var specHierarchySubtree = reader.ReadSubtree())
176178
{
177179
while (specHierarchySubtree.Read())
@@ -221,6 +223,8 @@ internal async Task ReadXmlAsync(XmlReader reader, CancellationToken token)
221223
{
222224
base.ReadXml(reader);
223225

226+
this.ReadXmlAttributes(reader);
227+
224228
while (await reader.ReadAsync())
225229
{
226230
if (token.IsCancellationRequested)
@@ -259,6 +263,32 @@ internal async Task ReadXmlAsync(XmlReader reader, CancellationToken token)
259263
}
260264
}
261265

266+
/// <summary>
267+
/// Reads the properties that are defined as XML Attributes (MAX-LENGTH)
268+
/// </summary>
269+
/// <param name="reader">
270+
/// an instance of <see cref="XmlReader"/>
271+
/// </param>
272+
private void ReadXmlAttributes(XmlReader reader)
273+
{
274+
var xmlLineInfo = reader as IXmlLineInfo;
275+
276+
this.logger.LogTrace("reading IS-TABLE-INTERNAL at line:position {LineNumber}:{LinePosition}", xmlLineInfo?.LineNumber, xmlLineInfo?.LinePosition);
277+
278+
var isTableInternal = reader.GetAttribute("IS-TABLE-INTERNAL");
279+
if (!string.IsNullOrWhiteSpace(isTableInternal))
280+
{
281+
try
282+
{
283+
this.IsTableInternal = XmlConvert.ToBoolean(isTableInternal);
284+
}
285+
catch (Exception e)
286+
{
287+
throw new SerializationException($"The SpecHierarchy.IS-TABLE-INTERNAL: {isTableInternal} at line:position {xmlLineInfo?.LineNumber}:{xmlLineInfo?.LinePosition} could not be converted to an INTEGER", e);
288+
}
289+
}
290+
}
291+
262292
/// <summary>
263293
/// Converts a <see cref="AttributeDefinition"/> object into its XML representation.
264294
/// </summary>

0 commit comments

Comments
 (0)