@@ -235,106 +235,112 @@ public void ProfilerIntegration_FullRoundtrip_Works(bool offlineCaching)
235235 {
236236 Skip . If ( TestEnvironment . IsGitHubActions , "Flaky in CI" ) ;
237237
238- var tcs = new TaskCompletionSource < string > ( ) ;
239- async Task VerifyAsync ( HttpRequestMessage message )
238+ TestHelpers . RetryTest ( maxAttempts : 2 , ExecuteTest ) ;
239+ return ;
240+
241+ void ExecuteTest ( )
240242 {
241- var payload = await message . Content ! . ReadAsStringAsync ( ) ;
242- // We're actually looking for type:profile but it must be sent in the same envelope as the transaction.
243- if ( payload . Contains ( "\" type\" :\" transaction\" " ) )
243+ var tcs = new TaskCompletionSource < string > ( ) ;
244+ async Task VerifyAsync ( HttpRequestMessage message )
244245 {
245- tcs . TrySetResult ( payload ) ;
246+ var payload = await message . Content ! . ReadAsStringAsync ( ) ;
247+ // We're actually looking for type:profile but it must be sent in the same envelope as the transaction.
248+ if ( payload . Contains ( "\" type\" :\" transaction\" " ) )
249+ {
250+ tcs . TrySetResult ( payload ) ;
251+ }
246252 }
247- }
248-
249- var cts = new CancellationTokenSource ( ) ;
250- cts . Token . Register ( ( ) => tcs . TrySetCanceled ( ) ) ;
251253
252- // envelope cache dir
253- using var cacheDirectory = offlineCaching ? new TempDirectory ( ) : null ;
254+ var cts = new CancellationTokenSource ( ) ;
255+ cts . Token . Register ( ( ) => tcs . TrySetCanceled ( ) ) ;
254256
255- // profiler temp dir (doesn't support `FileSystem`)
256- var tempDir = new TempDirectory ( ) ;
257-
258- var options = new SentryOptions
259- {
260- Dsn = ValidDsn ,
261- // To go through a round trip serialization of cached envelope
262- CacheDirectoryPath = cacheDirectory ? . Path ,
263- // So we don't need to deal with gzip'ed payload
264- RequestBodyCompressionLevel = CompressionLevel . NoCompression ,
265- CreateHttpMessageHandler = ( ) => new CallbackHttpClientHandler ( VerifyAsync ) ,
266- // Not to send some session envelope
267- AutoSessionTracking = false ,
268- Debug = true ,
269- DiagnosticLogger = _testOutputLogger ,
270- TracesSampleRate = 1.0 ,
271- ProfilesSampleRate = 1.0 ,
272- // This keeps all writing-to-file operations in memory instead of actually writing to disk
273- FileSystem = new FakeFileSystem ( )
274- } ;
257+ // envelope cache dir
258+ using var cacheDirectory = offlineCaching ? new TempDirectory ( ) : null ;
275259
276- // Disable process exit flush to resolve "There is no currently active test." errors.
277- options . DisableAppDomainProcessExitFlush ( ) ;
260+ // profiler temp dir (doesn't support `FileSystem`)
261+ var tempDir = new TempDirectory ( ) ;
278262
279- options . AddProfilingIntegration ( TimeSpan . FromSeconds ( 10 ) ) ;
263+ var options = new SentryOptions
264+ {
265+ Dsn = ValidDsn ,
266+ // To go through a round trip serialization of cached envelope
267+ CacheDirectoryPath = cacheDirectory ? . Path ,
268+ // So we don't need to deal with gzip'ed payload
269+ RequestBodyCompressionLevel = CompressionLevel . NoCompression ,
270+ CreateHttpMessageHandler = ( ) => new CallbackHttpClientHandler ( VerifyAsync ) ,
271+ // Not to send some session envelope
272+ AutoSessionTracking = false ,
273+ Debug = true ,
274+ DiagnosticLogger = _testOutputLogger ,
275+ TracesSampleRate = 1.0 ,
276+ ProfilesSampleRate = 1.0 ,
277+ // This keeps all writing-to-file operations in memory instead of actually writing to disk
278+ FileSystem = new FakeFileSystem ( )
279+ } ;
280+
281+ // Disable process exit flush to resolve "There is no currently active test." errors.
282+ options . DisableAppDomainProcessExitFlush ( ) ;
283+
284+ options . AddProfilingIntegration ( TimeSpan . FromSeconds ( 10 ) ) ;
280285
281- try
282- {
283- using var hub = new Hub ( options ) ;
286+ try
287+ {
288+ using var hub = new Hub ( options ) ;
284289
285- var factory = ( options . TransactionProfilerFactory as SamplingTransactionProfilerFactory ) ! ;
286- Skip . If ( TestEnvironment . IsGitHubActions && factory . StartupTimedOut , "Session sometimes takes too long to start in CI." ) ;
287- Assert . False ( factory . StartupTimedOut ) ;
290+ var factory = ( options . TransactionProfilerFactory as SamplingTransactionProfilerFactory ) ! ;
291+ Skip . If ( TestEnvironment . IsGitHubActions && factory . StartupTimedOut , "Session sometimes takes too long to start in CI." ) ;
292+ Assert . False ( factory . StartupTimedOut ) ;
288293
289- var clock = SentryStopwatch . StartNew ( ) ;
290- var tx = hub . StartTransaction ( "name" , "op" ) ;
291- RunForMs ( RuntimeMs ) ;
292- tx . Finish ( ) ;
293- var elapsedNanoseconds = ( ulong ) ( ( clock . CurrentDateTimeOffset - clock . StartDateTimeOffset ) . TotalMilliseconds * 1_000_000 ) ;
294+ var clock = SentryStopwatch . StartNew ( ) ;
295+ var tx = hub . StartTransaction ( "name" , "op" ) ;
296+ RunForMs ( RuntimeMs ) ;
297+ tx . Finish ( ) ;
298+ var elapsedNanoseconds = ( ulong ) ( ( clock . CurrentDateTimeOffset - clock . StartDateTimeOffset ) . TotalMilliseconds * 1_000_000 ) ;
294299
295- hub . FlushAsync ( ) . Wait ( ) ;
300+ hub . FlushAsync ( ) . Wait ( ) ;
296301
297- // Synchronizing in the tests to go through the caching and http transports
298- cts . CancelAfter ( options . FlushTimeout + TimeSpan . FromSeconds ( 1 ) ) ;
299- var ex = Record . Exception ( tcs . Task . Wait ) ;
300- Assert . Null ( ex ) ;
301- Assert . True ( tcs . Task . IsCompleted ) ;
302+ // Synchronizing in the tests to go through the caching and http transports
303+ cts . CancelAfter ( options . FlushTimeout + TimeSpan . FromSeconds ( 1 ) ) ;
304+ var ex = Record . Exception ( tcs . Task . Wait ) ;
305+ Assert . Null ( ex ) ;
306+ Assert . True ( tcs . Task . IsCompleted ) ;
302307
303- var envelopeLines = tcs . Task . Result . Split ( '\n ' ) ;
304- SkipIfFailsInCI ( ( ) =>
308+ var envelopeLines = tcs . Task . Result . Split ( '\n ' ) ;
309+ SkipIfFailsInCI ( ( ) =>
310+ {
311+ if ( envelopeLines . Length != 6 )
312+ {
313+ throw new ArgumentOutOfRangeException ( "envelopeLines" , "Invalid number of envelope lines." ) ;
314+ }
315+ } ) ;
316+
317+ // header rows before payloads
318+ envelopeLines [ 1 ] . Should ( ) . StartWith ( "{\" type\" :\" transaction\" " ) ;
319+ envelopeLines [ 3 ] . Should ( ) . StartWith ( "{\" type\" :\" profile\" " ) ;
320+
321+ var transaction = Json . Parse ( envelopeLines [ 2 ] , SentryTransaction . FromJson ) ;
322+
323+ // TODO do we want to bother with JSON parsing just to do this? Doing at least simple checks for now...
324+ // var profileInfo = Json.Parse(envelopeLines[4], ProfileInfo.FromJson);
325+ // ValidateProfile(profileInfo.Profile, elapsedNanoseconds);
326+ envelopeLines [ 4 ] . Should ( ) . Contain ( "\" profile\" :{" ) ;
327+ envelopeLines [ 4 ] . Should ( ) . Contain ( $ "\" id\" :\" { transaction . EventId } \" ") ;
328+ envelopeLines [ 4 ] . Length . Should ( ) . BeGreaterThan ( 10000 ) ;
329+
330+ Directory . GetFiles ( tempDir . Path ) . Should ( ) . BeEmpty ( "When profiling is done, the temp dir should be empty." ) ;
331+ }
332+ finally
305333 {
306- if ( envelopeLines . Length != 6 )
334+ // ensure the task is complete before leaving the test
335+ tcs . TrySetResult ( "" ) ;
336+ tcs . Task . Wait ( ) ;
337+
338+ if ( options . Transport is CachingTransport cachingTransport )
307339 {
308- throw new ArgumentOutOfRangeException ( "envelopeLines" , "Invalid number of envelope lines." ) ;
340+ // Disposing the caching transport will ensure its worker
341+ // is shut down before we try to dispose and delete the temp folder
342+ cachingTransport . Dispose ( ) ;
309343 }
310- } ) ;
311-
312- // header rows before payloads
313- envelopeLines [ 1 ] . Should ( ) . StartWith ( "{\" type\" :\" transaction\" " ) ;
314- envelopeLines [ 3 ] . Should ( ) . StartWith ( "{\" type\" :\" profile\" " ) ;
315-
316- var transaction = Json . Parse ( envelopeLines [ 2 ] , SentryTransaction . FromJson ) ;
317-
318- // TODO do we want to bother with JSON parsing just to do this? Doing at least simple checks for now...
319- // var profileInfo = Json.Parse(envelopeLines[4], ProfileInfo.FromJson);
320- // ValidateProfile(profileInfo.Profile, elapsedNanoseconds);
321- envelopeLines [ 4 ] . Should ( ) . Contain ( "\" profile\" :{" ) ;
322- envelopeLines [ 4 ] . Should ( ) . Contain ( $ "\" id\" :\" { transaction . EventId } \" ") ;
323- envelopeLines [ 4 ] . Length . Should ( ) . BeGreaterThan ( 10000 ) ;
324-
325- Directory . GetFiles ( tempDir . Path ) . Should ( ) . BeEmpty ( "When profiling is done, the temp dir should be empty." ) ;
326- }
327- finally
328- {
329- // ensure the task is complete before leaving the test
330- tcs . TrySetResult ( "" ) ;
331- tcs . Task . Wait ( ) ;
332-
333- if ( options . Transport is CachingTransport cachingTransport )
334- {
335- // Disposing the caching transport will ensure its worker
336- // is shut down before we try to dispose and delete the temp folder
337- cachingTransport . Dispose ( ) ;
338344 }
339345 }
340346 }
0 commit comments