diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Expected/18-Use Case/18-Use Case.sysml b/SysML2.NET.Serializer.TextualNotation.Tests/Expected/18-Use Case/18-Use Case.sysml
new file mode 100644
index 00000000..8eee106b
--- /dev/null
+++ b/SysML2.NET.Serializer.TextualNotation.Tests/Expected/18-Use Case/18-Use Case.sysml
@@ -0,0 +1,76 @@
+package '18-Use Case' {
+ part def Vehicle;
+ part def Person;
+ part def Environment;
+ part def 'Fuel Station';
+ use case 'provide transportation' {
+ subject vehicle: Vehicle;
+ actor driver: Person;
+ actor passengers: Person[0..4];
+ actor environment: Environment;
+ objective {
+ doc
+ /*
+ * Satisfy mission requirements to transport driver and passengers
+ * from starting location to ending location in conformance with
+ * the driving profile and meet the mission requirements for safety,
+ * reliability, comfort, and affordability.
+ */
+
+ }
+ :>> start {
+ doc
+ /* Mock-up of a pre-condition. */
+ assert constraint { doc /* Vehicle at starting location */ }
+ }
+ first start;
+ then include 'enter vehicle' {
+ subject;
+ actor :>> driver = 'provide transportation'::driver;
+ actor :>> passengers = 'provide transportation'::passengers;
+ }
+ then use case 'drive vehicle' {
+ include 'add fuel'[0..*] {
+ doc
+ /*
+ * Mock-up of an extension point.
+ * (But reference to 'add fuel' is in the wrong direction, and it doesn't
+ * make the extension condition sufficient to trigger the behavior.)
+ */
+
+ subject;
+ actor :>> fueler = driver;
+ :>> start {
+ doc
+ /* Fuel level < 10% max fuel */
+ }
+ }
+ }
+ then include 'exit vehicle' {
+ subject;
+ actor :>> driver = 'provide transportation'::driver;
+ actor :>> passengers = 'provide transportation'::passengers;
+ }
+ then done;
+ :>> done {
+ doc
+ /* Mock-up of a post-condition. */
+ assert constraint { doc /* Vehicle at ending location */ }
+ }
+ }
+ use case 'enter vehicle' {
+ subject vehicle: Vehicle;
+ actor driver: Person;
+ actor passengers: Person[0..4];
+ }
+ use case 'exit vehicle' {
+ subject vehicle: Vehicle;
+ actor driver: Person;
+ actor passengers: Person[0..4];
+ }
+ use case 'add fuel' {
+ subject vehicle: Vehicle;
+ actor fueler: Person;
+ actor 'fuel station': 'Fuel Station';
+ }
+}
diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj b/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj
index 0a0f680b..080d52cc 100644
--- a/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj
+++ b/SysML2.NET.Serializer.TextualNotation.Tests/SysML2.NET.Serializer.TextualNotation.Tests.csproj
@@ -15,6 +15,12 @@
false
true
en-US
+
+ true
+ true
diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs b/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs
index 5ec197dd..76f49c4d 100644
--- a/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs
+++ b/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationValidationTestFixture.cs
@@ -36,6 +36,7 @@ namespace SysML2.NET.Serializer.TextualNotation.Tests.Writers
using SysML2.NET.Serializer.Xmi;
[TestFixture]
+ [Parallelizable(ParallelScope.Children)]
public class TextualNotationValidationTestFixture
{
///
@@ -136,6 +137,7 @@ public void OneTimeTearDown()
[TestCase("15-Properties-Values-Expressions", "15_19a-Materials with Properties.sysmlx")]
[TestCase("17-Sequence Modeling", "17a-Sequence-Modeling.sysmlx")]
[TestCase("17-Sequence Modeling", "17b-Sequence-Modeling.sysmlx")]
+ [TestCase("18-Use Case", "18-Use Case.sysmlx")]
public async Task VerifyValidationTextualNotationXmi(string folderName, string fileName)
{
var loggerFactory = LoggerFactory.Create(builder =>
diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs b/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs
index 0340093c..b5c81676 100644
--- a/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs
+++ b/SysML2.NET.Serializer.TextualNotation/Writers/NameResolutionCache.cs
@@ -2346,7 +2346,7 @@ private void BuildInheritedEntries(IType type, Dictionary QueryImpliedGeneralClosure(IType type, List declaredS
///
/// Indexes, for lookup only, the members an implied general contributes.
///
+ /// The Type that reaches the general through an implied Specialization.
/// The Type reached through an implied Specialization.
/// The destination index.
/// Whether the owning scope is reached through the global namespace.
- private static void AddImpliedLookupEntries(IType impliedGeneral, Dictionary> index, bool isGlobal)
+ ///
+ /// The contribution is filtered through Type::removeRedefinedFeatures, because a member the
+ /// inheriting Type REDEFINES is not one of that Type's Memberships. KerML §8.3.3.1.10 derives
+ /// inheritedMemberships as removeRedefinedFeatures(inheritableMemberships(…)), whose
+ /// second condition rejects a Membership one of whose redefined Features is a directly
+ /// redefinedFeature of an ownedFeature of the Type.
+ /// Without the filter the redefined library member and the local redefinition both land in the
+ /// same simple-name bucket, so the name reads as shadowed and a reference that should be bare is
+ /// emitted fully qualified — first 'provide transportation'::start for first start.
+ /// The declared-supertype path needs no such filter: it reads inheritedMembership, which
+ /// has already applied it. Only this path bypasses that derivation, because an implied Specialization
+ /// is detached and its members have to be gathered directly.
+ ///
+ private static void AddImpliedLookupEntries(IType inheritingType, IType impliedGeneral, Dictionary> index, bool isGlobal)
{
if (impliedGeneral == null)
{
@@ -2500,6 +2514,16 @@ private static void AddImpliedLookupEntries(IType impliedGeneral, Dictionary PassesVisibilityFilter(member, isGlobal)))
{
AddLookupOnlyEntry(index, member);