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 NonMsdtcPromoterTests 14 { 15 public static string PromotedTokenString1 = "Promoted Token String Number 1"; 16 public static byte[] PromotedToken1 = StringToByteArray(PromotedTokenString1); 17 public static Guid PromoterType1 = new Guid("D9A34FDF-D02A-4EED-98C3-5AD092355E17"); 18 19 private static bool s_traceEnabled = true; 20 21 private static MethodInfo s_enlistPromotableSinglePhaseMethodInfo; 22 private static MethodInfo s_setDistributedTransactionIdentifierMethodInfo; 23 private static MethodInfo s_getPromotedTokenMethodInfo; 24 private static PropertyInfo s_promoterTypePropertyInfo; 25 private static FieldInfo s_promoterTypeDtcFieldInfo; 26 NonMsdtcPromoterTests()27 public NonMsdtcPromoterTests() 28 { 29 // reset the testFailures count back to 0 for each test case. 30 VerifySoftDependencies(); 31 } 32 VerifySoftDependencies()33 private static void VerifySoftDependencies() 34 { 35 // Call test methods here if you need to run them without the TestHost/MsTest harness. 36 // First, let's get the MethodInfo objects for the methods we are going to invoke thru reflection. 37 // We can use this array for both EnlistPromotableSinglePhase and SetDistributedTransactionIdentifier 38 if (s_enlistPromotableSinglePhaseMethodInfo == null) 39 { 40 Type[] parameterTypes = new Type[] { typeof(IPromotableSinglePhaseNotification), typeof(Guid) }; 41 s_enlistPromotableSinglePhaseMethodInfo = typeof(Transaction).GetTypeInfo().GetMethod("EnlistPromotableSinglePhase", parameterTypes); 42 s_setDistributedTransactionIdentifierMethodInfo = typeof(Transaction).GetTypeInfo().GetMethod("SetDistributedTransactionIdentifier", parameterTypes); 43 s_getPromotedTokenMethodInfo = typeof(Transaction).GetTypeInfo().GetMethod("GetPromotedToken"); 44 45 // And the PropertyInfo objects for PromoterType 46 s_promoterTypePropertyInfo = typeof(Transaction).GetTypeInfo().GetProperty("PromoterType", typeof(Guid)); 47 48 // And the FieldInfo for TransactionInterop.PromoterTypeDtc 49 s_promoterTypeDtcFieldInfo = typeof(TransactionInterop).GetTypeInfo().GetField("PromoterTypeDtc", BindingFlags.Public | BindingFlags.Static); 50 } 51 52 bool allMethodsAreThere = ((s_enlistPromotableSinglePhaseMethodInfo != null) && 53 (s_setDistributedTransactionIdentifierMethodInfo != null) && 54 (s_getPromotedTokenMethodInfo != null) && 55 (s_promoterTypePropertyInfo != null) && 56 (s_promoterTypeDtcFieldInfo != null) 57 ); 58 Assert.True(allMethodsAreThere, "At least one of the expected new methods or properties is not implemented by the available System.Transactions."); 59 } 60 61 #region Helper Methods 62 CompleteDependentCloneThread(object stateObject)63 private static void CompleteDependentCloneThread(object stateObject) 64 { 65 DependentTransaction cloneToComplete = (DependentTransaction)stateObject; 66 Trace("CompleteDependentCloneThread started - will sleep for 2 seconds"); 67 68 Task.Delay(TimeSpan.FromSeconds(2)).Wait(); 69 Trace("CompletedDependentCloneThread - completing the DependentTransaction..."); 70 cloneToComplete.Complete(); 71 } 72 Promote(string testCaseDescription, byte[] promotedTokenToCompare, Transaction txToPromote = null)73 public static void Promote(string testCaseDescription, byte[] promotedTokenToCompare, Transaction txToPromote = null) 74 { 75 if (txToPromote == null) 76 { 77 txToPromote = Transaction.Current; 78 } 79 80 IPromotableSinglePhaseNotification shouldBeNull = null; 81 shouldBeNull = CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 82 NonMsdtcPromoterTests.PromotedToken1, 83 null, 84 /*nonMSDTC = */ true, 85 txToPromote, 86 /*spcResponse=*/TransactionStatus.Committed, 87 /*expectRejection = */ true); 88 89 Assert.Null(shouldBeNull); 90 91 byte[] promotedToken = TxPromotedToken(txToPromote); 92 Assert.True(PromotedTokensMatch(promotedToken, NonMsdtcPromoterTests.PromotedToken1)); 93 } 94 Trace(string stringToTrace)95 public static void Trace(string stringToTrace) 96 { 97 if (s_traceEnabled) 98 { 99 Debug.WriteLine(stringToTrace); 100 } 101 } 102 NoStressTrace(string stringToTrace)103 public static void NoStressTrace(string stringToTrace) 104 { 105 //if (NonMsdtcPromoterTests.loopCount == 1) 106 { 107 Debug.WriteLine(stringToTrace); 108 } 109 } 110 StringToByteArray(string stringToConvert)111 private static byte[] StringToByteArray(string stringToConvert) 112 { 113 byte[] bytes = new byte[stringToConvert.Length * sizeof(char)]; 114 System.Buffer.BlockCopy(stringToConvert.ToCharArray(), 0, bytes, 0, bytes.Length); 115 return bytes; 116 } 117 TestPassed(bool displayTime = false)118 private static void TestPassed(bool displayTime = false) 119 { 120 if (displayTime) 121 { 122 NoStressTrace(string.Format("Pass: {0}", DateTime.Now.ToString())); 123 } 124 else 125 { 126 NoStressTrace("Pass"); 127 } 128 } 129 CreateVolatileEnlistment( AutoResetEvent outcomeReceived, Transaction tx = null, EnlistmentOptions options = EnlistmentOptions.None, bool votePrepared = true)130 private static MyEnlistment CreateVolatileEnlistment( 131 AutoResetEvent outcomeReceived, 132 Transaction tx = null, 133 EnlistmentOptions options = EnlistmentOptions.None, 134 bool votePrepared = true) 135 { 136 MyEnlistment enlistment = new MyEnlistment(outcomeReceived, votePrepared); 137 Transaction txToEnlist = Transaction.Current; 138 if (tx != null) 139 { 140 txToEnlist = tx; 141 } 142 txToEnlist.EnlistVolatile(enlistment, options); 143 return enlistment; 144 } 145 CreatePSPEEnlistment( Guid promoterType, byte[] promotedToken, AutoResetEvent outcomeReceived, bool nonMSDTC = true, Transaction tx = null, TransactionStatus spcResponse = TransactionStatus.Committed, bool expectRejection = false, bool comparePromotedToken = false, bool failInitialize = false, bool failPromote = false, bool failSPC = false, bool failGetPromoterType = false, bool failGetId = false, bool incorrectNotificationObjectToSetDistributedTransactionId = false )146 private static IPromotableSinglePhaseNotification CreatePSPEEnlistment( 147 Guid promoterType, 148 byte[] promotedToken, 149 AutoResetEvent outcomeReceived, 150 bool nonMSDTC = true, 151 Transaction tx = null, 152 TransactionStatus spcResponse = TransactionStatus.Committed, 153 bool expectRejection = false, 154 bool comparePromotedToken = false, 155 bool failInitialize = false, 156 bool failPromote = false, 157 bool failSPC = false, 158 bool failGetPromoterType = false, 159 bool failGetId = false, 160 bool incorrectNotificationObjectToSetDistributedTransactionId = false 161 ) 162 { 163 IPromotableSinglePhaseNotification enlistment = null; 164 165 Transaction txToEnlist = Transaction.Current; 166 if (tx != null) 167 { 168 txToEnlist = tx; 169 } 170 if (nonMSDTC) 171 { 172 NonMSDTCPromoterEnlistment nonMSDTCEnlistment = new NonMSDTCPromoterEnlistment(promoterType, 173 promotedToken, 174 outcomeReceived, 175 spcResponse, 176 failInitialize, 177 failPromote, 178 failSPC, 179 failGetPromoterType, 180 failGetId, 181 incorrectNotificationObjectToSetDistributedTransactionId); 182 if (nonMSDTCEnlistment.Enlist(txToEnlist, expectRejection, comparePromotedToken)) 183 { 184 enlistment = nonMSDTCEnlistment; 185 } 186 187 TryProhibitedOperations(txToEnlist, promoterType); 188 } 189 else 190 { 191 throw new ApplicationException("normal PSPE not implemented yet."); 192 } 193 194 return enlistment; 195 } 196 CreateDependentClone(bool blocking, Transaction tx = null)197 private static DependentTransaction CreateDependentClone(bool blocking, Transaction tx = null) 198 { 199 DependentTransaction clone = null; 200 if (tx == null) 201 { 202 tx = Transaction.Current; 203 } 204 205 clone = tx.DependentClone(blocking ? DependentCloneOption.BlockCommitUntilComplete : DependentCloneOption.RollbackIfNotComplete); 206 207 return clone; 208 } 209 TryProhibitedOperations(Transaction tx, Guid expectedPromoterType)210 private static void TryProhibitedOperations(Transaction tx, Guid expectedPromoterType) 211 { 212 // First make sure that we can do a simple clone. This should be allowed. 213 tx.Clone(); 214 215 try 216 { 217 Trace("Attempting TransactionInterop.GetDtcTransaction"); 218 TransactionInterop.GetDtcTransaction(tx); 219 throw new ApplicationException("TransactionInterop.GetDtcTransaction unexpectedly succeeded."); 220 } 221 catch (TransactionPromotionException ex) 222 { 223 if (TxPromoterType(tx) != expectedPromoterType) 224 { 225 Trace(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 226 throw new ApplicationException(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 227 } 228 } 229 230 try 231 { 232 Trace("Attempting TransactionInterop.GetExportCookie"); 233 byte[] dummyWhereabouts = new byte[1]; 234 TransactionInterop.GetExportCookie(tx, dummyWhereabouts); 235 throw new ApplicationException("TransactionInterop.GetExportCookie unexpectedly succeeded."); 236 } 237 catch (TransactionPromotionException ex) 238 { 239 if (TxPromoterType(tx) != expectedPromoterType) 240 { 241 Trace(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 242 throw new ApplicationException(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 243 } 244 } 245 246 try 247 { 248 Trace("Attempting TransactionInterop.GetTransmitterPropagationToken"); 249 byte[] dummyWhereabouts = new byte[1]; 250 TransactionInterop.GetTransmitterPropagationToken(tx); 251 throw new ApplicationException("TransactionInterop.GetTransmitterPropagationToken unexpectedly succeeded."); 252 } 253 catch (TransactionPromotionException ex) 254 { 255 if (TxPromoterType(tx) != expectedPromoterType) 256 { 257 Trace(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 258 throw new ApplicationException(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 259 } 260 } 261 262 try 263 { 264 Trace("Attempting EnlistDurable"); 265 DummyDurableEnlistment enlistment = new DummyDurableEnlistment(); 266 tx.EnlistDurable(new Guid("611653C3-8536-4158-A990-00A8EE08B195"), enlistment, EnlistmentOptions.None); 267 throw new ApplicationException("EnlistDurable unexpectedly succeeded."); 268 } 269 catch (TransactionPromotionException ex) 270 { 271 if (TxPromoterType(tx) != expectedPromoterType) 272 { 273 Trace(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 274 throw new ApplicationException(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 275 } 276 } 277 278 try 279 { 280 Trace("Attempting EnlistDurableSPC"); 281 DummyDurableEnlistmentSPC enlistment = new DummyDurableEnlistmentSPC(); 282 tx.EnlistDurable(new Guid("611653C3-8536-4158-A990-00A8EE08B195"), enlistment, EnlistmentOptions.None); 283 throw new ApplicationException("EnlistDurableSPC unexpectedly succeeded."); 284 } 285 catch (TransactionPromotionException ex) 286 { 287 if (TxPromoterType(tx) != expectedPromoterType) 288 { 289 Trace(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 290 throw new ApplicationException(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 291 } 292 } 293 294 // TODO #9582: Uncomment once IFormatter and BinaryFormatter are available in .NET Core 295 //try 296 //{ 297 // MemoryStream txStream = new MemoryStream(); 298 // IFormatter formatter = new BinaryFormatter(); 299 // formatter.Serialize(txStream, tx); 300 // throw new ApplicationException("Serialize of transaction unexpectedly succeeded."); 301 //} 302 //catch (TransactionPromotionException ex) 303 //{ 304 // if (TxPromoterType(tx) != expectedPromoterType) 305 // { 306 // Trace(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 307 // throw new ApplicationException(string.Format("Exception {0} occurred, but transaction has an unexpected PromoterType of {1}", ex.ToString(), TxPromoterType(tx))); 308 // } 309 //} 310 } 311 PromotedTokensMatch(byte[] one, byte[] two)312 private static bool PromotedTokensMatch(byte[] one, byte[] two) 313 { 314 if (one.Length != two.Length) 315 { 316 return false; 317 } 318 319 for (int i = 1; i < one.Length; i++) 320 { 321 if (one[i] != two[i]) 322 { 323 return false; 324 } 325 } 326 return true; 327 } 328 EnlistPromotable(IPromotableSinglePhaseNotification promotableNotification, Transaction txToEnlist, Guid promoterType)329 private static bool EnlistPromotable(IPromotableSinglePhaseNotification promotableNotification, Transaction txToEnlist, Guid promoterType) 330 { 331 object[] parameters = new object[] { promotableNotification, promoterType }; 332 bool returnVal = (bool)s_enlistPromotableSinglePhaseMethodInfo.Invoke(txToEnlist, parameters); 333 return returnVal; 334 } 335 SetDistributedTransactionId(IPromotableSinglePhaseNotification promotableNotification, Transaction txToSet, Guid distributedId)336 private static void SetDistributedTransactionId(IPromotableSinglePhaseNotification promotableNotification, Transaction txToSet, Guid distributedId) 337 { 338 object[] parameters = new object[] { promotableNotification, distributedId }; 339 s_setDistributedTransactionIdentifierMethodInfo.Invoke(txToSet, parameters); 340 } 341 TxPromoterType(Transaction txToGet)342 private static Guid TxPromoterType(Transaction txToGet) 343 { 344 return (Guid)s_promoterTypePropertyInfo.GetValue(txToGet); 345 } 346 TxPromotedToken(Transaction txToGet)347 private static byte[] TxPromotedToken(Transaction txToGet) 348 { 349 return (byte[])s_getPromotedTokenMethodInfo.Invoke(txToGet, null); 350 } 351 352 private static Guid PromoterTypeDtc 353 { 354 get 355 { 356 return (Guid)s_promoterTypeDtcFieldInfo.GetValue(null); 357 } 358 } 359 360 #endregion 361 362 #region NonMSDTCPromoterEnlistment 363 public class NonMSDTCPromoterEnlistment : IPromotableSinglePhaseNotification 364 { 365 private Guid _promoterType; 366 private byte[] _promotedToken; 367 private TransactionStatus _spcResponse; 368 private bool _failPromote; 369 private bool _failInitialize; 370 private bool _failSPC; 371 private bool _failGetPromoterType; 372 private bool _failGetId; 373 private bool _incorrectNotificationObjectToSetDistributedTransactionId; 374 private AutoResetEvent _completionEvent; 375 private Guid _distributedTxId; 376 private Transaction _enlistedTransaction; 377 NonMSDTCPromoterEnlistment(Guid promoterType, byte[] promotedTokenToReturn, AutoResetEvent completionEvent, TransactionStatus spcResponse = TransactionStatus.Committed, bool failInitialize = false, bool failPromote = false, bool failSPC = false, bool failGetPromoterType = false, bool failGetId = false, bool incorrectNotificationObjectToSetDistributedTransactionId = false )378 public NonMSDTCPromoterEnlistment(Guid promoterType, 379 byte[] promotedTokenToReturn, 380 AutoResetEvent completionEvent, 381 TransactionStatus spcResponse = TransactionStatus.Committed, 382 bool failInitialize = false, 383 bool failPromote = false, 384 bool failSPC = false, 385 bool failGetPromoterType = false, 386 bool failGetId = false, 387 bool incorrectNotificationObjectToSetDistributedTransactionId = false 388 ) 389 { 390 _promoterType = promoterType; 391 _promotedToken = promotedTokenToReturn; 392 _spcResponse = spcResponse; 393 _completionEvent = completionEvent; 394 _failInitialize = failInitialize; 395 _failPromote = failPromote; 396 _failSPC = failSPC; 397 _failGetPromoterType = failGetPromoterType; 398 _failGetId = failGetId; 399 _incorrectNotificationObjectToSetDistributedTransactionId = incorrectNotificationObjectToSetDistributedTransactionId; 400 } 401 402 public bool Aborted 403 { 404 get; 405 private set; 406 } 407 408 public bool Promoted 409 { 410 get; 411 private set; 412 } 413 Initialize()414 public void Initialize() 415 { 416 Trace("NonMSDTCPromoterEnlistment.Initialize"); 417 if (_failInitialize) 418 { 419 Trace("NonMSDTCPromoterEnlistment.Initialize - Failing based on configuration"); 420 throw new ApplicationException("Failing Initialize based on configuration"); 421 } 422 return; 423 } 424 Enlist(Transaction txToEnlist = null, bool expectRejection = false, bool comparePromotedToken = false)425 public bool Enlist(Transaction txToEnlist = null, bool expectRejection = false, bool comparePromotedToken = false) 426 { 427 if (txToEnlist == null) 428 { 429 txToEnlist = Transaction.Current; 430 } 431 432 // invoke txToEnlist.EnlistPromotableSinglePhase(this, this.promoterType) via reflection. 433 if (!EnlistPromotable(this, txToEnlist, _promoterType)) 434 { 435 if (expectRejection) 436 { 437 // invoke txToEnlist.PromoterType and txToEnlist.PromotedToken via reflection 438 if (comparePromotedToken && (TxPromoterType(txToEnlist) == _promoterType)) 439 { 440 if (TxPromotedToken(txToEnlist) != _promotedToken) 441 { 442 throw new ApplicationException("The PromotedToken does not match"); 443 } 444 } 445 return false; 446 } 447 else 448 { 449 throw new ApplicationException("EnlistPromotableSinglePhase failed when expected to succeed"); 450 } 451 } 452 else if (expectRejection) 453 { 454 throw new ApplicationException("EnlistPromotableSinglePhase succeeded when expected to fail"); 455 } 456 457 _enlistedTransaction = txToEnlist; 458 return true; 459 } 460 Rollback(SinglePhaseEnlistment singlePhaseEnlistment)461 public void Rollback(SinglePhaseEnlistment singlePhaseEnlistment) 462 { 463 Trace("NonMSDTCPromoterEnlistment.Aborted"); 464 this.Aborted = true; 465 _completionEvent.Set(); 466 singlePhaseEnlistment.Done(); 467 } 468 SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)469 public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment) 470 { 471 Trace("NonMSDTCPromoterEnlistment.SinglePhaseCommit"); 472 _completionEvent.Set(); 473 474 if (_failSPC) 475 { 476 Trace("NonMSDTCPromoterEnlistment.SinglePhaseCommit - Failing based on configuration"); 477 throw new ApplicationException("Failing SinglePhaseCommit based on configuration"); 478 } 479 480 switch (_spcResponse) 481 { 482 case TransactionStatus.Committed: 483 { 484 singlePhaseEnlistment.Committed(); 485 break; 486 } 487 488 case TransactionStatus.Aborted: 489 { 490 singlePhaseEnlistment.Aborted(new ApplicationException("Aborted by NonMSDTCPromoterEnlistment.SinglePhaseCommit")); 491 break; 492 } 493 494 case TransactionStatus.InDoubt: 495 { 496 singlePhaseEnlistment.InDoubt(new ApplicationException("InDoubt by NonMSDTCPromoterEnlistment.SinglePhaseCommit")); 497 break; 498 } 499 default: 500 { 501 throw new ApplicationException("InDoubt by NonMSDTCPromoterEnlistment.SinglePhaseCommit because of invalid TransactionStatus outcome value."); 502 } 503 } 504 } 505 Promote()506 public byte[] Promote() 507 { 508 Trace("NonMSDTCPromoterEnlistment.Promote"); 509 this.Promoted = true; 510 _distributedTxId = Guid.NewGuid(); 511 if (_failPromote) 512 { 513 Trace("NonMSDTCPromoterEnlistment.Promote - Failing based on configuration"); 514 throw new ApplicationException("Failing promotion based on configuration"); 515 } 516 517 // invoke this.enlistedTransaction.SetDistributedTransactionIdentifier(this, this.promoterType); via reflection 518 if (_incorrectNotificationObjectToSetDistributedTransactionId) 519 { 520 NonMSDTCPromoterEnlistment incorrectNotificationObject = new NonMSDTCPromoterEnlistment(_promoterType, _promotedToken, _completionEvent); 521 try 522 { 523 SetDistributedTransactionId(incorrectNotificationObject, _enlistedTransaction, _distributedTxId); 524 throw new ApplicationException("SetDistributedTransactionIdentifier did not throw the expected InvalidOperationException"); 525 } 526 catch (TargetInvocationException ex) 527 { 528 if (!(ex.InnerException is InvalidOperationException)) 529 { 530 throw new ApplicationException("SetDistributedTransactionIdentifier did not throw the expected InvalidOperationException"); 531 } 532 } 533 } 534 else 535 { 536 SetDistributedTransactionId(this, _enlistedTransaction, _distributedTxId); 537 } 538 539 return _promotedToken; 540 } 541 } 542 #endregion 543 544 #region MyEnlistment 545 public class MyEnlistment : IEnlistmentNotification 546 { 547 private bool _committed; 548 private bool _aborted; 549 private bool _indoubt; 550 private bool _votePrepared; 551 private bool _enlistDuringPrepare; 552 private EnlistmentOptions _enlistOptions; 553 private bool _expectSuccessfulEnlist; 554 private AutoResetEvent _secondEnlistmentCompleted; 555 556 private AutoResetEvent _outcomeReceived; 557 MyEnlistment( AutoResetEvent outcomeReceived, bool votePrepared = true, bool enlistDuringPrepare = false, EnlistmentOptions enlistOptions = EnlistmentOptions.None, bool expectSuccessfulEnlist = true, AutoResetEvent secondEnlistmentCompleted = null )558 public MyEnlistment( 559 AutoResetEvent outcomeReceived, 560 bool votePrepared = true, 561 bool enlistDuringPrepare = false, 562 EnlistmentOptions enlistOptions = EnlistmentOptions.None, 563 bool expectSuccessfulEnlist = true, 564 AutoResetEvent secondEnlistmentCompleted = null 565 ) 566 { 567 _outcomeReceived = outcomeReceived; 568 _votePrepared = votePrepared; 569 _enlistDuringPrepare = enlistDuringPrepare; 570 _enlistOptions = enlistOptions; 571 _expectSuccessfulEnlist = expectSuccessfulEnlist; 572 _secondEnlistmentCompleted = secondEnlistmentCompleted; 573 } 574 575 public Transaction TransactionToEnlist 576 { 577 get; 578 set; 579 } 580 581 public bool CommittedOutcome 582 { 583 get 584 { 585 return _committed; 586 } 587 } 588 589 public bool AbortedOutcome 590 { 591 get 592 { 593 return _aborted; 594 } 595 } 596 597 public bool InDoubtOutcome 598 { 599 get 600 { 601 return _indoubt; 602 } 603 } 604 Commit(Enlistment enlistment)605 public void Commit(Enlistment enlistment) 606 { 607 Trace("MyEnlistment.Commit"); 608 _committed = true; 609 enlistment.Done(); 610 _outcomeReceived.Set(); 611 } 612 InDoubt(Enlistment enlistment)613 public void InDoubt(Enlistment enlistment) 614 { 615 Trace("MyEnlistment.InDoubt"); 616 _indoubt = true; 617 enlistment.Done(); 618 _outcomeReceived.Set(); 619 } 620 Prepare(PreparingEnlistment preparingEnlistment)621 public void Prepare(PreparingEnlistment preparingEnlistment) 622 { 623 if (_enlistDuringPrepare) 624 { 625 Trace(string.Format("MyEnlistment.Prepare - attempting another enlistment with options {0}", _enlistOptions.ToString())); 626 try 627 { 628 MyEnlistment enlist2 = new MyEnlistment( 629 _secondEnlistmentCompleted, 630 /*votePrepared=*/ true, 631 /*enlistDuringPrepare=*/ false, 632 _enlistOptions); 633 this.TransactionToEnlist.EnlistVolatile(enlist2, _enlistOptions); 634 if (!_expectSuccessfulEnlist) 635 { 636 // Force rollback of the transaction because the second enlistment was unsuccessful. 637 Trace("MyEnlistment.Prepare - Force Rollback because second enlistment succeeded unexpectedly"); 638 _aborted = true; 639 _outcomeReceived.Set(); 640 preparingEnlistment.ForceRollback(new ApplicationException("MyEnlistment voted ForceRollback")); 641 return; 642 } 643 } 644 catch (Exception ex) 645 { 646 if (_expectSuccessfulEnlist) 647 { 648 Trace(string.Format("MyEnlistment.Prepare - Force Rollback because second enlistment failed unexpectedly - {0}; {1}", ex.GetType().ToString(), ex.ToString())); 649 // Force rollback of the transaction because the second enlistment was unsuccessful. 650 _aborted = true; 651 _outcomeReceived.Set(); 652 preparingEnlistment.ForceRollback(new ApplicationException("MyEnlistment voted ForceRollback")); 653 return; 654 } 655 } 656 } 657 658 if (_votePrepared) 659 { 660 Trace("MyEnlistment.Prepare voting Prepared"); 661 preparingEnlistment.Prepared(); 662 } 663 else 664 { 665 Trace("MyEnlistment.Prepare - Force Rollback"); 666 _aborted = true; 667 _outcomeReceived.Set(); 668 preparingEnlistment.ForceRollback(new ApplicationException("MyEnlistment voted ForceRollback")); 669 } 670 } 671 Rollback(Enlistment enlistment)672 public void Rollback(Enlistment enlistment) 673 { 674 Trace("MyEnlistment.Rollback"); 675 _aborted = true; 676 enlistment.Done(); 677 _outcomeReceived.Set(); 678 } 679 } 680 #endregion 681 682 #region DummyDurableEnlistment 683 public class DummyDurableEnlistment : IEnlistmentNotification 684 { Commit(Enlistment enlistment)685 public void Commit(Enlistment enlistment) 686 { 687 throw new NotImplementedException(); 688 } 689 InDoubt(Enlistment enlistment)690 public void InDoubt(Enlistment enlistment) 691 { 692 throw new NotImplementedException(); 693 } 694 Prepare(PreparingEnlistment preparingEnlistment)695 public void Prepare(PreparingEnlistment preparingEnlistment) 696 { 697 throw new NotImplementedException(); 698 } 699 Rollback(Enlistment enlistment)700 public void Rollback(Enlistment enlistment) 701 { 702 throw new NotImplementedException(); 703 } 704 } 705 #endregion 706 707 #region DummyDurableEnlistmentSPC 708 private class DummyDurableEnlistmentSPC : ISinglePhaseNotification 709 { SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment)710 public void SinglePhaseCommit(SinglePhaseEnlistment singlePhaseEnlistment) 711 { 712 throw new NotImplementedException(); 713 } 714 Commit(Enlistment enlistment)715 public void Commit(Enlistment enlistment) 716 { 717 throw new NotImplementedException(); 718 } 719 InDoubt(Enlistment enlistment)720 public void InDoubt(Enlistment enlistment) 721 { 722 throw new NotImplementedException(); 723 } 724 Prepare(PreparingEnlistment preparingEnlistment)725 public void Prepare(PreparingEnlistment preparingEnlistment) 726 { 727 throw new NotImplementedException(); 728 } 729 Rollback(Enlistment enlistment)730 public void Rollback(Enlistment enlistment) 731 { 732 throw new NotImplementedException(); 733 } 734 } 735 #endregion 736 737 // This class is used in conjunction with SubordinateTransaction. When asked via the Promote 738 // method, it needs to create a DTC transaction and return the propagation token. Since we 739 // can't just create another CommittableTransaction and promote it and return it's propagation 740 // token in the same AppDomain, we spin up another AppDomain and do it there. 741 private class MySimpleTransactionSuperior : ISimpleTransactionSuperior 742 { 743 private DtcTxCreator _dtcTxCreator = new DtcTxCreator() { TraceEnabled = false }; 744 private PromotedTx _promotedTx; 745 Promote()746 public byte[] Promote() 747 { 748 byte[] propagationToken = null; 749 750 Trace("MySimpleTransactionSuperior.Promote"); 751 propagationToken = _dtcTxCreator.CreatePromotedTx(ref _promotedTx); 752 753 return propagationToken; 754 } 755 Rollback()756 public void Rollback() 757 { 758 Trace("MySimpleTransactionSuperior.Rollback"); 759 _promotedTx.Rollback(); 760 } 761 Commit()762 public void Commit() 763 { 764 Trace("MySimpleTransactionSuperior.Commit"); 765 _promotedTx.Commit(); 766 } 767 } 768 769 public class DtcTxCreator // : MarshalByRefObject 770 { 771 private static bool s_trace = false; 772 773 public bool TraceEnabled 774 { 775 get { return s_trace; } 776 set { s_trace = value; } 777 } Trace(string stringToTrace, params object[] args)778 public static void Trace(string stringToTrace, params object[] args) 779 { 780 if (s_trace) 781 { 782 Debug.WriteLine(stringToTrace, args); 783 } 784 } 785 CreatePromotedTx(ref PromotedTx promotedTx)786 public byte[] CreatePromotedTx(ref PromotedTx promotedTx) 787 { 788 DtcTxCreator.Trace("DtcTxCreator.CreatePromotedTx"); 789 byte[] propagationToken; 790 CommittableTransaction commitTx = new CommittableTransaction(); 791 promotedTx = new PromotedTx(commitTx); 792 propagationToken = TransactionInterop.GetTransmitterPropagationToken(commitTx); 793 return propagationToken; 794 } 795 } 796 797 // This is the class that is created in the "other" AppDomain to create a 798 // CommittableTransaction, promote it to DTC, and return the propagation token. 799 // It also commits or aborts the transaction. Used by MySimpleTransactionSuperior 800 // to create a DTC transaction when asked to promote. 801 public class PromotedTx // : MarshalByRefObject 802 { 803 private CommittableTransaction _commitTx; 804 PromotedTx(CommittableTransaction commitTx)805 public PromotedTx(CommittableTransaction commitTx) 806 { 807 DtcTxCreator.Trace("PromotedTx constructor"); 808 _commitTx = commitTx; 809 } 810 ~PromotedTx()811 ~PromotedTx() 812 { 813 DtcTxCreator.Trace("PromotedTx destructor"); 814 if (_commitTx != null) 815 { 816 DtcTxCreator.Trace("PromotedTx destructor calling Rollback"); 817 _commitTx.Rollback(); 818 _commitTx = null; 819 } 820 } 821 Commit()822 public void Commit() 823 { 824 DtcTxCreator.Trace("PromotedTx.Commit"); 825 _commitTx.Commit(); 826 _commitTx = null; 827 } 828 Rollback()829 public void Rollback() 830 { 831 DtcTxCreator.Trace("PromotedTx.Rollback"); 832 _commitTx.Rollback(); 833 _commitTx = null; 834 } 835 } 836 837 #region TestCase_ methods TestCase_VolatileEnlistments( int count, TransactionStatus expectedOutcome, EnlistmentOptions options = EnlistmentOptions.None, bool commitTx = true, bool votePrepared = true, Type expectedExceptionType = null)838 private static void TestCase_VolatileEnlistments( 839 int count, 840 TransactionStatus expectedOutcome, 841 EnlistmentOptions options = EnlistmentOptions.None, 842 bool commitTx = true, 843 bool votePrepared = true, 844 Type expectedExceptionType = null) 845 { 846 string testCaseDescription = string.Format("TestCase_VolatileEnlistments; count = {0}; expectedOutcome = {1}; options = {2}; votePrepared = {3}, expectedExceptionType = {4}", 847 count, 848 expectedOutcome.ToString(), 849 options.ToString(), 850 votePrepared, 851 expectedExceptionType); 852 853 Trace("**** " + testCaseDescription + " ****"); 854 855 AutoResetEvent[] enlistmentDoneEvts = new AutoResetEvent[count]; 856 MyEnlistment[] vols = new MyEnlistment[count]; 857 for (int i = 0; i < count; i++) 858 { 859 enlistmentDoneEvts[i] = new AutoResetEvent(false); 860 } 861 862 try 863 { 864 using (TransactionScope ts = new TransactionScope()) 865 { 866 for (int i = 0; i < count; i++) 867 { 868 vols[i] = CreateVolatileEnlistment(enlistmentDoneEvts[i], null, options, votePrepared); 869 } 870 if (commitTx) 871 { 872 ts.Complete(); 873 } 874 } 875 } 876 catch (Exception ex) 877 { 878 Assert.Equal(expectedExceptionType, ex.GetType()); 879 } 880 881 for (int i = 0; i < count; i++) 882 { 883 Assert.True(enlistmentDoneEvts[i].WaitOne(TimeSpan.FromSeconds(5))); 884 } 885 886 int passCount = 0; 887 for (int i = 0; i < count; i++) 888 { 889 if (((expectedOutcome == TransactionStatus.Committed) && vols[i].CommittedOutcome) || 890 ((expectedOutcome == TransactionStatus.Aborted) && vols[i].AbortedOutcome) || 891 ((expectedOutcome == TransactionStatus.InDoubt) && vols[i].InDoubtOutcome) 892 ) 893 { 894 passCount++; 895 } 896 } 897 Assert.Equal(count, passCount); 898 899 TestPassed(); 900 } 901 TestCase_PSPENonMsdtc(bool commit, bool promote, TransactionStatus spcResponse, int p0BeforePSPE = 0, int p0AfterPSPE = 0, int p1BeforePSPE = 0, int p1AfterPSPE = 0, int p0AfterPromote = 0, int p1AfterPromote = 0 )902 private static void TestCase_PSPENonMsdtc(bool commit, 903 bool promote, 904 TransactionStatus spcResponse, 905 int p0BeforePSPE = 0, 906 int p0AfterPSPE = 0, 907 int p1BeforePSPE = 0, 908 int p1AfterPSPE = 0, 909 int p0AfterPromote = 0, 910 int p1AfterPromote = 0 911 ) 912 { 913 string testCaseDescription = string.Format( 914 "TestCase_PSPENonMsdtc commit={0}; promote={1}; spcResponse= {2}; p0BeforePSPE={3}; p0AfterPSPE={4}; p1BeforePSPE={5}; p1AfterPSPE={6}; p0AfterPromote={7}; p1AfterPromote={8}", 915 commit, 916 promote, 917 spcResponse, 918 p0BeforePSPE, 919 p0AfterPSPE, 920 p1BeforePSPE, 921 p1AfterPSPE, 922 p0AfterPromote, 923 p1AfterPromote); 924 925 Trace("**** " + testCaseDescription + " ****"); 926 927 // It doesn't make sense to have "AfterPromote" enlistments if we aren't going to promote the transaction. 928 if (!promote) 929 { 930 if ((p0AfterPromote > 0) || (p1AfterPromote > 0)) 931 { 932 Trace("Not promoting - Resetting p0AfterPromote and p1AfterPromote to 0."); 933 p0AfterPromote = 0; 934 p1AfterPromote = 0; 935 } 936 } 937 938 AutoResetEvent completedEvent = new AutoResetEvent(false); 939 940 IPromotableSinglePhaseNotification enlistment = null; 941 Transaction savedTransaction = null; 942 943 int numVolatiles = p0BeforePSPE + p0AfterPSPE + p1BeforePSPE + p1AfterPSPE + p0AfterPromote + p1AfterPromote; 944 945 AutoResetEvent[] enlistmentDoneEvts = new AutoResetEvent[numVolatiles]; 946 MyEnlistment[] vols = new MyEnlistment[numVolatiles]; 947 for (int i = 0; i < numVolatiles; i++) 948 { 949 enlistmentDoneEvts[i] = new AutoResetEvent(false); 950 } 951 952 try 953 { 954 using (TransactionScope ts = new TransactionScope()) 955 { 956 // For checking status later, outside the scope. 957 savedTransaction = Transaction.Current.Clone(); 958 959 if (p0BeforePSPE > 0) 960 { 961 for (int i = 0; i < p0BeforePSPE; i++) 962 { 963 vols[i] = CreateVolatileEnlistment(enlistmentDoneEvts[i], null, EnlistmentOptions.EnlistDuringPrepareRequired, /*votePrepared=*/ true); 964 } 965 } 966 967 if (p1BeforePSPE > 0) 968 { 969 for (int i = 0; i < p1BeforePSPE; i++) 970 { 971 vols[p0BeforePSPE + i] = CreateVolatileEnlistment(enlistmentDoneEvts[p0BeforePSPE + i], null, EnlistmentOptions.None, /*votePrepared=*/ true); 972 } 973 } 974 975 enlistment = CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 976 NonMsdtcPromoterTests.PromotedToken1, 977 completedEvent, 978 /*nonMSDTC = */ true, 979 /*tx = */ null, 980 spcResponse, 981 /*expectRejection=*/ false 982 ); 983 984 if (p0AfterPSPE > 0) 985 { 986 for (int i = 0; i < p0AfterPSPE; i++) 987 { 988 vols[p0BeforePSPE + p1BeforePSPE + i] = CreateVolatileEnlistment( 989 enlistmentDoneEvts[p0BeforePSPE + p1BeforePSPE + i], 990 null, EnlistmentOptions.EnlistDuringPrepareRequired, /*votePrepared=*/ true); 991 } 992 } 993 994 if (p1AfterPSPE > 0) 995 { 996 for (int i = 0; i < p1AfterPSPE; i++) 997 { 998 vols[p0BeforePSPE + p1BeforePSPE + p0AfterPSPE + i] = CreateVolatileEnlistment( 999 enlistmentDoneEvts[p0BeforePSPE + p1BeforePSPE + p0AfterPSPE + i], 1000 null, EnlistmentOptions.None, /*votePrepared=*/ true); 1001 } 1002 } 1003 1004 if (promote) 1005 { 1006 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1007 1008 if (p0AfterPromote > 0) 1009 { 1010 for (int i = 0; i < p0AfterPromote; i++) 1011 { 1012 vols[p0BeforePSPE + p1BeforePSPE + p0AfterPSPE + p1AfterPSPE + i] = CreateVolatileEnlistment( 1013 enlistmentDoneEvts[p0BeforePSPE + p1BeforePSPE + p0AfterPSPE + p1AfterPSPE + i], 1014 null, EnlistmentOptions.EnlistDuringPrepareRequired, /*votePrepared=*/ true); 1015 } 1016 } 1017 1018 if (p1AfterPromote > 0) 1019 { 1020 for (int i = 0; i < p1AfterPromote; i++) 1021 { 1022 vols[p0BeforePSPE + p1BeforePSPE + p0AfterPSPE + p1AfterPSPE + p0AfterPromote + i] = CreateVolatileEnlistment( 1023 enlistmentDoneEvts[p0BeforePSPE + p1BeforePSPE + p0AfterPSPE + p1AfterPSPE + p0AfterPromote + i], 1024 null, EnlistmentOptions.None, /*votePrepared=*/ true); 1025 } 1026 } 1027 } 1028 1029 if (commit) 1030 { 1031 ts.Complete(); 1032 } 1033 } 1034 } 1035 catch (Exception ex) 1036 { 1037 TransactionAbortedException abortedEx = ex as TransactionAbortedException; 1038 if ((abortedEx != null) && (spcResponse != TransactionStatus.Aborted)) 1039 { 1040 Assert.Equal(spcResponse, TransactionStatus.Aborted); 1041 } 1042 1043 TransactionInDoubtException indoubtEx = ex as TransactionInDoubtException; 1044 if ((indoubtEx != null) && (spcResponse != TransactionStatus.InDoubt)) 1045 { 1046 Assert.Equal(spcResponse, TransactionStatus.InDoubt); 1047 } 1048 1049 if (spcResponse == TransactionStatus.Committed) 1050 { 1051 Trace(string.Format("Caught unexpected exception {0}:{1}", ex.GetType().ToString(), ex.ToString())); 1052 return; 1053 } 1054 } 1055 1056 NonMSDTCPromoterEnlistment nonDtcEnlistment = enlistment as NonMSDTCPromoterEnlistment; 1057 Assert.NotNull(nonDtcEnlistment); 1058 1059 if (numVolatiles > 0) 1060 { 1061 for (int i = 0; i < numVolatiles; i++) 1062 { 1063 Assert.True(enlistmentDoneEvts[i].WaitOne(TimeSpan.FromSeconds(5))); 1064 } 1065 1066 int passCount = 0; 1067 for (int i = 0; i < numVolatiles; i++) 1068 { 1069 if (commit) 1070 { 1071 if (((spcResponse == TransactionStatus.Committed) && vols[i].CommittedOutcome) || 1072 ((spcResponse == TransactionStatus.Aborted) && vols[i].AbortedOutcome) || 1073 ((spcResponse == TransactionStatus.InDoubt) && vols[i].InDoubtOutcome) 1074 ) 1075 { 1076 passCount++; 1077 } 1078 } 1079 else 1080 { 1081 if (vols[i].AbortedOutcome) 1082 { 1083 passCount++; 1084 } 1085 } 1086 } 1087 Assert.Equal(numVolatiles, passCount); 1088 } 1089 1090 Assert.True(completedEvent.WaitOne(TimeSpan.FromSeconds(5))); 1091 1092 Assert.False(!promote && nonDtcEnlistment.Promoted); 1093 1094 Assert.False(promote && !nonDtcEnlistment.Promoted); 1095 1096 if (commit) 1097 { 1098 Assert.False((spcResponse == TransactionStatus.Committed) && (nonDtcEnlistment.Aborted)); 1099 Assert.Equal(spcResponse, savedTransaction.TransactionInformation.Status); 1100 } 1101 else 1102 { 1103 Assert.True(nonDtcEnlistment.Aborted); 1104 Assert.Equal(TransactionStatus.Aborted, savedTransaction.TransactionInformation.Status); 1105 } 1106 1107 TestPassed(); 1108 } 1109 TestCase_PSPENonMsdtcWithClones( bool commit, bool promote, TransactionStatus spcResponse, int abortingBeforePSPE = 0, int abortingAfterPSPE = 0, int blockingBeforePSPE = 0, int blockingAfterPSPE = 0, int abortingAfterPromote = 0, int blockingAfterPromote = 0)1110 private static void TestCase_PSPENonMsdtcWithClones( 1111 bool commit, 1112 bool promote, 1113 TransactionStatus spcResponse, 1114 int abortingBeforePSPE = 0, 1115 int abortingAfterPSPE = 0, 1116 int blockingBeforePSPE = 0, 1117 int blockingAfterPSPE = 0, 1118 int abortingAfterPromote = 0, 1119 int blockingAfterPromote = 0) 1120 { 1121 string testCaseDescription = string.Format( 1122 "TestCase_PSPENonMsdtcWithClones commit={0}; promote={1}; spcResponse= {2}; abortingBeforePSPE={3}; abortingAfterPSPE={4}; blockingBeforePSPE={5}; blockingAfterPSPE={6}; abortingAfterPromote={7}; blockingAfterPromote={8}", 1123 commit, 1124 promote, 1125 spcResponse, 1126 abortingBeforePSPE, 1127 abortingAfterPSPE, 1128 blockingBeforePSPE, 1129 blockingAfterPSPE, 1130 abortingAfterPromote, 1131 blockingAfterPromote); 1132 1133 Trace("**** " + testCaseDescription + " ****"); 1134 1135 // It doesn't make sense to have "AfterPromote" enlistments if we aren't going to promote the transaction. 1136 if (!promote) 1137 { 1138 if ((abortingAfterPromote > 0) || (blockingAfterPromote > 0)) 1139 { 1140 Trace("Not promoting - Resetting abortingAfterPromote and blockingAfterPromote to 0."); 1141 abortingAfterPromote = 0; 1142 blockingAfterPromote = 0; 1143 } 1144 } 1145 1146 AutoResetEvent completedEvent = new AutoResetEvent(false); 1147 1148 IPromotableSinglePhaseNotification enlistment = null; 1149 1150 int numClones = abortingBeforePSPE + abortingAfterPSPE + blockingBeforePSPE + blockingAfterPSPE + abortingAfterPromote + blockingAfterPromote; 1151 1152 1153 DependentTransaction[] clones = new DependentTransaction[numClones]; 1154 1155 try 1156 { 1157 using (TransactionScope ts = new TransactionScope()) 1158 { 1159 if (abortingBeforePSPE > 0) 1160 { 1161 for (int i = 0; i < abortingBeforePSPE; i++) 1162 { 1163 clones[i] = CreateDependentClone(/*blocking=*/false); 1164 } 1165 } 1166 1167 if (blockingBeforePSPE > 0) 1168 { 1169 for (int i = 0; i < blockingBeforePSPE; i++) 1170 { 1171 clones[abortingBeforePSPE + i] = CreateDependentClone(/*blocking=*/true); 1172 } 1173 } 1174 1175 enlistment = CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1176 NonMsdtcPromoterTests.PromotedToken1, 1177 completedEvent, 1178 /*nonMSDTC = */ true, 1179 /*tx = */ null, 1180 spcResponse, 1181 /*expectRejection=*/ false 1182 ); 1183 1184 if (abortingAfterPSPE > 0) 1185 { 1186 for (int i = 0; i < abortingAfterPSPE; i++) 1187 { 1188 clones[abortingBeforePSPE + blockingBeforePSPE + i] = CreateDependentClone(/*blocking=*/false); 1189 } 1190 } 1191 1192 if (blockingAfterPSPE > 0) 1193 { 1194 for (int i = 0; i < blockingAfterPSPE; i++) 1195 { 1196 clones[abortingBeforePSPE + blockingBeforePSPE + abortingAfterPSPE + i] = CreateDependentClone(/*blocking=*/true); 1197 } 1198 } 1199 1200 if (promote) 1201 { 1202 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1203 1204 if (abortingAfterPromote > 0) 1205 { 1206 for (int i = 0; i < abortingAfterPromote; i++) 1207 { 1208 clones[abortingBeforePSPE + blockingBeforePSPE + abortingAfterPSPE + blockingAfterPSPE + i] = CreateDependentClone(/*blocking=*/false); 1209 } 1210 } 1211 1212 if (blockingAfterPromote > 0) 1213 { 1214 for (int i = 0; i < blockingAfterPromote; i++) 1215 { 1216 clones[abortingBeforePSPE + blockingBeforePSPE + abortingAfterPSPE + blockingAfterPSPE + abortingAfterPromote + i] = CreateDependentClone(/*blocking=*/true); 1217 } 1218 } 1219 } 1220 1221 // Complete all the clones 1222 for (int i = 0; i < numClones; i++) 1223 { 1224 clones[i].Complete(); 1225 } 1226 1227 if (commit) 1228 { 1229 ts.Complete(); 1230 } 1231 } 1232 } 1233 catch (Exception ex) 1234 { 1235 TransactionAbortedException abortedEx = ex as TransactionAbortedException; 1236 if ((abortedEx != null) && (spcResponse != TransactionStatus.Aborted)) 1237 { 1238 Assert.Equal(spcResponse, TransactionStatus.Aborted); 1239 } 1240 1241 TransactionInDoubtException indoubtEx = ex as TransactionInDoubtException; 1242 if ((indoubtEx != null) && (spcResponse != TransactionStatus.InDoubt)) 1243 { 1244 Assert.Equal(spcResponse, TransactionStatus.InDoubt); 1245 } 1246 1247 Assert.NotEqual(spcResponse, TransactionStatus.Committed); 1248 } 1249 1250 TestPassed(); 1251 } 1252 TestCase_AbortFromVolatile(bool promote, EnlistmentOptions enlistmentOptions = EnlistmentOptions.None)1253 public static void TestCase_AbortFromVolatile(bool promote, EnlistmentOptions enlistmentOptions = EnlistmentOptions.None) 1254 { 1255 string testCaseDescription = string.Format( 1256 "TestCase_AbortFromVolatile promote={0}; enlistmentOptions = {1}", 1257 promote, 1258 enlistmentOptions.ToString() 1259 ); 1260 1261 Trace("**** " + testCaseDescription + " ****"); 1262 1263 AutoResetEvent volCompleted = new AutoResetEvent(false); 1264 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1265 MyEnlistment vol = null; 1266 NonMSDTCPromoterEnlistment pspe = null; 1267 1268 Assert.Throws<TransactionAbortedException>(() => 1269 { 1270 using (TransactionScope ts = new TransactionScope()) 1271 { 1272 vol = CreateVolatileEnlistment(volCompleted, null, enlistmentOptions, false); 1273 1274 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1275 NonMsdtcPromoterTests.PromotedToken1, 1276 pspeCompleted, 1277 /*nonMSDTC = */ true, 1278 /*tx = */ null, 1279 /*spcResponse=*/ TransactionStatus.Committed, 1280 /*expectRejection=*/ false 1281 ); 1282 1283 if (promote) 1284 { 1285 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1286 } 1287 1288 ts.Complete(); 1289 } 1290 }); 1291 1292 Assert.True(volCompleted.WaitOne(TimeSpan.FromSeconds(5)) && pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1293 1294 Assert.True(vol.AbortedOutcome); 1295 1296 if (promote) 1297 { 1298 Assert.True(pspe.Promoted); 1299 } 1300 else 1301 { 1302 Assert.False(pspe.Promoted); 1303 } 1304 1305 Assert.True(pspe.Aborted); 1306 1307 TestPassed(); 1308 } 1309 TestCase_AbortingCloneNotCompleted(bool promote)1310 public static void TestCase_AbortingCloneNotCompleted(bool promote) 1311 { 1312 string testCaseDescription = string.Format( 1313 "TestCase_AbortingCloneNotCompleted promote={0}", 1314 promote 1315 ); 1316 1317 Trace("**** " + testCaseDescription + " ****"); 1318 1319 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1320 NonMSDTCPromoterEnlistment pspe = null; 1321 1322 try 1323 { 1324 using (TransactionScope ts = new TransactionScope()) 1325 { 1326 CreateDependentClone(/*blocking=*/false); 1327 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1328 NonMsdtcPromoterTests.PromotedToken1, 1329 pspeCompleted, 1330 /*nonMSDTC = */ true, 1331 /*tx = */ null, 1332 /*spcResponse=*/ TransactionStatus.Committed, 1333 /*expectRejection=*/ false 1334 ); 1335 1336 if (promote) 1337 { 1338 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1339 } 1340 1341 ts.Complete(); 1342 } 1343 } 1344 catch (Exception ex) 1345 { 1346 Assert.IsType<TransactionAbortedException>(ex); 1347 } 1348 1349 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1350 1351 if (promote) 1352 { 1353 Assert.True(pspe.Promoted); 1354 } 1355 else 1356 { 1357 Assert.False(pspe.Promoted); 1358 } 1359 1360 Assert.True(pspe.Aborted); 1361 1362 TestPassed(); 1363 } 1364 TestCase_BlockingCloneCompletedAfterCommit(bool promote)1365 public static void TestCase_BlockingCloneCompletedAfterCommit(bool promote) 1366 { 1367 string testCaseDescription = string.Format( 1368 "TestCase_BlockingCloneCompletedAfterCommit promote={0}", 1369 promote 1370 ); 1371 1372 Trace("**** " + testCaseDescription + " ****"); 1373 1374 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1375 NonMSDTCPromoterEnlistment pspe = null; 1376 1377 NoStressTrace(string.Format("There will be a 2 second delay here - {0}", DateTime.Now.ToString())); 1378 1379 try 1380 { 1381 using (TransactionScope ts = new TransactionScope()) 1382 { 1383 DependentTransaction clone = CreateDependentClone(/*blocking=*/true); 1384 1385 Task.Run(() => CompleteDependentCloneThread(clone)); 1386 1387 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1388 NonMsdtcPromoterTests.PromotedToken1, 1389 pspeCompleted, 1390 /*nonMSDTC = */ true, 1391 /*tx = */ null, 1392 /*spcResponse=*/ TransactionStatus.Committed, 1393 /*expectRejection=*/ false 1394 ); 1395 1396 if (promote) 1397 { 1398 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1399 } 1400 1401 ts.Complete(); 1402 } 1403 } 1404 catch (Exception ex) 1405 { 1406 Assert.Null(ex); 1407 } 1408 1409 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(2))); 1410 1411 if (promote) 1412 { 1413 Assert.True(pspe.Promoted); 1414 } 1415 else 1416 { 1417 Assert.False(pspe.Promoted); 1418 } 1419 1420 Assert.False(pspe.Aborted); 1421 1422 TestPassed(true); 1423 } 1424 TestCase_TransactionTimeout(bool promote)1425 public static void TestCase_TransactionTimeout(bool promote) 1426 { 1427 string testCaseDescription = string.Format( 1428 "TestCase_TransactionTimeout promote={0}", 1429 promote 1430 ); 1431 1432 Trace("**** " + testCaseDescription + " ****"); 1433 1434 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1435 NonMSDTCPromoterEnlistment pspe = null; 1436 1437 Assert.Throws<TransactionAbortedException>(() => 1438 { 1439 CommittableTransaction tx = new CommittableTransaction(TimeSpan.FromSeconds(1)); 1440 1441 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1442 NonMsdtcPromoterTests.PromotedToken1, 1443 pspeCompleted, 1444 /*nonMSDTC = */ true, 1445 tx, 1446 /*spcResponse=*/ TransactionStatus.Committed, 1447 /*expectRejection=*/ false 1448 ); 1449 1450 if (promote) 1451 { 1452 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1, tx); 1453 } 1454 1455 NoStressTrace(string.Format("There will be a 3 second delay here - {0}", DateTime.Now.ToString())); 1456 1457 Task.Delay(TimeSpan.FromSeconds(3)).Wait(); 1458 1459 NoStressTrace(string.Format("Woke up from sleep. Attempting Commit - {0}", DateTime.Now.ToString())); 1460 1461 tx.Commit(); 1462 }); 1463 1464 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1465 1466 if (promote) 1467 { 1468 Assert.True(pspe.Promoted); 1469 } 1470 else 1471 { 1472 Assert.False(pspe.Promoted); 1473 } 1474 1475 Assert.True(pspe.Aborted); 1476 1477 TestPassed(true); 1478 } 1479 TestCase_EnlistDuringPrepare(bool promote, bool beforePromote, EnlistmentOptions firstOptions = EnlistmentOptions.None, EnlistmentOptions secondOptions = EnlistmentOptions.None, bool expectSecondEnlistSuccess = true )1480 public static void TestCase_EnlistDuringPrepare(bool promote, 1481 bool beforePromote, 1482 EnlistmentOptions firstOptions = EnlistmentOptions.None, 1483 EnlistmentOptions secondOptions = EnlistmentOptions.None, 1484 bool expectSecondEnlistSuccess = true 1485 ) 1486 { 1487 string testCaseDescription = string.Format( 1488 "TestCase_EnlistDuringPrepare promote={0}; beforePromote={1}, firstOptions={2}, secondOptions={3}, expectSecondEnlistSuccess={4}", 1489 promote, 1490 beforePromote, 1491 firstOptions.ToString(), 1492 secondOptions.ToString(), 1493 expectSecondEnlistSuccess 1494 ); 1495 1496 Trace("**** " + testCaseDescription + " ****"); 1497 1498 AutoResetEvent volCompleted = new AutoResetEvent(false); 1499 AutoResetEvent vol2Completed = new AutoResetEvent(false); 1500 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1501 MyEnlistment vol = null; 1502 NonMSDTCPromoterEnlistment pspe = null; 1503 1504 try 1505 { 1506 using (TransactionScope ts = new TransactionScope()) 1507 { 1508 if (beforePromote) 1509 { 1510 vol = new MyEnlistment( 1511 volCompleted, 1512 true, 1513 true, 1514 secondOptions, 1515 /*expectSuccessfulEnlist=*/ expectSecondEnlistSuccess, 1516 vol2Completed); 1517 vol.TransactionToEnlist = Transaction.Current; 1518 Transaction.Current.EnlistVolatile(vol, firstOptions); 1519 } 1520 1521 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1522 NonMsdtcPromoterTests.PromotedToken1, 1523 pspeCompleted, 1524 /*nonMSDTC = */ true, 1525 /*tx = */ null, 1526 /*spcResponse=*/ TransactionStatus.Committed, 1527 /*expectRejection=*/ false 1528 ); 1529 1530 if (promote) 1531 { 1532 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1533 1534 if (!beforePromote) 1535 { 1536 vol = new MyEnlistment( 1537 volCompleted, 1538 true, 1539 true, 1540 secondOptions, 1541 /*expectSuccessfulEnlist=*/ expectSecondEnlistSuccess, 1542 vol2Completed); 1543 vol.TransactionToEnlist = Transaction.Current; 1544 Transaction.Current.EnlistVolatile(vol, firstOptions); 1545 } 1546 } 1547 1548 ts.Complete(); 1549 } 1550 } 1551 catch (Exception ex) 1552 { 1553 Assert.Null(ex); 1554 } 1555 1556 if (!expectSecondEnlistSuccess) 1557 { 1558 vol2Completed.Set(); 1559 } 1560 1561 Assert.True(volCompleted.WaitOne(TimeSpan.FromSeconds(5)) && vol2Completed.WaitOne(TimeSpan.FromSeconds(5)) && 1562 pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1563 1564 Assert.False(vol.AbortedOutcome); 1565 1566 if (promote) 1567 { 1568 Assert.True(pspe.Promoted); 1569 } 1570 else 1571 { 1572 Assert.False(pspe.Promoted); 1573 } 1574 1575 Assert.False(pspe.Aborted); 1576 1577 TestPassed(); 1578 } 1579 TestCase_GetStatusAndDistributedId()1580 public static void TestCase_GetStatusAndDistributedId() 1581 { 1582 string testCaseDescription = "TestCase_GetDistributedId"; 1583 1584 Trace("**** " + testCaseDescription + " ****"); 1585 1586 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1587 NonMSDTCPromoterEnlistment pspe = null; 1588 1589 try 1590 { 1591 using (TransactionScope ts = new TransactionScope()) 1592 { 1593 TransactionStatus txStatus = Transaction.Current.TransactionInformation.Status; 1594 Assert.Equal(TransactionStatus.Active, txStatus); 1595 1596 Guid distId = Transaction.Current.TransactionInformation.DistributedIdentifier; 1597 Assert.Equal(Guid.Empty, distId); 1598 1599 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1600 NonMsdtcPromoterTests.PromotedToken1, 1601 pspeCompleted, 1602 /*nonMSDTC = */ true, 1603 /*tx = */ null, 1604 /*spcResponse=*/ TransactionStatus.Committed, 1605 /*expectRejection=*/ false 1606 ); 1607 1608 txStatus = Transaction.Current.TransactionInformation.Status; 1609 Assert.Equal(TransactionStatus.Active, txStatus); 1610 1611 distId = Transaction.Current.TransactionInformation.DistributedIdentifier; 1612 Assert.Equal(Guid.Empty, distId); 1613 1614 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1615 1616 txStatus = Transaction.Current.TransactionInformation.Status; 1617 Assert.Equal(TransactionStatus.Active, txStatus); 1618 1619 distId = Transaction.Current.TransactionInformation.DistributedIdentifier; 1620 Assert.NotEqual(distId, Guid.Empty); 1621 ts.Complete(); 1622 } 1623 1624 TestPassed(); 1625 } 1626 catch (Exception ex) 1627 { 1628 Assert.Null(ex); 1629 } 1630 } 1631 TestCase_DisposeCommittableTransaction(bool promote)1632 public static void TestCase_DisposeCommittableTransaction(bool promote) 1633 { 1634 string testCaseDescription = string.Format( 1635 "TestCase_DisposeCommittableTransaction promote={0}", 1636 promote 1637 ); 1638 1639 Trace("**** " + testCaseDescription + " ****"); 1640 1641 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1642 NonMSDTCPromoterEnlistment pspe = null; 1643 Transaction savedTransaction = null; 1644 1645 try 1646 { 1647 CommittableTransaction tx = new CommittableTransaction(TimeSpan.FromMinutes(1)); 1648 savedTransaction = tx.Clone(); 1649 1650 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1651 NonMsdtcPromoterTests.PromotedToken1, 1652 pspeCompleted, 1653 /*nonMSDTC = */ true, 1654 tx, 1655 /*spcResponse=*/ TransactionStatus.Committed, 1656 /*expectRejection=*/ false 1657 ); 1658 1659 if (promote) 1660 { 1661 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1, tx); 1662 } 1663 1664 tx.Dispose(); 1665 1666 tx.Commit(); 1667 } 1668 catch (Exception ex) 1669 { 1670 Assert.IsType<ObjectDisposedException>(ex); 1671 } 1672 1673 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1674 1675 if (promote) 1676 { 1677 Assert.True(pspe.Promoted); 1678 } 1679 else 1680 { 1681 Assert.False(pspe.Promoted); 1682 } 1683 1684 Assert.True(pspe.Aborted); 1685 1686 Assert.Equal(TransactionStatus.Aborted, savedTransaction.TransactionInformation.Status); 1687 1688 TestPassed(); 1689 } 1690 TestCase_OutcomeRegistration(bool promote)1691 public static void TestCase_OutcomeRegistration(bool promote) 1692 { 1693 string testCaseDescription = string.Format( 1694 "TestCase_OutcomeRegistration promote={0}", 1695 promote 1696 ); 1697 1698 Trace("**** " + testCaseDescription + " ****"); 1699 1700 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1701 NonMSDTCPromoterEnlistment pspe = null; 1702 int numberOfCompletions = 0; 1703 CommittableTransaction tx = null; 1704 1705 try 1706 { 1707 tx = new CommittableTransaction(TimeSpan.FromSeconds(5)); 1708 1709 tx.TransactionCompleted += delegate (object sender, TransactionEventArgs completedArgs) 1710 { 1711 Trace("Completed event registered before PSPE"); 1712 numberOfCompletions++; 1713 Assert.Equal(TransactionStatus.Committed, completedArgs.Transaction.TransactionInformation.Status); 1714 }; 1715 1716 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1717 NonMsdtcPromoterTests.PromotedToken1, 1718 pspeCompleted, 1719 /*nonMSDTC = */ true, 1720 tx, 1721 /*spcResponse=*/ TransactionStatus.Committed, 1722 /*expectRejection=*/ false 1723 ); 1724 1725 tx.TransactionCompleted += delegate (object sender, TransactionEventArgs completedArgs) 1726 { 1727 Trace("Completed event registered after PSPE"); 1728 numberOfCompletions++; 1729 Assert.Equal(TransactionStatus.Committed, completedArgs.Transaction.TransactionInformation.Status); 1730 }; 1731 1732 if (promote) 1733 { 1734 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1, tx); 1735 1736 tx.TransactionCompleted += delegate (object sender, TransactionEventArgs completedArgs) 1737 { 1738 Trace("Completed event registered after promote"); 1739 numberOfCompletions++; 1740 Assert.Equal(TransactionStatus.Committed, completedArgs.Transaction.TransactionInformation.Status); 1741 }; 1742 } 1743 1744 tx.Commit(); 1745 } 1746 catch (Exception ex) 1747 { 1748 Assert.Null(ex); 1749 } 1750 1751 tx.TransactionCompleted += delegate (object sender, TransactionEventArgs completedArgs) 1752 { 1753 Trace("Completed event registered after commit"); 1754 numberOfCompletions++; 1755 Assert.Equal(TransactionStatus.Committed, completedArgs.Transaction.TransactionInformation.Status); 1756 }; 1757 1758 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1759 1760 if (promote) 1761 { 1762 Assert.True(pspe.Promoted); 1763 } 1764 else 1765 { 1766 Assert.False(pspe.Promoted); 1767 } 1768 1769 Assert.Equal((promote ? 4 : 3), numberOfCompletions); 1770 1771 TestPassed(); 1772 } 1773 TestCase_PromoterType()1774 public static void TestCase_PromoterType() 1775 { 1776 string testCaseDescription = "TestCase_PromoterType"; 1777 1778 Trace("**** " + testCaseDescription + " ****"); 1779 1780 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1781 NonMSDTCPromoterEnlistment pspe = null; 1782 1783 try 1784 { 1785 using (TransactionScope ts = new TransactionScope()) 1786 { 1787 Assert.Equal(Guid.Empty, TxPromoterType(Transaction.Current)); 1788 1789 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1790 NonMsdtcPromoterTests.PromotedToken1, 1791 pspeCompleted, 1792 /*nonMSDTC = */ true, 1793 /*tx = */ null, 1794 /*spcResponse=*/ TransactionStatus.Committed, 1795 /*expectRejection=*/ false 1796 ); 1797 1798 Assert.Equal(NonMsdtcPromoterTests.PromoterType1, TxPromoterType(Transaction.Current)); 1799 1800 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1801 1802 Assert.Equal(NonMsdtcPromoterTests.PromoterType1, TxPromoterType(Transaction.Current)); 1803 1804 ts.Complete(); 1805 } 1806 } 1807 catch (Exception ex) 1808 { 1809 Assert.Null(ex); 1810 } 1811 1812 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1813 1814 Assert.True(pspe.Promoted); 1815 1816 TestPassed(); 1817 } 1818 TestCase_PromoterTypeMSDTC()1819 public static void TestCase_PromoterTypeMSDTC() 1820 { 1821 string testCaseDescription = "TestCase_PromoterTypeMSDTC"; 1822 1823 Trace("**** " + testCaseDescription + " ****"); 1824 1825 AutoResetEvent volCompleted = new AutoResetEvent(false); 1826 MyEnlistment vol = null; 1827 1828 try 1829 { 1830 using (TransactionScope ts = new TransactionScope()) 1831 { 1832 Assert.Equal(Guid.Empty, TxPromoterType(Transaction.Current)); 1833 1834 vol = CreateVolatileEnlistment(volCompleted); 1835 1836 // Force MSDTC promotion. 1837 TransactionInterop.GetDtcTransaction(Transaction.Current); 1838 1839 // TransactionInterop.PromoterTypeDtc 1840 Assert.Equal(PromoterTypeDtc, TxPromoterType(Transaction.Current)); 1841 1842 ts.Complete(); 1843 } 1844 } 1845 catch (Exception ex) 1846 { 1847 Trace(string.Format("Caught unexpected exception {0}:{1}", ex.GetType().ToString(), ex.ToString())); 1848 return; 1849 } 1850 1851 Assert.True(volCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1852 1853 Assert.True(vol.CommittedOutcome); 1854 1855 TestPassed(); 1856 } 1857 TestCase_FailPromotableSinglePhaseNotificationCalls()1858 public static void TestCase_FailPromotableSinglePhaseNotificationCalls() 1859 { 1860 string testCaseDescription = "TestCase_FailPromotableSinglePhaseNotificationCalls"; 1861 1862 Trace("**** " + testCaseDescription + " ****"); 1863 1864 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1865 NonMSDTCPromoterEnlistment pspe = null; 1866 1867 Trace("Fail Initialize"); 1868 try 1869 { 1870 using (TransactionScope ts = new TransactionScope()) 1871 { 1872 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1873 NonMsdtcPromoterTests.PromotedToken1, 1874 pspeCompleted, 1875 /*nonMSDTC = */ true, 1876 /*tx = */ null, 1877 /*spcResponse=*/ TransactionStatus.Committed, 1878 /*expectRejection=*/ false, 1879 /*comparePromotedToken=*/ false, 1880 /*failInitialize=*/ true, 1881 /*failPromote=*/ false, 1882 /*failSPC=*/ false, 1883 /*failGetPromoterType=*/ false, 1884 /*failGetId=*/ false 1885 ); 1886 bool shouldNotBeExecuted = true; 1887 Assert.False(shouldNotBeExecuted); 1888 } 1889 } 1890 catch (Exception ex) 1891 { 1892 Assert.True(ex is ApplicationException || (ex is TargetInvocationException && ex.InnerException is ApplicationException)); 1893 } 1894 1895 Trace("Fail Promote"); 1896 try 1897 { 1898 using (TransactionScope ts = new TransactionScope()) 1899 { 1900 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1901 NonMsdtcPromoterTests.PromotedToken1, 1902 pspeCompleted, 1903 /*nonMSDTC = */ true, 1904 /*tx = */ null, 1905 /*spcResponse=*/ TransactionStatus.Committed, 1906 /*expectRejection=*/ false, 1907 /*comparePromotedToken=*/ false, 1908 /*failInitialize=*/ false, 1909 /*failPromote=*/ true, 1910 /*failSPC=*/ false, 1911 /*failGetPromoterType=*/ false, 1912 /*failGetId=*/ false 1913 ); 1914 1915 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1916 ts.Complete(); 1917 } 1918 } 1919 catch (Exception ex) 1920 { 1921 Assert.True(ex is ApplicationException || (ex is TargetInvocationException && ex.InnerException is ApplicationException)); 1922 } 1923 1924 // The NonMSDTCPromoterEnlistment is coded to set "Promoted" at the beginning of Promote, before 1925 // throwing. 1926 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1927 1928 Assert.True(pspe.Promoted); 1929 1930 Trace("Fail SinglePhaseCommit"); 1931 try 1932 { 1933 using (TransactionScope ts = new TransactionScope()) 1934 { 1935 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1936 NonMsdtcPromoterTests.PromotedToken1, 1937 pspeCompleted, 1938 /*nonMSDTC = */ true, 1939 /*tx = */ null, 1940 /*spcResponse=*/ TransactionStatus.Committed, 1941 /*expectRejection=*/ false, 1942 /*comparePromotedToken=*/ false, 1943 /*failInitialize=*/ false, 1944 /*failPromote=*/ false, 1945 /*failSPC=*/ true, 1946 /*failGetPromoterType=*/ false, 1947 /*failGetId=*/ false 1948 ); 1949 1950 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 1951 ts.Complete(); 1952 } 1953 } 1954 catch (Exception ex) 1955 { 1956 Assert.True(ex is ApplicationException || (ex is TargetInvocationException && ex.InnerException is ApplicationException)); 1957 } 1958 1959 // The NonMSDTCPromoterEnlistment is coded to set "Promoted" at the beginning of Promote, before 1960 // throwing. 1961 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 1962 1963 Assert.True(pspe.Promoted); 1964 1965 TestPassed(); 1966 } 1967 TestCase_SetDistributedIdAtWrongTime()1968 public static void TestCase_SetDistributedIdAtWrongTime() 1969 { 1970 string testCaseDescription = "TestCase_SetDistributedIdAtWrongTime"; 1971 1972 Trace("**** " + testCaseDescription + " ****"); 1973 1974 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 1975 NonMSDTCPromoterEnlistment pspe = null; 1976 NonMSDTCPromoterEnlistment dummyPSPE = new NonMSDTCPromoterEnlistment(NonMsdtcPromoterTests.PromoterType1, NonMsdtcPromoterTests.PromotedToken1, pspeCompleted); 1977 1978 Guid guidToSet = new Guid("236BC646-FE3B-41F9-99F7-08BF448D8420"); 1979 1980 using (TransactionScope ts = new TransactionScope()) 1981 { 1982 Trace("Before EnlistPromotable"); 1983 Exception ex = Assert.ThrowsAny<Exception>(() => SetDistributedTransactionId(dummyPSPE, Transaction.Current, guidToSet)); 1984 Assert.True(ex is TransactionException || (ex is TargetInvocationException && ex.InnerException is TransactionException)); 1985 1986 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 1987 NonMsdtcPromoterTests.PromotedToken1, 1988 pspeCompleted, 1989 /*nonMSDTC = */ true, 1990 /*tx = */ null, 1991 /*spcResponse=*/ TransactionStatus.Committed, 1992 /*expectRejection=*/ false, 1993 /*comparePromotedToken=*/ false, 1994 /*failInitialize=*/ false, 1995 /*failPromote=*/ false, 1996 /*failSPC=*/ false, 1997 /*failGetPromoterType=*/ false, 1998 /*failGetId=*/ false 1999 ); 2000 2001 Trace("After EnlistPromotable"); 2002 ex = Assert.ThrowsAny<Exception>(() => SetDistributedTransactionId(dummyPSPE, Transaction.Current, guidToSet)); 2003 Assert.True(ex is TransactionException || (ex is TargetInvocationException && ex.InnerException is TransactionException)); 2004 2005 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 2006 2007 Trace("After Promotion"); 2008 ex = Assert.ThrowsAny<Exception>(() => SetDistributedTransactionId(dummyPSPE, Transaction.Current, guidToSet)); 2009 Assert.True(ex is TransactionException || (ex is TargetInvocationException && ex.InnerException is TransactionException)); 2010 2011 ts.Complete(); 2012 } 2013 2014 // The NonMSDTCPromoterEnlistment is coded to set "Promoted" at the beginning of Promote, before 2015 // throwing. 2016 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 2017 2018 Assert.True(pspe.Promoted); 2019 2020 TestPassed(); 2021 } 2022 TestCase_SetDistributedIdWithWrongNotificationObject()2023 public static void TestCase_SetDistributedIdWithWrongNotificationObject() 2024 { 2025 string testCaseDescription = "TestCase_SetDistributedIdWithWrongNotificationObject"; 2026 2027 Trace("**** " + testCaseDescription + " ****"); 2028 2029 AutoResetEvent pspeCompleted = new AutoResetEvent(false); 2030 NonMSDTCPromoterEnlistment pspe = null; 2031 2032 Guid guidToSet = new Guid("236BC646-FE3B-41F9-99F7-08BF448D8420"); 2033 2034 try 2035 { 2036 using (TransactionScope ts = new TransactionScope()) 2037 { 2038 pspe = (NonMSDTCPromoterEnlistment)CreatePSPEEnlistment(NonMsdtcPromoterTests.PromoterType1, 2039 NonMsdtcPromoterTests.PromotedToken1, 2040 pspeCompleted, 2041 /*nonMSDTC = */ true, 2042 /*tx = */ null, 2043 /*spcResponse=*/ TransactionStatus.Committed, 2044 /*expectRejection=*/ false, 2045 /*comparePromotedToken=*/ false, 2046 /*failInitialize=*/ false, 2047 /*failPromote=*/ false, 2048 /*failSPC=*/ false, 2049 /*failGetPromoterType=*/ false, 2050 /*failGetId=*/ false, 2051 /*incorrectNotificationObjectToSetDistributedTransactionId=*/ true 2052 ); 2053 2054 Promote(testCaseDescription, NonMsdtcPromoterTests.PromotedToken1); 2055 2056 ts.Complete(); 2057 } 2058 } 2059 catch (Exception ex) 2060 { 2061 Assert.Null(ex); 2062 } 2063 2064 // The NonMSDTCPromoterEnlistment is coded to set "Promoted" at the beginning of Promote, before 2065 // throwing. 2066 Assert.True(pspeCompleted.WaitOne(TimeSpan.FromSeconds(5))); 2067 2068 Assert.True(pspe.Promoted); 2069 2070 TestPassed(); 2071 } 2072 2073 #endregion 2074 2075 2076 /// <summary> 2077 /// This test case is very basic Volatile Enlistment test. 2078 /// </summary> 2079 [Fact] VolatileEnlistments()2080 public void VolatileEnlistments() 2081 { 2082 TestCase_VolatileEnlistments(1, TransactionStatus.Committed); 2083 TestCase_VolatileEnlistments(5, TransactionStatus.Committed); 2084 TestCase_VolatileEnlistments(1, TransactionStatus.Aborted, EnlistmentOptions.None, false); 2085 TestCase_VolatileEnlistments(5, TransactionStatus.Aborted, EnlistmentOptions.None, false); 2086 TestCase_VolatileEnlistments(1, TransactionStatus.Aborted, EnlistmentOptions.None, true, false, typeof(TransactionAbortedException)); 2087 } 2088 2089 /// <summary> 2090 /// Tests PSPE Non-MSDTC with volatile enlistments. 2091 /// </summary> 2092 [Theory] 2093 [InlineData(true, false, TransactionStatus.Committed)] 2094 [InlineData(true, true, TransactionStatus.Committed)] 2095 [InlineData(false, false, TransactionStatus.Committed)] 2096 [InlineData(false, true, TransactionStatus.Committed)] 2097 [InlineData(true, false, TransactionStatus.Aborted)] 2098 [InlineData(true, true, TransactionStatus.Aborted)] 2099 [InlineData(true, false, TransactionStatus.InDoubt)] 2100 [InlineData(true, true, TransactionStatus.InDoubt)] PSPENonMSDTCWithVolatileEnlistments(bool commit, bool promote, TransactionStatus spcResponse)2101 public void PSPENonMSDTCWithVolatileEnlistments(bool commit, bool promote, TransactionStatus spcResponse) 2102 { 2103 TestCase_PSPENonMsdtc(commit, promote, spcResponse); 2104 TestCase_PSPENonMsdtc(commit, promote, spcResponse, 1); 2105 TestCase_PSPENonMsdtc(commit, promote, spcResponse, 1, 1); 2106 TestCase_PSPENonMsdtc(commit, promote, spcResponse, 1, 1, 1); 2107 TestCase_PSPENonMsdtc(commit, promote, spcResponse, 1, 1, 1, 1); 2108 } 2109 2110 /// <summary> 2111 /// Tests PSPE Non-MSDTC with dependent clones. 2112 /// </summary> 2113 [Theory] 2114 [InlineData(true, false, TransactionStatus.Committed)] 2115 [InlineData(true, true, TransactionStatus.Committed)] 2116 [InlineData(false, false, TransactionStatus.Committed)] 2117 [InlineData(false, true, TransactionStatus.Committed)] 2118 [InlineData(true, false, TransactionStatus.Aborted)] 2119 [InlineData(true, true, TransactionStatus.Aborted)] 2120 [InlineData(true, false, TransactionStatus.InDoubt)] 2121 [InlineData(true, true, TransactionStatus.InDoubt)] PSPENonMSDTCWithDependentClones(bool commit, bool promote, TransactionStatus spcResponse)2122 public void PSPENonMSDTCWithDependentClones(bool commit, bool promote, TransactionStatus spcResponse) 2123 { 2124 TestCase_PSPENonMsdtcWithClones(commit, promote, spcResponse); 2125 TestCase_PSPENonMsdtcWithClones(commit, promote, spcResponse, 1); 2126 TestCase_PSPENonMsdtcWithClones(commit, promote, spcResponse, 1, 1); 2127 TestCase_PSPENonMsdtcWithClones(commit, promote, spcResponse, 1, 1, 1); 2128 TestCase_PSPENonMsdtcWithClones(commit, promote, spcResponse, 1, 1, 1, 1); 2129 } 2130 2131 /// <summary> 2132 /// PSPE Non-MSDTC Abort From Volatile. 2133 /// </summary> 2134 [Theory] 2135 [InlineData(false, EnlistmentOptions.EnlistDuringPrepareRequired)] 2136 [InlineData(true, EnlistmentOptions.EnlistDuringPrepareRequired)] 2137 [InlineData(false, EnlistmentOptions.None)] 2138 [InlineData(true, EnlistmentOptions.None)] PSPENonMsdtcAbortFromVolatile(bool promote, EnlistmentOptions options)2139 public void PSPENonMsdtcAbortFromVolatile(bool promote, EnlistmentOptions options) 2140 { 2141 // Abort from p0 and p1 volatile, not promoted and promoted. 2142 TestCase_AbortFromVolatile(promote, options); 2143 } 2144 2145 /// <summary> 2146 /// PSPE Non-MSDTC Aborting Clone Not Completed. 2147 /// </summary> 2148 [Theory] 2149 [InlineData(false)] 2150 [InlineData(true)] PSPENonMsdtcAbortingCloneNotCompleted(bool promote)2151 public void PSPENonMsdtcAbortingCloneNotCompleted(bool promote) 2152 { 2153 // Aborting clone that isn't completed 2154 TestCase_AbortingCloneNotCompleted(promote); 2155 } 2156 2157 /// <summary> 2158 /// PSPE Non-MSDTC Blocking Clone Completed After Commit. 2159 /// </summary> 2160 [OuterLoop] // long delay 2161 [Theory] 2162 [InlineData(false)] 2163 [InlineData(true)] PSPENonMsdtcBlockingCloneCompletedAfterCommit(bool promote)2164 public void PSPENonMsdtcBlockingCloneCompletedAfterCommit(bool promote) 2165 { 2166 // Blocking clone that isn't completed before commit 2167 TestCase_BlockingCloneCompletedAfterCommit(promote); 2168 } 2169 2170 /// <summary> 2171 /// PSPE Non-MSDTC Timeout. 2172 /// </summary> 2173 [OuterLoop] // long timeout 2174 [Theory] 2175 [InlineData(false)] 2176 [InlineData(true)] PSPENonMsdtcTimeout(bool promote)2177 public void PSPENonMsdtcTimeout(bool promote) 2178 { 2179 // tx timeout 2180 TestCase_TransactionTimeout(promote); 2181 } 2182 2183 /// <summary> 2184 /// PSPE Non-MSDTC Enlist During Phase 0. 2185 /// </summary> 2186 [Theory] 2187 [InlineData(false, true, EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.EnlistDuringPrepareRequired, true)] 2188 [InlineData(false, true, EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.None, true)] 2189 [InlineData(true, true, EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.EnlistDuringPrepareRequired, true)] 2190 [InlineData(true, true, EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.None, true)] 2191 [InlineData(true, false, EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.EnlistDuringPrepareRequired, true)] 2192 [InlineData(true, false, EnlistmentOptions.EnlistDuringPrepareRequired, EnlistmentOptions.EnlistDuringPrepareRequired, true)] 2193 [InlineData(false, true, EnlistmentOptions.None, EnlistmentOptions.None, false)] 2194 [InlineData(false, true, EnlistmentOptions.None, EnlistmentOptions.None, false)] 2195 [InlineData(true, true, EnlistmentOptions.None, EnlistmentOptions.EnlistDuringPrepareRequired, false)] 2196 [InlineData(true, true, EnlistmentOptions.None, EnlistmentOptions.None, false)] 2197 [InlineData(true, false, EnlistmentOptions.None, EnlistmentOptions.EnlistDuringPrepareRequired, false)] 2198 [InlineData(true, false, EnlistmentOptions.None, EnlistmentOptions.None, false)] PSPENonMsdtcEnlistDuringPhase0( bool promote, bool beforePromote, EnlistmentOptions options, EnlistmentOptions secondOptions, bool expectSecondEnlistSuccess)2199 public void PSPENonMsdtcEnlistDuringPhase0( 2200 bool promote, bool beforePromote, EnlistmentOptions options, EnlistmentOptions secondOptions, bool expectSecondEnlistSuccess) 2201 { 2202 TestCase_EnlistDuringPrepare(promote, beforePromote, options, secondOptions, expectSecondEnlistSuccess); 2203 } 2204 2205 /// <summary> 2206 /// PSPE Non-MSDTC Get Status and Distributed Id. 2207 /// </summary> 2208 [Fact] PSPENonMsdtcGetStatusAndDistributedId()2209 public void PSPENonMsdtcGetStatusAndDistributedId() 2210 { 2211 // Retrieve the DistributedId before PSPE, before Promote, and after Promote 2212 TestCase_GetStatusAndDistributedId(); 2213 } 2214 2215 /// <summary> 2216 /// PSPE Non-MSDTC Dispose Committable Transaction. 2217 /// </summary> 2218 [Theory] 2219 [InlineData(false)] 2220 [InlineData(true)] PSPENonMsdtcDisposeCommittable(bool promote)2221 public void PSPENonMsdtcDisposeCommittable(bool promote) 2222 { 2223 // Dispose a committable transaction early. 2224 TestCase_DisposeCommittableTransaction(promote); 2225 } 2226 2227 /// <summary> 2228 /// PSPE Non-MSDTC Completed Event. 2229 /// </summary> 2230 [Theory] 2231 [InlineData(false)] 2232 [InlineData(true)] PSPENonMsdtcCompletedEvent(bool promote)2233 public void PSPENonMsdtcCompletedEvent(bool promote) 2234 { 2235 // Registration for completed event 2236 TestCase_OutcomeRegistration(promote); 2237 } 2238 2239 /// <summary> 2240 /// PSPE Non-MSDTC Get PromoterType. 2241 /// </summary> 2242 [Fact] PSPENonMsdtcGetPromoterType()2243 public void PSPENonMsdtcGetPromoterType() 2244 { 2245 // get_PromoterType 2246 TestCase_PromoterType(); 2247 } 2248 2249 /// <summary> 2250 /// PSPE Non-MSDTC Get PromoterType. 2251 /// </summary> 2252 [Fact] PSPENonMsdtcGetPromoterTypeMSDTC()2253 public void PSPENonMsdtcGetPromoterTypeMSDTC() 2254 { 2255 // get_PromoterType 2256 TestCase_PromoterTypeMSDTC(); 2257 } 2258 2259 /// <summary> 2260 /// PSPE Non-MSDTC Fail PromotableSinglePhaseNotification Calls. 2261 /// </summary> 2262 [Fact] PSPENonMsdtcFailPromotableSinglePhaseNotificationCalls()2263 public void PSPENonMsdtcFailPromotableSinglePhaseNotificationCalls() 2264 { 2265 // Fail calls to the non-MSDTC Promotable Enlistment 2266 TestCase_FailPromotableSinglePhaseNotificationCalls(); 2267 } 2268 2269 /// <summary> 2270 /// Make SetDistributedTransactionIdentifier calls at the wrong time - negative test. 2271 /// </summary> 2272 [Fact] PSPENonMsdtcInCorrectSetDistributedTransactionIdentifierCalls()2273 public void PSPENonMsdtcInCorrectSetDistributedTransactionIdentifierCalls() 2274 { 2275 // Call SetDistributedTransactionIdentifier at the wrong time. 2276 TestCase_SetDistributedIdAtWrongTime(); 2277 } 2278 2279 /// <summary> 2280 /// Call SetDistributedTransactionIdentifier with incorrect notification object - negative test. 2281 /// </summary> 2282 [Fact] PSPENonMsdtcSetDistributedTransactionIdentifierCallWithWrongNotificationObject()2283 public void PSPENonMsdtcSetDistributedTransactionIdentifierCallWithWrongNotificationObject() 2284 { 2285 // Call SetDistributedTransactionIdentifier at the wrong time. 2286 TestCase_SetDistributedIdWithWrongNotificationObject(); 2287 } 2288 2289 [Fact] 2290 [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Expects PNSE due to not being supported on core")] SimpleTransactionSuperior()2291 public void SimpleTransactionSuperior() 2292 { 2293 MySimpleTransactionSuperior superior = new MySimpleTransactionSuperior(); 2294 SubordinateTransaction subTx = new SubordinateTransaction(IsolationLevel.Serializable, superior); 2295 2296 AutoResetEvent durableCompleted = new AutoResetEvent(false); 2297 MyEnlistment durable = null; 2298 2299 durable = new MyEnlistment( 2300 durableCompleted, 2301 true, 2302 false, 2303 EnlistmentOptions.None, 2304 /*expectSuccessfulEnlist=*/ false, 2305 /*secondEnlistmentCompleted=*/ null); 2306 durable.TransactionToEnlist = Transaction.Current; 2307 2308 Assert.Throws<PlatformNotSupportedException>(() => // SubordinateTransaction promotes to MSDTC 2309 { 2310 subTx.EnlistDurable(Guid.NewGuid(), durable, EnlistmentOptions.None); 2311 }); 2312 } 2313 } 2314 } 2315