1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Reflection;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Xunit;
10 
11 namespace System.Transactions.Tests
12 {
13     public class LTMEnlistmentTests
14     {
15         const int MaxTransactionCommitTimeoutInSeconds = 5;
16 
LTMEnlistmentTests()17         public LTMEnlistmentTests()
18         {
19         }
20 
21         [Theory]
22         [InlineData(1, EnlistmentOptions.None, Phase1Vote.Prepared, SinglePhaseVote.Committed, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
23         [InlineData(2, EnlistmentOptions.None, Phase1Vote.Prepared, SinglePhaseVote.Committed, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
24         [InlineData(1, EnlistmentOptions.None, Phase1Vote.Prepared, SinglePhaseVote.Aborted, true, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)]
25         [InlineData(2, EnlistmentOptions.None, Phase1Vote.Prepared, SinglePhaseVote.Aborted, true, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)]
26         [InlineData(1, EnlistmentOptions.EnlistDuringPrepareRequired, Phase1Vote.Prepared, SinglePhaseVote.Committed, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
27         [InlineData(2, EnlistmentOptions.EnlistDuringPrepareRequired, Phase1Vote.Prepared, SinglePhaseVote.Committed, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
SinglePhaseDurable(int volatileCount, EnlistmentOptions volatileEnlistmentOption, Phase1Vote volatilePhase1Vote, SinglePhaseVote singlePhaseVote, bool commit, EnlistmentOutcome expectedVolatileOutcome, TransactionStatus expectedTxStatus)28         public void SinglePhaseDurable(int volatileCount, EnlistmentOptions volatileEnlistmentOption, Phase1Vote volatilePhase1Vote, SinglePhaseVote singlePhaseVote, bool commit, EnlistmentOutcome expectedVolatileOutcome, TransactionStatus expectedTxStatus)
29         {
30             Transaction tx = null;
31             try
32             {
33                 using (TransactionScope ts = new TransactionScope())
34                 {
35                     tx = Transaction.Current.Clone();
36 
37                     if (volatileCount > 0)
38                     {
39                         TestSinglePhaseEnlistment[] volatiles = new TestSinglePhaseEnlistment[volatileCount];
40                         for (int i = 0; i < volatileCount; i++)
41                         {
42                             // It doesn't matter what we specify for SinglePhaseVote.
43                             volatiles[i] = new TestSinglePhaseEnlistment(volatilePhase1Vote, SinglePhaseVote.InDoubt, expectedVolatileOutcome);
44                             tx.EnlistVolatile(volatiles[i], volatileEnlistmentOption);
45                         }
46                     }
47 
48                     // Doesn't really matter what we specify for EnlistmentOutcome here. This is an SPC, so Phase2 won't happen for this enlistment.
49                     TestSinglePhaseEnlistment durable = new TestSinglePhaseEnlistment(Phase1Vote.Prepared, singlePhaseVote, EnlistmentOutcome.Committed);
50                     tx.EnlistDurable(Guid.NewGuid(), durable, EnlistmentOptions.None);
51 
52                     if (commit)
53                     {
54                         ts.Complete();
55                     }
56                 }
57             }
58             catch (TransactionInDoubtException)
59             {
60                 Assert.Equal(expectedTxStatus, TransactionStatus.InDoubt);
61             }
62             catch (TransactionAbortedException)
63             {
64                 Assert.Equal(expectedTxStatus, TransactionStatus.Aborted);
65             }
66 
67 
68             Assert.NotNull(tx);
69             Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status);
70         }
71 
72         [Theory]
73         // TODO: Issue #10353 - This test needs to change once we have promotion support.
74         // Right now any attempt to create a two phase durable enlistment will attempt to promote and will fail because promotion is not supported. This results in the transaction being
75         // aborted.
76         [InlineData(0, EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, Phase1Vote.Prepared, true, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)]
77         [InlineData(1, EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, Phase1Vote.Prepared, true, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)]
78         [InlineData(2, EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, Phase1Vote.Prepared, true, EnlistmentOutcome.Aborted, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)]
79         [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Expects PNSE due to not being supported on core")]
TwoPhaseDurable(int volatileCount, EnlistmentOptions volatileEnlistmentOption, EnlistmentOptions durableEnlistmentOption, Phase1Vote volatilePhase1Vote, Phase1Vote durablePhase1Vote, bool commit, EnlistmentOutcome expectedVolatileOutcome, EnlistmentOutcome expectedDurableOutcome, TransactionStatus expectedTxStatus)80         public void TwoPhaseDurable(int volatileCount, EnlistmentOptions volatileEnlistmentOption, EnlistmentOptions durableEnlistmentOption, Phase1Vote volatilePhase1Vote, Phase1Vote durablePhase1Vote, bool commit, EnlistmentOutcome expectedVolatileOutcome, EnlistmentOutcome expectedDurableOutcome, TransactionStatus expectedTxStatus)
81         {
82             Transaction tx = null;
83             try
84             {
85                 using (TransactionScope ts = new TransactionScope())
86                 {
87                     tx = Transaction.Current.Clone();
88 
89                     if (volatileCount > 0)
90                     {
91                         TestEnlistment[] volatiles = new TestEnlistment[volatileCount];
92                         for (int i = 0; i < volatileCount; i++)
93                         {
94                             // It doesn't matter what we specify for SinglePhaseVote.
95                             volatiles[i] = new TestEnlistment(volatilePhase1Vote, expectedVolatileOutcome);
96                             tx.EnlistVolatile(volatiles[i], volatileEnlistmentOption);
97                         }
98                     }
99 
100                     TestEnlistment durable = new TestEnlistment(Phase1Vote.Prepared, expectedDurableOutcome);
101                     // TODO: Issue #10353 - This needs to change once we have promotion support.
102                     Assert.Throws<PlatformNotSupportedException>(() => // Creation of two phase durable enlistment attempts to promote to MSDTC
103                     {
104                         tx.EnlistDurable(Guid.NewGuid(), durable, durableEnlistmentOption);
105                     });
106 
107                     if (commit)
108                     {
109                         ts.Complete();
110                     }
111                 }
112             }
113             catch (TransactionInDoubtException)
114             {
115                 Assert.Equal(expectedTxStatus, TransactionStatus.InDoubt);
116             }
117             catch (TransactionAbortedException)
118             {
119                 Assert.Equal(expectedTxStatus, TransactionStatus.Aborted);
120             }
121 
122             Assert.NotNull(tx);
123             Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status);
124         }
125 
126         [Theory]
127         [InlineData(EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.None, Phase1Vote.Prepared, true, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
128         [InlineData(EnlistmentOptions.None, EnlistmentOptions.None, Phase1Vote.Prepared, false, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
EnlistDuringPhase0(EnlistmentOptions enlistmentOption, EnlistmentOptions phase0EnlistmentOption, Phase1Vote phase1Vote, bool expectPhase0EnlistSuccess, bool commit, EnlistmentOutcome expectedOutcome, TransactionStatus expectedTxStatus)129         public void EnlistDuringPhase0(EnlistmentOptions enlistmentOption, EnlistmentOptions phase0EnlistmentOption, Phase1Vote phase1Vote, bool expectPhase0EnlistSuccess, bool commit, EnlistmentOutcome expectedOutcome, TransactionStatus expectedTxStatus)
130         {
131             Transaction tx = null;
132             AutoResetEvent outcomeEvent = null;
133             try
134             {
135                 using (TransactionScope ts = new TransactionScope())
136                 {
137                     tx = Transaction.Current.Clone();
138                     outcomeEvent = new AutoResetEvent(false);
139                     TestEnlistment enlistment = new TestEnlistment(phase1Vote, expectedOutcome, true, expectPhase0EnlistSuccess, outcomeEvent);
140                     tx.EnlistVolatile(enlistment, enlistmentOption);
141 
142                     if (commit)
143                     {
144                         ts.Complete();
145                     }
146                 }
147             }
148             catch (TransactionInDoubtException)
149             {
150                 Assert.Equal(expectedTxStatus, TransactionStatus.InDoubt);
151             }
152             catch (TransactionAbortedException)
153             {
154                 Assert.Equal(expectedTxStatus, TransactionStatus.Aborted);
155             }
156 
157             Assert.True(outcomeEvent.WaitOne(TimeSpan.FromSeconds(MaxTransactionCommitTimeoutInSeconds)));
158             Assert.NotNull(tx);
159             Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status);
160         }
161 
162         [Theory]
163         [InlineData(5, EnlistmentOptions.None, Phase1Vote.Prepared, Phase1Vote.Prepared, true, EnlistmentOutcome.Committed, TransactionStatus.Committed)]
164         [InlineData(5, EnlistmentOptions.None, Phase1Vote.Prepared, Phase1Vote.ForceRollback, true, EnlistmentOutcome.Aborted, TransactionStatus.Aborted)]
EnlistVolatile(int volatileCount, EnlistmentOptions enlistmentOption, Phase1Vote volatilePhase1Vote, Phase1Vote lastPhase1Vote, bool commit, EnlistmentOutcome expectedEnlistmentOutcome, TransactionStatus expectedTxStatus)165         public void EnlistVolatile(int volatileCount, EnlistmentOptions enlistmentOption, Phase1Vote volatilePhase1Vote, Phase1Vote lastPhase1Vote, bool commit, EnlistmentOutcome expectedEnlistmentOutcome, TransactionStatus expectedTxStatus)
166         {
167             AutoResetEvent[] outcomeEvents = null;
168             Transaction tx = null;
169             try
170             {
171                 using (TransactionScope ts = new TransactionScope())
172                 {
173                     tx = Transaction.Current.Clone();
174 
175                     if (volatileCount > 0)
176                     {
177                         TestEnlistment[] volatiles = new TestEnlistment[volatileCount];
178                         outcomeEvents = new AutoResetEvent[volatileCount];
179                         for (int i = 0; i < volatileCount-1; i++)
180                         {
181                             outcomeEvents[i] = new AutoResetEvent(false);
182                             volatiles[i] = new TestEnlistment(volatilePhase1Vote, expectedEnlistmentOutcome, false, true, outcomeEvents[i]);
183                             tx.EnlistVolatile(volatiles[i], enlistmentOption);
184                         }
185 
186                         outcomeEvents[volatileCount-1] = new AutoResetEvent(false);
187                         volatiles[volatileCount - 1] = new TestEnlistment(lastPhase1Vote, expectedEnlistmentOutcome, false, true, outcomeEvents[volatileCount-1]);
188                         tx.EnlistVolatile(volatiles[volatileCount - 1], enlistmentOption);
189                     }
190 
191                     if (commit)
192                     {
193                         ts.Complete();
194                     }
195                 }
196             }
197             catch (TransactionInDoubtException)
198             {
199                 Assert.Equal(expectedTxStatus, TransactionStatus.InDoubt);
200             }
201             catch (TransactionAbortedException)
202             {
203                 Assert.Equal(expectedTxStatus, TransactionStatus.Aborted);
204             }
205 
206             Assert.True(AutoResetEvent.WaitAll(outcomeEvents, TimeSpan.FromSeconds(MaxTransactionCommitTimeoutInSeconds)));
207             Assert.NotNull(tx);
208             Assert.Equal(expectedTxStatus, tx.TransactionInformation.Status);
209         }
210 
211     }
212 }
213