I found a bug in OpenTap 9.21, and hope it will be fixed in the next release.
I am running two test plans parallelly by calling TestPlan.ExecuteAsync at the same time. Each test plan use SCPIRegexStep to send SCPI commands to a different instrument.
But I got an exception at this line:return membersLookup.AddValue((targetType, sourceType), (result?.ToArray() ?? Array.Empty<(IMemberData, bool)>()));
in the following function:
static (IMemberData, bool hasEnabledAttribute) GetSettingsLookup(TypeData targetType, ITypeData sourceType)
{
if(membersLookup.TryGetValue((targetType, sourceType), out var value))
return value;
…
return membersLookup.AddValue((targetType, sourceType), (result?.ToArray() ?? Array.Empty<(IMemberData, bool)>()));
}
Because after the first task call membersLookup.AddValue, the second task with the same key can not add duplicate key into membersLookup.
the exception call stack is as follows:
2023-10-07 10:11:22.024213 ; TestPlan ; Warning ; TestPlan aborted.
2023-10-07 10:11:22.025078 ; TestPlan ; Error ; An element with the same key but a different value already exists. Key: ‘(OpenTap.IResource, OpenTap.Plugins.BasicSteps.SCPIRegexStep)’
2023-10-07 10:11:22.030716 ; TestPlan ; Debug ; ArgumentException: An element with the same key but a different value already exists. Key: ‘(OpenTap.IResource, OpenTap.Plugins.BasicSteps.SCPIRegexStep)’
2023-10-07 10:11:22.081944 ; TestPlan ; Debug ; at System.Collections.Immutable.ImmutableDictionary2.HashBucket.Add(TKey key, TValue value, IEqualityComparer
1 keyOnlyComparer, IEqualityComparer1 valueComparer, KeyCollisionBehavior behavior, OperationResult& result) 2023-10-07 10:11:22.081952 ; TestPlan ; Debug ; at System.Collections.Immutable.ImmutableDictionary
2.Add(TKey key, TValue value, KeyCollisionBehavior behavior, MutationInput origin)
2023-10-07 10:11:22.081953 ; TestPlan ; Debug ; at System.Collections.Immutable.ImmutableDictionary2.Add(TKey key, TValue value) 2023-10-07 10:11:22.081955 ; TestPlan ; Debug ; at OpenTap.TestStepExtensions.GetSettingsLookup(TypeData targetType, ITypeData sourceType) 2023-10-07 10:11:22.081957 ; TestPlan ; Debug ; at OpenTap.TestStepExtensions.GetObjectSettings[T,T2,T3](T2 item, Boolean onlyEnabled, Func
3 transform, HashSet1 itemSet, TypeData targetType) 2023-10-07 10:11:22.081959 ; TestPlan ; Debug ; at OpenTap.TestStepExtensions.GetObjectSettings[T,T2,T3](IEnumerable
1 objects, Boolean onlyEnabled, Func3 transform, HashSet
1 itemSet)
2023-10-07 10:11:22.081961 ; TestPlan ; Debug ; at OpenTap.ResourceDependencyAnalyzer.getManyResources[T](T steps)
2023-10-07 10:11:22.081963 ; TestPlan ; Debug ; at OpenTap.ResourceDependencyAnalyzer.GetAllResources(Object references, Boolean& errorDetected)
2023-10-07 10:11:22.081964 ; TestPlan ; Debug ; at OpenTap.ResourceManagerUtils.GetResourceNodesNoCache(Object source)
2023-10-07 10:11:22.081966 ; TestPlan ; Debug ; at OpenTap.ResourceManagerUtils.GetResourceNodes(IEnumerable1 _source) 2023-10-07 10:11:22.081970 ; TestPlan ; Debug ; at OpenTap.ResourceTaskManager.BeginStep(TestPlanRun planRun, ITestStepParent item, TestPlanExecutionStage stage, CancellationToken cancellationToken) 2023-10-07 10:11:22.081972 ; TestPlan ; Debug ; at OpenTap.TestPlan.OpenInternal(TestPlanRun run, Boolean isOpen, List
1 steps)
I tried to fix this bug by replacing return membersLookup.AddValue((targetType, sourceType), (result?.ToArray() ?? Array.Empty<(IMemberData, bool)>())); with the following code, and it seems works:
static readonly object lookupLock = new object();
static (IMemberData, bool hasEnabledAttribute) GetSettingsLookup(TypeData targetType, ITypeData sourceType)
{
if(membersLookup.TryGetValue((targetType, sourceType), out var value))
return value;
…
lock (lookupLock)
{
if (!membersLookupCache.TryGetValue((targetType, sourceType), out value))
{
value = membersLookupCache.AddValue((targetType, sourceType), (result?.ToArray() ?? Array.Empty<(IMemberData, bool)>()));
}
}
return value;
}