chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
using System;
using System.Collections.Generic;
using System.Threading;
using NUnit.Framework;
using OpenTap.Plugins.BasicSteps;
using OpenTap.UnitTests;
 
namespace OpenTap.Engine.UnitTests
{
    [TestFixture]
    public class TestStepTests
    {
        [Test]
        public void FormattedName()
        {
            var delay = new DelayStep() {DelaySecs = 0.1, Name = "Delay: {Time Delay}"};
            var formattedName = delay.GetFormattedName();
            Assert.AreEqual("Delay: 0.1 s", formattedName);
        }
 
        [Test]
        public void AnnotatedFormattedName()
        {
            // both annotating the step itself and TestStep.Name should give the same GetFormatted read-only string.
            
            var delay = new DelayStep() {DelaySecs = 0.1, Name = "Delay: {Time Delay}"};
            var annotation = AnnotationCollection.Annotate(delay);
            var formattedName = annotation.Get<IStringReadOnlyValueAnnotation>().Value;
            Assert.AreEqual("Delay: 0.1 s", formattedName);
 
            var formattedName2 = annotation.GetMember(nameof(TestStep.Name)).Get<IStringReadOnlyValueAnnotation>().Value;
            Assert.AreEqual("Delay: 0.1 s", formattedName2);
        }
 
        [Test]
        public void FormattedNameIssue()
        {
            var logStep = new LogStep() {};
            logStep.Name = "Log: {0}"; // At one point this caused a bug, but it was not because of GetFormattedName.
            var formattedName = logStep.GetFormattedName();
            Assert.AreEqual("Log: {0}", formattedName);
 
            var plan = new TestPlan();
            plan.ChildTestSteps.Add(logStep);
            var run = plan.Execute();
            Assert.AreEqual(Verdict.NotSet, run.Verdict);
        }
 
        [Test]
        public void TestGetObjectSettings()
        {
            // A race condition issue occured inside GetObjectSettings.
            // to reproduce it, do it in two synchronized threads.
            // the mix of threads and semaphores below are to ensure that the threadsa
            // starts as much in sync as possible.
            var steps = new ITestStep[]
            {
                new DelayStep(), new SequenceStep(), new LockStep(), new SequenceStep(),
                new DialogStep(), new BusyStep(), new ArtifactStep(), new SerializeEnumTest.Step1(), new SerializeEnumTest.Step2(),
                new MemberDataProviderTests.Delay2Step(), new ResultTest.ActionStep(), new DutStep2(), new IfStep()
            };
            int threadCount = 2;
            var threadWaitSem = new Semaphore(0, threadCount);
            var mainWaitSem = new Semaphore(0, threadCount);
            Exception error = null;
            for (int i = 0; i < threadCount; i++)
            {
                TapThread.Start(() =>
                {
                    //signal the main thread that we are ready to go.
                    mainWaitSem.Release();
                    // wait for the main thread to signal back.
                    threadWaitSem.WaitOne();
                    try
                    {
                        TestStepExtensions.GetObjectSettings<object, ITestStep, object>(steps, false, (t, data) => t, new HashSet<object>());
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                    //signal the main thread that we are done.
                    mainWaitSem.Release();
                });
            }
 
            // wait for the threads to be ready.
            for(int i = 0; i < threadCount; i++)
                mainWaitSem.WaitOne();
            
            // signal that the threads can start.
            threadWaitSem.Release(threadCount);
            
            // Wait for all to complete.
            for(int i = 0; i < threadCount; i++)
                mainWaitSem.WaitOne();
            
            // the issue should cause an exception when reproduced.
            // if its fixed the error will be null.
            Assert.IsNull(error);
        }
    }
}