Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,46 @@ public void VerifyThatBuiltInRulesCanBeExecutedAndMessageBusMessagesAreReceived(
Assert.AreEqual(2, messageReceivedCounter);
}

[Test]
public void VerifyThatBuiltInRuleVerificationStatusIsSetToFailedAndPersistedWhenViolationsAreFound()
{
OperationContainer writtenOperationContainer = null;

this.session.Setup(s => s.Write(It.IsAny<OperationContainer>()))
.Returns(Task.CompletedTask)
.Callback<OperationContainer>(oc => writtenOperationContainer = oc);

var service = new RuleVerificationService(this.builtInRules);

var ruleVerificationList = new RuleVerificationList(Guid.NewGuid(), this.cache, this.uri);
this.iteration.RuleVerificationList.Add(ruleVerificationList);

var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), this.cache, this.uri)
{
Name = this.builtInRuleName,
IsActive = true
};

ruleVerificationList.RuleVerification.Add(builtInRuleVerification);

Assert.AreEqual(RuleVerificationStatusKind.NONE, builtInRuleVerification.Status);

service.Execute(this.session.Object, ruleVerificationList);

Assert.IsTrue(builtInRuleVerification.Violation.Any());
Assert.AreEqual(RuleVerificationStatusKind.FAILED, builtInRuleVerification.Status);

// the status must be part of the write transaction, otherwise the data-source round-trip resets it to its stored value
Assert.IsNotNull(writtenOperationContainer);

var writtenVerification = writtenOperationContainer.Operations
.Select(operation => operation.ModifiedThing)
.OfType<CDP4Common.DTO.BuiltInRuleVerification>()
.Single();

Assert.AreEqual(RuleVerificationStatusKind.FAILED, writtenVerification.Status);
}

[Test]
public async Task VerifyThatUserRuleVerificationCanBeExecutedAndMessageBusMessagesAreReceived()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace CDP4Composition.Services
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;

Expand Down Expand Up @@ -151,7 +152,7 @@ public async Task Execute(ISession session, RuleVerificationList verificationLis

if (builtInRuleVerification != null && builtInRuleVerification.IsActive)
{
this.Execute(session, builtInRuleVerification, verificationList);
await this.ExecuteAsync(session, builtInRuleVerification, verificationList);
}

var userRuleVerification = ruleVerification as UserRuleVerification;
Expand All @@ -175,7 +176,7 @@ public async Task Execute(ISession session, RuleVerificationList verificationLis
/// <param name="container">
/// The container <see cref="RuleVerificationList"/> of the <paramref name="userRuleVerification"/>
/// </param>
private void Execute(ISession session, BuiltInRuleVerification builtInRuleVerification, RuleVerificationList container)
private async Task ExecuteAsync(ISession session, BuiltInRuleVerification builtInRuleVerification, RuleVerificationList container)
{
var iteration = (Iteration)container.Container;

Expand All @@ -200,9 +201,11 @@ private void Execute(ISession session, BuiltInRuleVerification builtInRuleVerifi
return;
}

var violations = builtInRule.Verify(iteration);
var violations = builtInRule.Verify(iteration).ToList();

this.UpdateExecutedOn(session, builtInRuleVerification);
var status = violations.Any() ? RuleVerificationStatusKind.FAILED : RuleVerificationStatusKind.PASSED;

await this.UpdateExecutedOnAsync(session, builtInRuleVerification, status);

builtInRuleVerification.Violation.AddRange(violations);

Expand Down Expand Up @@ -242,10 +245,10 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi
}

userRuleVerification.Violation.Clear();
userRuleVerification.Status = RuleVerificationStatusKind.PASSED;

session.CDPMessageBus.SendObjectChangeEvent(userRuleVerification, EventKind.Updated);

var status = RuleVerificationStatusKind.PASSED;
IEnumerable<RuleViolation> violations = null;

switch (userRuleVerification.Rule.ClassKind)
Expand Down Expand Up @@ -274,12 +277,14 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi

if (violations is not null)
{
userRuleVerification.Status = RuleVerificationStatusKind.FAILED;
var violationList = violations.ToList();

status = violationList.Any() ? RuleVerificationStatusKind.FAILED : RuleVerificationStatusKind.PASSED;

IDisposable subscription = null;

//Listen for changes to the verification rule that will happen after UpdateExecutedOn in order to get the updated version.
//The violations must be added lastly as they are not persistent
//Listen for changes to the verification rule that will happen after UpdateExecutedOnAsync in order to get the updated version.
//The violations must be added lastly as they are not persistent
subscription = session.CDPMessageBus.Listen<ObjectChangedEvent>(userRuleVerification)
.Where(objectChange => objectChange.EventKind == EventKind.Updated)
.ObserveOn(RxApp.MainThreadScheduler)
Expand All @@ -290,31 +295,39 @@ private async Task Execute(ISession session, UserRuleVerification userRuleVerifi
subscription.Dispose();

var verification = updated.ChangedThing as UserRuleVerification;
verification.Violation.AddRange(violations);
verification.Violation.AddRange(violationList);

session.CDPMessageBus.SendObjectChangeEvent(verification, EventKind.Updated);

foreach (var ruleViolation in violations)
foreach (var ruleViolation in violationList)
{
session.CDPMessageBus.SendObjectChangeEvent(ruleViolation, EventKind.Added);
}
});
}

await this.UpdateExecutedOn(session, userRuleVerification);
await this.UpdateExecutedOnAsync(session, userRuleVerification, status);
}

/// <summary>
/// Updates the Executed On property of the <paramref name="ruleVerification"/> in the data-source
/// Updates the <see cref="RuleVerification.ExecutedOn"/> and <see cref="RuleVerification.Status"/> properties of
/// the <paramref name="ruleVerification"/> in the data-source.
/// </summary>
/// <param name="session">
/// The <see cref="ISession"/> instance used to update the contained <see cref="RuleVerification"/> instances on the data-source.
/// </param>
/// <param name="ruleVerification">
/// The <see cref="RuleVerification"/> that is to be updated.
/// </param>
/// <param name="status">
/// The <see cref="RuleVerificationStatusKind"/> that reflects the outcome of the verification and that is to be persisted.
/// </param>
/// <returns>An awaitable <see cref="Task"/></returns>
private async Task UpdateExecutedOn(ISession session, RuleVerification ruleVerification)
/// <remarks>
/// The <paramref name="status"/> is set on the clone so that it is part of the write transaction. If it were only
/// set on the cached instance, the data-source round-trip would echo the previously stored status and reset it.
/// </remarks>
private async Task UpdateExecutedOnAsync(ISession session, RuleVerification ruleVerification, RuleVerificationStatusKind status)
{
try
{
Expand All @@ -323,8 +336,12 @@ private async Task UpdateExecutedOn(ISession session, RuleVerification ruleVerif
var transactionContext = TransactionContextResolver.ResolveContext(ruleVerification);
var transaction = new ThingTransaction(transactionContext, clone);
clone.ExecutedOn = DateTime.UtcNow;
clone.Status = status;

var operationContainer = transaction.FinalizeTransaction();

ruleVerification.Status = status;

await session.Write(operationContainer);
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,5 +395,64 @@ public void VerifyThatViolationAreAddedRemoved()
this.messageBus.SendObjectChangeEvent(userRuleVerification, EventKind.Updated);
Assert.IsEmpty(userRow.ContainedRows);
}

[Test]
public void VerifyThatViolatingThingsAreResolvedToRowsWithNamesAndShortNames()
{
var elementDefinition = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri)
{
Name = "Battery",
ShortName = "bat",
Owner = this.domain
};

this.cache.TryAdd(new CacheKey(elementDefinition.Iid, this.iteration.Iid), new Lazy<Thing>(() => elementDefinition));

var ruleVerificationList = new RuleVerificationList(Guid.NewGuid(), this.cache, this.uri)
{
Owner = this.domain
};

this.iteration.RuleVerificationList.Add(ruleVerificationList);

var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), this.cache, this.uri)
{
Name = "BuiltIn",
Status = RuleVerificationStatusKind.INCONCLUSIVE,
IsActive = true
};

ruleVerificationList.RuleVerification.Add(builtInRuleVerification);

var listRowViewModel = new RuleVerificationListRowViewModel(ruleVerificationList, this.session.Object, null);

var builtInRow = listRowViewModel.ContainedRows.Single(x => x.Thing == builtInRuleVerification);

var violation = new RuleViolation(Guid.NewGuid(), this.cache, this.uri)
{
Description = "The Element Definition does not contain the required parameters."
};

violation.ViolatingThing.Add(elementDefinition.Iid);

builtInRuleVerification.Violation.Add(violation);
this.revision.SetValue(builtInRuleVerification, 10);
this.messageBus.SendObjectChangeEvent(builtInRuleVerification, EventKind.Updated);

var violationRow = builtInRow.ContainedRows.OfType<RuleViolationRowViewModel>().Single();
Assert.AreEqual(violation.Description, violationRow.Tooltip);

var violatingThingRow = violationRow.ContainedRows.OfType<ViolatingThingRowViewModel>().Single();
Assert.AreEqual(elementDefinition, violatingThingRow.Thing);
Assert.AreEqual("Battery", violatingThingRow.Name);
Assert.AreEqual("bat", violatingThingRow.ShortName);
Assert.AreEqual(this.domain.ShortName, violatingThingRow.OwnerName);

// the violation row (and its resolved violating thing) is removed when the violation is cleared
builtInRuleVerification.Violation.Clear();
this.revision.SetValue(builtInRuleVerification, 20);
this.messageBus.SendObjectChangeEvent(builtInRuleVerification, EventKind.Updated);
Assert.IsEmpty(builtInRow.ContainedRows);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace CDP4EngineeringModel.Tests.ViewModels.RuleVerificationListBrowser
using CDP4Common.Types;

using CDP4Composition.DragDrop;
using CDP4Composition.Events;
using CDP4Composition.Navigation;
using CDP4Composition.Navigation.Interfaces;
using CDP4Composition.Services;
Expand Down Expand Up @@ -392,5 +393,51 @@ public async Task VerifyThatIfRuleVerificationListIsSelecedTheRulesCanBeVerified

this.ruleVerificationService.Verify(x => x.Execute(this.session.Object, ruleVerificationList));
}

[Test]
public async Task VerifyThatHighlightCommandHighlightsAllViolatingThingsBelowSelectedRow()
{
var elementDefinition1 = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri) { Name = "Battery", ShortName = "bat", Owner = this.domain };
var elementDefinition2 = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri) { Name = "Panel", ShortName = "pan", Owner = this.domain };

this.cache.TryAdd(new CacheKey(elementDefinition1.Iid, this.iteration.Iid), new Lazy<Thing>(() => elementDefinition1));
this.cache.TryAdd(new CacheKey(elementDefinition2.Iid, this.iteration.Iid), new Lazy<Thing>(() => elementDefinition2));

var ruleVerificationList = new RuleVerificationList(Guid.NewGuid(), this.cache, this.uri) { Owner = this.domain };
this.iteration.RuleVerificationList.Add(ruleVerificationList);

var builtInRuleVerification = new BuiltInRuleVerification(Guid.NewGuid(), this.cache, this.uri) { Name = "BuiltIn", IsActive = true };
ruleVerificationList.RuleVerification.Add(builtInRuleVerification);

var violation1 = new RuleViolation(Guid.NewGuid(), this.cache, this.uri) { Description = "v1" };
violation1.ViolatingThing.Add(elementDefinition1.Iid);

var violation2 = new RuleViolation(Guid.NewGuid(), this.cache, this.uri) { Description = "v2" };
violation2.ViolatingThing.Add(elementDefinition2.Iid);

var vm = new RuleVerificationListBrowserViewModel(this.iteration, this.participant, this.session.Object, null, null, null, null);

var listRow = vm.RuleVerificationListRowViewModels.Single(x => x.Thing == ruleVerificationList);

builtInRuleVerification.Violation.Add(violation1);
builtInRuleVerification.Violation.Add(violation2);
this.revision.SetValue(builtInRuleVerification, 10);
this.messageBus.SendObjectChangeEvent(builtInRuleVerification, EventKind.Updated);

var highlightedThings = new List<Thing>();
this.messageBus.Listen<HighlightEvent>().Subscribe(x => highlightedThings.Add(x.HighlightedThing));

var highlightedElementUsageDefinitions = new List<ElementDefinition>();
this.messageBus.Listen<ElementUsageHighlightEvent>().Subscribe(x => highlightedElementUsageDefinitions.Add(x.ElementDefinition));

// selecting the parent Rule Verification List row should highlight all violating things below it
vm.SelectedThing = listRow;
await vm.HighlightCommand.Execute();

CollectionAssert.AreEquivalent(new Thing[] { elementDefinition1, elementDefinition2 }, highlightedThings);

// ElementDefinition violating things must also raise an ElementUsageHighlightEvent so the Product Tree highlights their usages
CollectionAssert.AreEquivalent(new[] { elementDefinition1, elementDefinition2 }, highlightedElementUsageDefinitions);
}
}
}
Loading
Loading