< Summary

Class:Azure.Core.TestFramework.TestRecording
Assembly:Azure.Core.TestFramework
File(s):C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.TestFramework\src\TestRecording.cs
Covered lines:89
Uncovered lines:26
Coverable lines:115
Total lines:327
Line coverage:77.3% (89 of 115)
Covered branches:33
Total branches:54
Branch coverage:61.1% (33 of 54)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-93.75%66.67%
get_Mode()-100%100%
get_Random()-68.18%57.14%
get_Now()-45.45%50%
get_UtcNow()-100%100%
Load()-100%100%
Dispose(...)-92.86%75%
Dispose()-0%100%
InstrumentClientOptions(...)-100%100%
CreateTransport(...)-75%25%
GenerateId()-100%100%
GenerateAlphaNumericId(...)-88.89%66.67%
GenerateId(...)-100%100%
GenerateAssetName(...)-66.67%75%
IsTrack1SessionRecord()-100%100%
GetVariable(...)-42.86%25%
SetVariable(...)-100%100%
DisableIdReuse()-100%100%
DisableRecording()-0%100%
DisableRequestBodyRecording()-100%100%
.ctor(...)-100%100%
Dispose()-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.TestFramework\src\TestRecording.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.IO;
 6using System.Linq;
 7using System.Runtime.CompilerServices;
 8using System.Security.Cryptography;
 9using System.Text.Json;
 10using System.Threading;
 11using System.Threading.Tasks;
 12using Azure.Core.Pipeline;
 13using Azure.Core.Tests.TestFramework;
 14
 15namespace Azure.Core.TestFramework
 16{
 17    public class TestRecording : IDisposable
 18    {
 19        private const string RandomSeedVariableKey = "RandomSeed";
 20        private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 21        private const string charsLower = "abcdefghijklmnopqrstuvwxyz0123456789";
 22        internal const string DateTimeOffsetNowVariableKey = "DateTimeOffsetNow";
 23
 911324        public TestRecording(RecordedTestMode mode, string sessionFile, RecordedTestSanitizer sanitizer, RecordMatcher m
 25        {
 911326            Mode = mode;
 911327            _sessionFile = sessionFile;
 911328            _sanitizer = sanitizer;
 911329            _matcher = matcher;
 30
 911331            switch (Mode)
 32            {
 33                case RecordedTestMode.Record:
 434                    _session = new RecordSession();
 435                    if (File.Exists(_sessionFile))
 36                    {
 37                        try
 38                        {
 439                            _previousSession = Load();
 040                        }
 441                        catch (Exception)
 42                        {
 43                            // ignore
 444                        }
 45                    }
 46                    break;
 47                case RecordedTestMode.Playback:
 910948                    _session = Load();
 49                    break;
 50            }
 911351        }
 52
 7397453        public RecordedTestMode Mode { get; }
 54
 911355        private readonly AsyncLocal<EntryRecordModel> _disableRecording = new AsyncLocal<EntryRecordModel>();
 56
 57        private readonly string _sessionFile;
 58
 59        private readonly RecordedTestSanitizer _sanitizer;
 60
 61        private readonly RecordMatcher _matcher;
 62
 63        private readonly RecordSession _session;
 64
 65        private RecordSession _previousSession;
 66
 67        private TestRandom _random;
 68
 69        public TestRandom Random
 70        {
 71            get
 72            {
 12712073                if (_random == null)
 74                {
 836975                    switch (Mode)
 76                    {
 77                        case RecordedTestMode.Live:
 078                            var csp = new RNGCryptoServiceProvider();
 079                            var bytes = new byte[4];
 080                            csp.GetBytes(bytes);
 081                            _random = new TestRandom(Mode, BitConverter.ToInt32(bytes, 0));
 082                            break;
 83                        case RecordedTestMode.Record:
 84                            // Try get the seed from existing session
 285                            if (!(_previousSession != null &&
 286                                  _previousSession.Variables.TryGetValue(RandomSeedVariableKey, out string seedString) &
 287                                  int.TryParse(seedString, out int seed)
 288                                ))
 89                            {
 290                                _random = new TestRandom(Mode);
 291                                seed = _random.Next();
 92                            }
 293                            _session.Variables[RandomSeedVariableKey] = seed.ToString();
 294                            _random = new TestRandom(Mode, seed);
 295                            break;
 96                        case RecordedTestMode.Playback:
 836797                            if (IsTrack1SessionRecord())
 98                            {
 99                                //random is not really used for track 1 playback, so randomly pick one as seed
 0100                                _random = new TestRandom(Mode, (int)DateTime.UtcNow.Ticks);
 101                            }
 102                            else
 103                            {
 8367104                                _random = new TestRandom(Mode, int.Parse(_session.Variables[RandomSeedVariableKey]));
 105                            }
 8367106                            break;
 107                        default:
 0108                            throw new ArgumentOutOfRangeException();
 109                    }
 110                }
 127120111                return _random;
 112            }
 113        }
 114
 115        /// <summary>
 116        /// The moment in time that this test is being run.
 117        /// </summary>
 118        private DateTimeOffset? _now;
 119
 120        /// <summary>
 121        /// Gets the moment in time that this test is being run.  This is useful
 122        /// for any test recordings that capture the current time.
 123        /// </summary>
 124        public DateTimeOffset Now
 125        {
 126            get
 127            {
 3980128                if (_now == null)
 129                {
 1672130                    switch (Mode)
 131                    {
 132                        case RecordedTestMode.Live:
 0133                            _now = DateTimeOffset.Now;
 0134                            break;
 135                        case RecordedTestMode.Record:
 136                            // While we can cache DateTimeOffset.Now for playing back tests,
 137                            // a number of auth mechanisms are time sensitive and will require
 138                            // values in the present when re-recording
 0139                            _now = DateTimeOffset.Now;
 0140                            _session.Variables[DateTimeOffsetNowVariableKey] = _now.Value.ToString("O"); // Use the "Rou
 0141                            break;
 142                        case RecordedTestMode.Playback:
 1672143                            _now = DateTimeOffset.Parse(_session.Variables[DateTimeOffsetNowVariableKey]);
 1672144                            break;
 145                        default:
 0146                            throw new ArgumentOutOfRangeException();
 147                    }
 148                }
 3980149                return _now.Value;
 150            }
 151        }
 152
 153        /// <summary>
 154        /// Gets the moment in time that this test is being run in UTC format.
 155        /// This is useful for any test recordings that capture the current time.
 156        /// </summary>
 2124157        public DateTimeOffset UtcNow => Now.ToUniversalTime();
 158
 159        private RecordSession Load()
 160        {
 9113161            using FileStream fileStream = File.OpenRead(_sessionFile);
 9113162            using JsonDocument jsonDocument = JsonDocument.Parse(fileStream);
 9109163            return RecordSession.Deserialize(jsonDocument.RootElement);
 9109164        }
 165
 166        public void Dispose(bool save)
 167        {
 9113168            if (Mode == RecordedTestMode.Record && save)
 169            {
 4170                var directory = Path.GetDirectoryName(_sessionFile);
 4171                Directory.CreateDirectory(directory);
 172
 4173                _session.Sanitize(_sanitizer);
 4174                if (_session.IsEquivalent(_previousSession, _matcher))
 175                {
 0176                    return;
 177                }
 178
 4179                using FileStream fileStream = File.Create(_sessionFile);
 4180                var utf8JsonWriter = new Utf8JsonWriter(fileStream, new JsonWriterOptions()
 4181                {
 4182                    Indented = true
 4183                });
 4184                _session.Serialize(utf8JsonWriter);
 4185                utf8JsonWriter.Flush();
 186            }
 9113187        }
 188
 189        public void Dispose()
 190        {
 0191            Dispose(true);
 0192        }
 193
 194        public T InstrumentClientOptions<T>(T clientOptions) where T : ClientOptions
 195        {
 14939196            clientOptions.Transport = CreateTransport(clientOptions.Transport);
 14939197            return clientOptions;
 198        }
 199
 200        public HttpPipelineTransport CreateTransport(HttpPipelineTransport currentTransport)
 201        {
 14941202            return Mode switch
 14941203            {
 0204                RecordedTestMode.Live => currentTransport,
 6205                RecordedTestMode.Record => new RecordTransport(_session, currentTransport, entry => _disableRecording.Va
 29878206                RecordedTestMode.Playback => new PlaybackTransport(_session, _matcher, _sanitizer, Random,
 356250207                    entry => _disableRecording.Value == EntryRecordModel.RecordWithoutRequestBody),
 0208                _ => throw new ArgumentOutOfRangeException(nameof(Mode), Mode, null),
 14941209            };
 210        }
 211
 212        public string GenerateId()
 213        {
 1357214            return Random.Next().ToString();
 215        }
 216
 217        public string GenerateAlphaNumericId(string prefix, int? maxLength = null, bool useOnlyLowercase = false)
 218        {
 656219            var stringChars = new char[8];
 220
 11808221            for (int i = 0; i < stringChars.Length; i++)
 222            {
 5248223                if (useOnlyLowercase)
 224                {
 5120225                    stringChars[i] = charsLower[Random.Next(charsLower.Length)];
 226                }
 227                else
 228                {
 128229                    stringChars[i] = chars[Random.Next(chars.Length)];
 230                }
 231            }
 232
 656233            var finalString = new string(stringChars);
 656234            if (maxLength.HasValue)
 235            {
 0236                return $"{prefix}{finalString}".Substring(0, maxLength.Value);
 237            }
 238            else
 239            {
 656240                return $"{prefix}{finalString}";
 241            }
 242        }
 243
 244        public string GenerateId(string prefix, int maxLength)
 245        {
 118246            var id = $"{prefix}{Random.Next()}";
 118247            return id.Length > maxLength ? id.Substring(0, maxLength): id;
 248        }
 249
 250        public string GenerateAssetName(string prefix, [CallerMemberName]string callerMethodName = "testframework_failed
 251        {
 6280252            if (Mode == RecordedTestMode.Playback && IsTrack1SessionRecord())
 253            {
 0254                return _session.Names[callerMethodName].Dequeue();
 255            }
 256            else
 257            {
 6280258                return prefix + Random.Next(9999);
 259            }
 260        }
 261
 262        public bool IsTrack1SessionRecord()
 263        {
 14647264            return _session.Entries.FirstOrDefault()?.IsTrack1Recording ?? false;
 265        }
 266
 267        public string GetVariable(string variableName, string defaultValue)
 268        {
 15743269            switch (Mode)
 270            {
 271                case RecordedTestMode.Record:
 0272                    _session.Variables[variableName] = defaultValue;
 0273                    return defaultValue;
 274                case RecordedTestMode.Live:
 0275                    return defaultValue;
 276                case RecordedTestMode.Playback:
 15743277                    _session.Variables.TryGetValue(variableName, out string value);
 15743278                    return value;
 279                default:
 0280                    throw new ArgumentOutOfRangeException();
 281            }
 282        }
 283
 284        public void SetVariable(string variableName, string value)
 285        {
 145286            switch (Mode)
 287            {
 288                case RecordedTestMode.Record:
 2289                    _session.Variables[variableName] = value;
 290                    break;
 291                default:
 292                    break;
 293            }
 145294        }
 295
 296        public void DisableIdReuse()
 297        {
 8298            _previousSession = null;
 8299        }
 300
 301        public DisableRecordingScope DisableRecording()
 302        {
 0303            return new DisableRecordingScope(this, EntryRecordModel.DontRecord);
 304        }
 305
 306        public DisableRecordingScope DisableRequestBodyRecording()
 307        {
 98308            return new DisableRecordingScope(this, EntryRecordModel.RecordWithoutRequestBody);
 309        }
 310
 311        public struct DisableRecordingScope : IDisposable
 312        {
 313            private readonly TestRecording _testRecording;
 314
 315            public DisableRecordingScope(TestRecording testRecording, EntryRecordModel entryRecordModel)
 316            {
 98317                _testRecording = testRecording;
 98318                _testRecording._disableRecording.Value = entryRecordModel;
 98319            }
 320
 321            public void Dispose()
 322            {
 98323                _testRecording._disableRecording.Value = EntryRecordModel.Record;
 98324            }
 325        }
 326    }
 327}