예제 #1
0
        private async Task ProcessArtifactAsync(BuildSyncContext context, BuildArtifact artifact, CancellationToken stoppingToken)
        {
            // Stream the artifact down to disk
            var artifactFile = await SaveArtifactAsync(artifact, stoppingToken);

            try
            {
                using var archive = ZipFile.OpenRead(artifactFile);
                foreach (var file in archive.Entries)
                {
                    using (_logger.BeginScope("File: {ArtifactFile}", file.FullName))
                    {
                        if (file.Name.EndsWith(".xml"))
                        {
                            _logger.LogTrace(new EventId(0, "ProcessingFile"), "Processing file {ArtifactFile}...", file);
                            await ProcessTestResultFileAsync(context, file, stoppingToken);

                            _logger.LogTrace(new EventId(0, "ProcessedFile"), "Processed file {ArtifactFile}.", file);
                        }
                        else
                        {
                            _logger.LogTrace(new EventId(0, "SkippingFile"), "Skipping artifact file {ArtifactFile}.", file);
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                _logger.LogDebug(new EventId(0, "CancellingArtifactProcessing"), "Cancelling processing of artifact: {ArtifactName}.", artifact.Name);
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError(new EventId(0, "ErrorProcessingArtifact"), ex, "Error processing artifact: {ArtifactName}.", artifact.Name);

                // We don't want to continue processing this build if an artifact failed. That way we can try again later.
                throw;
            }
            finally
            {
                try
                {
                    _logger.LogTrace(new EventId(0, "DeletingTemporaryFile"), "Deleting temporary file {TempFile} for Artifact {ArtifactName}.", artifactFile, artifact.Name);
                    File.Delete(artifactFile);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(new EventId(0, "ErrorDeletingTemporaryFile"), ex, "Error deleting temporary file: {TempFile}.", artifactFile);
                }
            }
        }
예제 #2
0
        private async Task ProcessBuildAsync(BuildSyncContext context, CancellationToken stoppingToken)
        {
            // Mark this build as in progress.
            context.DbBuild.SyncStatus     = SyncStatus.InProgress;
            context.DbBuild.SyncStartedUtc = DateTime.UtcNow;
            await context.Db.SaveChangesAsync();

            _logger.LogDebug(new EventId(0, "StartedSyncForBuild"), "Started Sync for Build #{BuildNumber}", context.DbBuild.BuildNumber);

            try
            {
                var artifacts = await context.GetArtifactsAsync(cancellationToken : stoppingToken);

                foreach (var artifact in artifacts)
                {
                    _logger.LogDebug(new EventId(0, "ProcessingArtifact"), "Processing artifact {ArtifactName}...", artifact.Name);
                    await ProcessArtifactAsync(context, artifact, stoppingToken);

                    _logger.LogDebug(new EventId(0, "ProcessedArtifact"), "Processed artifact {ArtifactName}.", artifact.Name);
                }

                context.DbBuild.SyncStatus      = SyncStatus.Complete;
                context.DbBuild.SyncCompleteUtc = DateTime.UtcNow;
                await context.Db.SaveChangesAsync();

                _logger.LogDebug(new EventId(0, "SavedBuild"), "Saved all results from build #{BuildNumber}", context.DbBuild.BuildNumber);
            }
            catch (OperationCanceledException)
            {
                _logger.LogDebug(new EventId(0, "CancellingBuildProcessing"), "Cancelling processing of build: #{BuildNumber}.", context.DbBuild.BuildNumber);
                context.DbBuild.SyncStatus = SyncStatus.Cancelled;
                await context.Db.SaveChangesAsync();

                throw;
            }
            catch (Exception ex)
            {
                context.DbBuild.SyncStatus = SyncStatus.Failed;
                await context.Db.SaveChangesAsync();

                _logger.LogError(new EventId(0, "ErrorProcessingBuild"), ex, "Error Processing Build #{BuildNumber}.", context.DbBuild.BuildNumber);
            }
        }
예제 #3
0
        private async Task ProcessBranchAsync(PipelineScrapeContext context, PipelineConfig config, string branch, CancellationToken stoppingToken)
        {
            // Look up the most recent build (for now)
            _logger.LogDebug(new EventId(0, "FetchingBuilds"), "Fetching builds for {PipelineProject}/{PipelineName} in {BranchName}...", config.Project, config.Name, branch);
            var builds = await context.GetBuildsAsync(branch, stoppingToken);

            _logger.LogInformation(new EventId(0, "FetchedBuilds"), "Fetched {BuildCount} builds.", builds.Count);

            foreach (var build in builds)
            {
                using (_logger.BeginScope("Build {BuildId} #{BuildNumber}", build.Id, build.BuildNumber, build.SourceVersion))
                {
                    using (var scope = _scopeFactory.CreateScope())
                    {
                        var db = scope.ServiceProvider.GetRequiredService <TestResultsDbContext>();
                        // Load the Build from the new db context
                        var dbBuild = await db.Builds.FirstOrDefaultAsync(b => b.Id == build.Id);

                        if (dbBuild == null)
                        {
                            _logger.LogError(new EventId(0, "CouldNotFindBuild"), "Could not find build #{BuildNumber} (ID: {BuildDbId}) in the database!", build.BuildNumber, build.Id);
                            continue;
                        }

                        var buildContext = new BuildSyncContext(context, dbBuild, db);

                        // TODO: Retry of unsynced builds?
                        _logger.LogInformation(new EventId(0, "ProcessingBuild"), "Processing build #{BuildNumber}...", build.BuildNumber);
                        var sw = Stopwatch.StartNew();
                        await ProcessBuildAsync(buildContext, stoppingToken);

                        _logger.LogInformation(new EventId(0, "ProcessedBuild"), "Processed build #{BuildNumber} in {Elapsed}.", build.BuildNumber, sw.Elapsed);
                    }
                }
            }
        }
예제 #4
0
        private async Task ProcessTestResultFileAsync(BuildSyncContext context, ZipArchiveEntry file, CancellationToken stoppingToken)
        {
            try
            {
                using var stream = file.Open();
                var doc = await XDocument.LoadAsync(stream, LoadOptions.None, stoppingToken);

                // Remove the extension but keep the full relative path to get the run name.
                var runName = file.FullName.Substring(0, file.FullName.Length - 4);
                var dbRunId = await context.PipelineContext.ResultsContext.GetOrCreateRunAsync(runName, stoppingToken);

                _logger.LogTrace(new EventId(0, "ParsingTestResults"), "Parsing test results.");
                var assemblies = XUnitTestResultsFormat.Parse(doc);

                _logger.LogTrace(new EventId(0, "ProcessingTestResults"), "Processing test results...");

                foreach (var assembly in assemblies)
                {
                    foreach (var collection in assembly.Collections)
                    {
                        var dbCollection = dbAssembly.Collections.FirstOrDefault(c => c.Name == collection.Name);
                        if (dbCollection == null)
                        {
                            dbCollection = new PipelineTestAssembly()
                            {
                                Name = assembly.Name
                            };
                            context.Db.TestAssemblies.Add(dbAssembly);
                        }
                        foreach (var method in collection.Methods)
                        {
                            var dbMethodId = await context.PipelineContext.ResultsContext.GetOrCreateMethodAsync(dbCollectionId, method.Type, method.Name, stoppingToken);

                            foreach (var result in method.Results)
                            {
                                // Truncate off the type name
                                var resultName = result.Name.StartsWith($"{method.Type}.") ? result.Name.Substring(method.Type.Length + 1) : result.Name;

                                var dbCaseId = await context.PipelineContext.ResultsContext.GetOrCreateTestCaseAsync(dbMethodId, resultName, stoppingToken);

                                var quarantinedOn = new List <string>();
                                foreach (var trait in result.Traits)
                                {
                                    if (trait.Name.StartsWith("Flaky:"))
                                    {
                                        quarantinedOn.Add(trait.Name.Substring(6));
                                    }
                                    else if (trait.Name.StartsWith("Quarantined:"))
                                    {
                                        quarantinedOn.Add(trait.Name.Substring(12));
                                    }
                                }

                                var dbResult = new PipelineTestResult()
                                {
                                    BuildId       = context.DbBuild.Id,
                                    CaseId        = dbCaseId,
                                    RunId         = dbRunId,
                                    Traits        = string.Join(";", result.Traits.Select(t => $"{t.Name}={t.Value}")),
                                    Quarantined   = result.Traits.Any(t => t.Name.StartsWith("Flaky:") || t.Name.StartsWith("Quarantined:")),
                                    QuarantinedOn = string.Join(";", result.Traits.Where(t => t.Name.StartsWith("Flaky:")).Select(t => t.Name.Substring(6))),
                                    Result        = result.Outcome switch
                                    {
                                        FailureTestOutcome f => TestResultKind.Fail,
                                        SkippedTestOutcome s => TestResultKind.Skip,
                                        SuccessfulTestOutcome _ => TestResultKind.Pass,
                                    _ => TestResultKind.Unknown,
                                    }
                                };
                                context.Db.TestResults.Add(dbResult);
                            }
                        }
                    }
                }

                // Save results per-file.
                _logger.LogTrace(new EventId(0, "SavingTestResults"), "Saving test results to database...");
                await context.Db.SaveChangesAsync();

                _logger.LogTrace(new EventId(0, "SavedTestResults"), "Saved test results to database");
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError(new EventId(0, "ErrorProcessingTestResults"), ex, "Error processing test result file: {TestResultsFile}", file.FullName);
                throw;
            }
        }