| | 1 | | // Copyright (c) Microsoft Corporation. All rights reserved. |
| | 2 | | // Licensed under the MIT License. |
| | 3 | |
|
| | 4 | | using System.Collections.Generic; |
| | 5 | | using System.Diagnostics; |
| | 6 | | using System.IO; |
| | 7 | | using System.Linq; |
| | 8 | | using NUnit.Framework; |
| | 9 | |
|
| | 10 | | namespace Azure.Core.TestFramework |
| | 11 | | { |
| | 12 | | [Category("Recorded")] |
| | 13 | | public abstract class RecordedTestBase : ClientTestBase |
| | 14 | | { |
| 10513 | 15 | | protected RecordedTestSanitizer Sanitizer { get; set; } |
| | 16 | |
|
| 10533 | 17 | | protected RecordMatcher Matcher { get; set; } |
| | 18 | |
|
| 134019 | 19 | | public TestRecording Recording { get; private set; } |
| | 20 | |
|
| 87165 | 21 | | public RecordedTestMode Mode { get; } |
| | 22 | |
|
| | 23 | | // copied the Windows version https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib |
| | 24 | | // as it is the most restrictive of all platforms |
| 64 | 25 | | private static readonly HashSet<char> s_invalidChars = new HashSet<char>(new char[] |
| 64 | 26 | | { |
| 64 | 27 | | '\"', '<', '>', '|', '\0', |
| 64 | 28 | | (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, |
| 64 | 29 | | (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, |
| 64 | 30 | | (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, |
| 64 | 31 | | (char)31, ':', '*', '?', '\\', '/' |
| 64 | 32 | | }); |
| | 33 | |
|
| | 34 | | #if DEBUG |
| | 35 | | /// <summary> |
| | 36 | | /// Flag you can (temporarily) enable to save failed test recordings |
| | 37 | | /// and debug/re-run at the point of failure without re-running |
| | 38 | | /// potentially lengthy live tests. This should never be checked in |
| | 39 | | /// and will be compiled out of release builds to help make that easier |
| | 40 | | /// to spot. |
| | 41 | | /// </summary> |
| | 42 | | public bool SaveDebugRecordingsOnFailure { get; set; } = false; |
| | 43 | | #endif |
| | 44 | |
|
| 544 | 45 | | protected RecordedTestBase(bool isAsync) : this(isAsync, RecordedTestUtilities.GetModeFromEnvironment()) |
| | 46 | | { |
| 544 | 47 | | } |
| | 48 | |
|
| 978 | 49 | | protected RecordedTestBase(bool isAsync, RecordedTestMode mode) : base(isAsync) |
| | 50 | | { |
| 978 | 51 | | Sanitizer = new RecordedTestSanitizer(); |
| 978 | 52 | | Matcher = new RecordMatcher(); |
| 978 | 53 | | Mode = mode; |
| 978 | 54 | | } |
| | 55 | |
|
| | 56 | | private string GetSessionFilePath() |
| | 57 | | { |
| 9109 | 58 | | TestContext.TestAdapter testAdapter = TestContext.CurrentContext.Test; |
| | 59 | |
|
| 283257 | 60 | | string name = new string(testAdapter.Name.Select(c => s_invalidChars.Contains(c) ? '%' : c).ToArray()); |
| 9109 | 61 | | string additionalParameterName = testAdapter.Properties.ContainsKey(ClientTestFixtureAttribute.RecordingDire |
| 9109 | 62 | | testAdapter.Properties.Get(ClientTestFixtureAttribute.RecordingDirectorySuffixKey).ToString() : |
| 9109 | 63 | | null; |
| | 64 | |
|
| 9109 | 65 | | string className = testAdapter.ClassName.Substring(testAdapter.ClassName.LastIndexOf('.') + 1); |
| | 66 | |
|
| 9109 | 67 | | string fileName = name + (IsAsync ? "Async" : string.Empty) + ".json"; |
| 9109 | 68 | | return Path.Combine(TestContext.CurrentContext.TestDirectory, |
| 9109 | 69 | | "SessionRecords", |
| 9109 | 70 | | additionalParameterName == null ? className : $"{className}({additionalParameterName})", |
| 9109 | 71 | | fileName); |
| | 72 | | } |
| | 73 | |
|
| | 74 | | /// <summary> |
| | 75 | | /// Add a static TestEventListener which will redirect SDK logging |
| | 76 | | /// to Console.Out for easy debugging. |
| | 77 | | /// </summary> |
| 1892 | 78 | | private static TestLogger Logger { get; set; } |
| | 79 | |
|
| | 80 | | /// <summary> |
| | 81 | | /// Start logging events to the console if debugging or in Live mode. |
| | 82 | | /// This will run once before any tests. |
| | 83 | | /// </summary> |
| | 84 | | [OneTimeSetUp] |
| | 85 | | public void StartLoggingEvents() |
| | 86 | | { |
| 946 | 87 | | if (Mode == RecordedTestMode.Live || Debugger.IsAttached) |
| | 88 | | { |
| 0 | 89 | | Logger = new TestLogger(); |
| | 90 | | } |
| 946 | 91 | | } |
| | 92 | |
|
| | 93 | | /// <summary> |
| | 94 | | /// Stop logging events and do necessary cleanup. |
| | 95 | | /// This will run once after all tests have finished. |
| | 96 | | /// </summary> |
| | 97 | | [OneTimeTearDown] |
| | 98 | | public void StopLoggingEvents() |
| | 99 | | { |
| 946 | 100 | | Logger?.Dispose(); |
| 946 | 101 | | Logger = null; |
| 946 | 102 | | } |
| | 103 | |
|
| | 104 | | [SetUp] |
| | 105 | | public virtual void StartTestRecording() |
| | 106 | | { |
| | 107 | | // Only create test recordings for the latest version of the service |
| 17979 | 108 | | TestContext.TestAdapter test = TestContext.CurrentContext.Test; |
| 17979 | 109 | | if (Mode != RecordedTestMode.Live && |
| 17979 | 110 | | test.Properties.ContainsKey("SkipRecordings")) |
| | 111 | | { |
| 8870 | 112 | | throw new IgnoreException((string) test.Properties.Get("SkipRecordings")); |
| | 113 | | } |
| 9109 | 114 | | Recording = new TestRecording(Mode, GetSessionFilePath(), Sanitizer, Matcher); |
| 9109 | 115 | | } |
| | 116 | |
|
| | 117 | | [TearDown] |
| | 118 | | public virtual void StopTestRecording() |
| | 119 | | { |
| 17979 | 120 | | bool save = TestContext.CurrentContext.Result.FailCount == 0; |
| | 121 | | #if DEBUG |
| | 122 | | save |= SaveDebugRecordingsOnFailure; |
| | 123 | | #endif |
| 17979 | 124 | | Recording?.Dispose(save); |
| 9109 | 125 | | } |
| | 126 | | } |
| | 127 | | } |