Browse code

Build and use gotestsum for running all tests

1. Dockerfile.Windows modified to build gotestsum.exe

2. Use gotestsum.exe in invoking the execution of:

(a) Unit tests (run in containers),
(b) Integration tests (run outside containers)
(c) Integration-cli (run outside containers)

No changes made to other categories of tests (e.g.
LCOW).

3. Copy .xml files produced by gotestsum in
appropriate paths where Jenkins can ingest them

4. Modify Jenkinsfile to mark results output as
being jUnit "type" as well as to archive the
.xml test result files as artifacts.

Signed-off-by: Vikram bir Singh <vikrambir.singh@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Vikram bir Singh authored on 2019/10/08 10:14:51
Showing 4 changed files
... ...
@@ -166,6 +166,7 @@ FROM microsoft/windowsservercore
166 166
 SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
167 167
 
168 168
 ARG GO_VERSION=1.13.4
169
+ARG GOTESTSUM_COMMIT=v0.3.5
169 170
 
170 171
 # Environment variable notes:
171 172
 #  - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
... ...
@@ -174,7 +175,8 @@ ENV GO_VERSION=${GO_VERSION} `
174 174
     GIT_VERSION=2.11.1 `
175 175
     GOPATH=C:\gopath `
176 176
     GO111MODULE=off `
177
-    FROM_DOCKERFILE=1
177
+    FROM_DOCKERFILE=1 `
178
+    GOTESTSUM_COMMIT=${GOTESTSUM_COMMIT}
178 179
 
179 180
 RUN `
180 181
   Function Test-Nano() { `
... ...
@@ -249,13 +251,46 @@ RUN `
249 249
   Remove-Item C:\binutils.zip; `
250 250
   Remove-Item C:\gitsetup.zip; `
251 251
   `
252
-  Write-Host INFO: Creating source directory...; `
253
-  New-Item -ItemType Directory -Path ${GOPATH}\src\github.com\docker\docker | Out-Null; `
252
+  # Ensure all directories exist that we will require below....
253
+  $srcDir = """$Env:GOPATH`\src\github.com\docker\docker\bundles"""; `
254
+  Write-Host INFO: Ensuring existence of directory $srcDir...; `
255
+  New-Item -Force -ItemType Directory -Path $srcDir | Out-Null; `
254 256
   `
255 257
   Write-Host INFO: Configuring git core.autocrlf...; `
256
-  C:\git\cmd\git config --global core.autocrlf true; `
258
+  C:\git\cmd\git config --global core.autocrlf true;
259
+
260
+RUN `
261
+  Function Build-GoTestSum() { `
262
+    Write-Host "INFO: Building gotestsum version $Env:GOTESTSUM_COMMIT in $Env:GOPATH"; `
263
+    $optsForGet = @('"get"', '"-d"', '"gotest.tools/gotestsum"'); `
264
+    &go $optsForGet; `
265
+    $savedExitCode = $LASTEXITCODE; `
266
+    if ($savedExitCode -ne 0) {  `
267
+      Throw '"Failed getting gotestsum sources..."'  `
268
+    }; `
269
+    Write-Host "INFO:     Sources obtained for gotestsum..."; `
270
+    $GotestsumPath=Join-Path -Path $Env:GOPATH -ChildPath "src\gotest.tools\gotestsum"; `
271
+    Push-Location $GotestsumPath; `
272
+    $optsForCheckout = @('"checkout"', '"-q"', """$Env:GOTESTSUM_COMMIT"""); `
273
+    &git $optsForCheckout; `
274
+    $savedExitCode = $LASTEXITCODE; `
275
+    if ($savedExitCode -eq 0) { `
276
+      Write-Host "INFO:     Checkout done for gotestsum..."; `
277
+      $optsForBuild = @('"build"', '"-buildmode=exe"'); `
278
+      &go $optsForBuild; `
279
+      $savedExitCode = $LASTEXITCODE; `
280
+    } else { `
281
+      Throw '"gotestsum checkout failed..."'; `
282
+    } `
283
+    Pop-Location; `
284
+    `
285
+    if ($savedExitCode -ne 0) {  `
286
+      Throw '"gotestsum build failed..."'; `
287
+    } `
288
+    Write-Host "INFO:     Build done for gotestsum..."; `
289
+  } `
257 290
   `
258
-  Write-Host INFO: Completed
291
+  Build-GoTestSum
259 292
 
260 293
 # Make PowerShell the default entrypoint
261 294
 ENTRYPOINT ["powershell.exe"]
... ...
@@ -928,13 +928,14 @@ pipeline {
928 928
                     }
929 929
                     post {
930 930
                         always {
931
+                            junit testResults: 'bundles/junit-report-*.xml', allowEmptyResults: true
931 932
                             catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
932 933
                                 powershell '''
933 934
                                 $bundleName="windowsRS1-integration"
934 935
                                 Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip"
935 936
 
936 937
                                 # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location
937
-                                Compress-Archive -Path "${env:TEMP}/CIDUT.out", "${env:TEMP}/CIDUT.err" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip"
938
+                                Compress-Archive -Path "${env:TEMP}/CIDUT.out", "${env:TEMP}/CIDUT.err", "${env:TEMP}/testresults/unittests/junit-report-unit-tests.xml" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip"
938 939
                                 '''
939 940
 
940 941
                                 archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true
... ...
@@ -988,13 +989,14 @@ pipeline {
988 988
                     }
989 989
                     post {
990 990
                         always {
991
+                            junit testResults: 'bundles/junit-report-*.xml', allowEmptyResults: true
991 992
                             catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') {
992 993
                                 powershell '''
993 994
                                 $bundleName="windowsRS5-integration"
994 995
                                 Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip"
995 996
 
996 997
                                 # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location
997
-                                Compress-Archive -Path "${env:TEMP}/CIDUT.out", "${env:TEMP}/CIDUT.err" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip"
998
+                                Compress-Archive -Path "${env:TEMP}/CIDUT.out", "${env:TEMP}/CIDUT.err", "${env:TEMP}/junit-report-*.xml" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip"
998 999
                                 '''
999 1000
 
1000 1001
                                 archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true
... ...
@@ -447,6 +447,8 @@ Try {
447 447
     $errorActionPreference='Stop'
448 448
     New-Item -ItemType Directory "$env:TEMP" -ErrorAction SilentlyContinue | Out-Null
449 449
     New-Item -ItemType Directory "$env:TEMP\userprofile" -ErrorAction SilentlyContinue  | Out-Null
450
+    New-Item -ItemType Directory "$env:TEMP\testresults" -ErrorAction SilentlyContinue  | Out-Null
451
+    New-Item -ItemType Directory "$env:TEMP\testresults\unittests" -ErrorAction SilentlyContinue  | Out-Null
450 452
     New-Item -ItemType Directory "$env:TEMP\localappdata" -ErrorAction SilentlyContinue | Out-Null
451 453
     New-Item -ItemType Directory "$env:TEMP\binary" -ErrorAction SilentlyContinue | Out-Null
452 454
     New-Item -ItemType Directory "$env:TEMP\installer" -ErrorAction SilentlyContinue | Out-Null
... ...
@@ -525,6 +527,16 @@ Try {
525 525
         if (-not($LastExitCode -eq 0)) {
526 526
             Throw "ERROR: Failed to docker cp the daemon binary (dockerd.exe) to $env:TEMP\binary"
527 527
         }
528
+
529
+        $GotestsumBinRelLocationInContainer="src\gotest.tools\gotestsum\gotestsum.exe"
530
+        if (-not($LastExitCode -eq 0)) {
531
+            Throw "ERROR: Failed to docker cp the gotestsum binary (gotestsum.exe) to $env:TEMP\binary"
532
+        }
533
+        docker cp "$COMMITHASH`:c`:\gopath\$GotestsumBinRelLocationInContainer" $env:TEMP\binary\
534
+        if (-not (Test-Path "$env:TEMP\binary\gotestsum.exe")) {
535
+            Throw "ERROR: gotestsum.exe not found...." `
536
+        }
537
+
528 538
         $ErrorActionPreference = "Stop"
529 539
 
530 540
         # Copy the built dockerd.exe to dockerd-$COMMITHASH.exe so that easily spotted in task manager.
... ...
@@ -774,11 +786,36 @@ Try {
774 774
     # Run the unit tests inside a container unless SKIP_UNIT_TESTS is defined
775 775
     if (($null -eq $env:LCOW_MODE) -and ($null -eq $env:LCOW_BASIC_MODE)) {
776 776
         if ($null -eq $env:SKIP_UNIT_TESTS) {
777
+            $ContainerNameForUnitTests = $COMMITHASH + "_UnitTests"
777 778
             Write-Host -ForegroundColor Cyan "INFO: Running unit tests at $(Get-Date)..."
778 779
             $ErrorActionPreference = "SilentlyContinue"
779
-            $Duration=$(Measure-Command {docker run -e DOCKER_GITCOMMIT=$COMMITHASH$CommitUnsupported docker hack\make.ps1 -TestUnit | Out-Host })
780
+            $Duration=$(Measure-Command {docker run --name $ContainerNameForUnitTests -e DOCKER_GITCOMMIT=$COMMITHASH$CommitUnsupported docker hack\make.ps1 -TestUnit | Out-Host })
781
+            $TestRunExitCode = $LastExitCode
780 782
             $ErrorActionPreference = "Stop"
783
+
784
+            # Saving for artifacts......
785
+            $unitTestsContPath="$ContainerNameForUnitTests`:c`:\gopath\src\github.com\docker\docker\bundles"
786
+            $JunitExpectedContFilePath = "$unitTestsContPath\junit-report-unit-tests.xml"
787
+            docker cp $JunitExpectedContFilePath "$TEMPORIG"
788
+            if (-not($LastExitCode -eq 0)) {
789
+                Throw "ERROR: Failed to docker cp the unit tests report ($JunitExpectedContFilePath) to $TEMPORIG"
790
+            }
791
+
792
+            if (Test-Path "$TEMPORIG\junit-report-unit-tests.xml") {
793
+                Write-Host -ForegroundColor Magenta "INFO: Unit tests results($TEMPORIG\junit-report-unit-tests.xml) exist. pwd=$pwd"
794
+            } else {
795
+                Write-Host -ForegroundColor Magenta "ERROR: Unit tests results($TEMPORIG\junit-report-unit-tests.xml) do not exist. pwd=$pwd"
796
+            }
797
+            
798
+            # Saving where jenkins will take a look at.....
799
+            $bundlesDir = "bundles"
800
+            New-Item -Force -ItemType Directory $bundlesDir | Out-Null
801
+            docker cp $JunitExpectedContFilePath "$bundlesDir"
781 802
             if (-not($LastExitCode -eq 0)) {
803
+                Throw "ERROR: Failed to docker cp the unit tests report ($JunitExpectedContFilePath) to $bundlesDir"
804
+            }
805
+
806
+            if (-not($TestRunExitCode -eq 0)) {
782 807
                 Throw "ERROR: Unit tests failed"
783 808
             }
784 809
             Write-Host  -ForegroundColor Green "INFO: Unit tests ended at $(Get-Date). Duration`:$Duration"
... ...
@@ -830,7 +867,9 @@ Try {
830 830
             $env:OrigDOCKER_HOST="$env:DOCKER_HOST"
831 831
     
832 832
             #https://blogs.technet.microsoft.com/heyscriptingguy/2011/09/20/solve-problems-with-external-command-lines-in-powershell/ is useful to see tokenising
833
-            $c = "go test "
833
+            $jsonFilePath = "..\\bundles\\go-test-report-intcli-tests.json"
834
+            $xmlFilePath = "..\\bundles\\junit-report-intcli-tests.xml"
835
+            $c = "gotestsum --format=standard-quiet --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- "
834 836
             $c += "`"-test.v`" "
835 837
             if ($null -ne $env:INTEGRATION_TEST_NAME) { # Makes is quicker for debugging to be able to run only a subset of the integration tests
836 838
                 $c += "`"-test.run`" "
... ...
@@ -864,10 +903,12 @@ Try {
864 864
                 if (!($env:INTEGRATION_TESTFLAGS)) {
865 865
                     $env:INTEGRATION_TESTFLAGS = "-test.v"
866 866
                 }
867
-                Set-Location "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker"
868 867
                 $start=(Get-Date); Invoke-Expression ".\hack\make.ps1 -TestIntegration"; $Duration=New-Timespan -Start $start -End (Get-Date)
868
+                $IntTestsRunResult = $LastExitCode
869 869
                 $ErrorActionPreference = "Stop"
870
-                if (-not($LastExitCode -eq 0)) {
870
+                # Copy all the test results to TEMPORIG for archival
871
+                Copy-Item -Path "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker\bundles\junit-report*xml" -Destination $TEMPORIG
872
+                if (-not($IntTestsRunResult -eq 0)) {
871 873
                     Throw "ERROR: Integration API tests failed at $(Get-Date). Duration`:$Duration"
872 874
                 }
873 875
 
... ...
@@ -877,6 +918,7 @@ Try {
877 877
                 Set-Location "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker\integration-cli"
878 878
                 # Explicit to not use measure-command otherwise don't get output as it goes
879 879
                 $start=(Get-Date); Invoke-Expression $c; $Duration=New-Timespan -Start $start -End (Get-Date)
880
+                Copy-Item -Path $xmlFilePath -Destination $TEMPORIG
880 881
             }
881 882
             $ErrorActionPreference = "Stop"
882 883
             if (-not($LastExitCode -eq 0)) {
... ...
@@ -94,6 +94,7 @@ param(
94 94
 $ErrorActionPreference = "Stop"
95 95
 $ProgressPreference = "SilentlyContinue"
96 96
 $pushed=$False  # To restore the directory if we have temporarily pushed to one.
97
+Set-Variable GOTESTSUM_LOCATION -option Constant -value "$env:GOPATH/src/gotest.tools/gotestsum"
97 98
 
98 99
 # Utility function to get the commit ID of the repository
99 100
 Function Get-GitCommit() {
... ...
@@ -319,31 +320,42 @@ Function Run-UnitTests() {
319 319
     $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/man"
320 320
     $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration"
321 321
     $pkgList = $pkgList -replace "`r`n", " "
322
-    $goTestCommand = "go test" + $raceParm + " -cover -ldflags -w -tags """ + "autogen daemon" + """ -a """ + "-test.timeout=10m" + """ $pkgList"
322
+    $goTestCommand = "$GOTESTSUM_LOCATION\gotestsum.exe --format=standard-quiet --jsonfile=bundles\go-test-report-unit-tests.json --junitfile=bundles\junit-report-unit-tests.xml -- " + $raceParm + " -cover -ldflags -w -a """ + "-test.timeout=10m" + """ $pkgList"
323
+    Write-Host "INFO: Invoking unit tests run with $goTestCommand"
323 324
     Invoke-Expression $goTestCommand
324 325
     if ($LASTEXITCODE -ne 0) { Throw "Unit tests failed" }
325 326
 }
326 327
 
327 328
 # Run the integration tests
328 329
 Function Run-IntegrationTests() {
329
-    $env:DOCKER_INTEGRATION_DAEMON_DEST = $root + "\bundles\tmp"
330
+    $escRoot = [Regex]::Escape($root)
331
+    $env:DOCKER_INTEGRATION_DAEMON_DEST = $bundlesDir + "\tmp"
330 332
     $dirs = go list -test -f '{{- if ne .ForTest `"`" -}}{{- .Dir -}}{{- end -}}' .\integration\...
331
-    $integration_api_dirs = @()
332 333
     ForEach($dir in $dirs) {
333
-        $integration_api_dirs += $dir
334
-        Write-Host "Building test suite binary $dir"
335
-        go test -c -o "$dir\test.exe" $dir
336
-    }
337
-
338
-    ForEach($dir in $integration_api_dirs) {
334
+        # Normalize directory name for using in the test results files.
335
+        $normDir = $dir.Trim()
336
+        $normDir = $normDir -replace $escRoot, ""
337
+        $normDir = $normDir -replace "\\", "-"
338
+        $normDir = $normDir -replace "\/", "-"
339
+        $normDir = $normDir -replace "\.", "-"
340
+        if ($normDir.StartsWith("-"))
341
+        {
342
+            $normDir = $normDir.TrimStart("-")
343
+        }
344
+        if ($normDir.EndsWith("-"))
345
+        {
346
+            $normDir = $normDir.TrimEnd("-")
347
+        }
348
+        $jsonFilePath = $bundlesDir + "\go-test-report-int-tests-$normDir" + ".json"
349
+        $xmlFilePath = $bundlesDir + "\junit-report-int-tests-$normDir" + ".xml"
339 350
         Set-Location $dir
340 351
         Write-Host "Running $($PWD.Path)"
341 352
         $pinfo = New-Object System.Diagnostics.ProcessStartInfo
342
-        $pinfo.FileName = "$($PWD.Path)\test.exe"
353
+        $pinfo.FileName = "gotestsum.exe"
343 354
         $pinfo.WorkingDirectory = "$($PWD.Path)"
344 355
         $pinfo.RedirectStandardError = $true
345 356
         $pinfo.UseShellExecute = $false
346
-        $pinfo.Arguments = $env:INTEGRATION_TESTFLAGS
357
+        $pinfo.Arguments = "--format=standard-quiet --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- $env:INTEGRATION_TESTFLAGS"
347 358
         $p = New-Object System.Diagnostics.Process
348 359
         $p.StartInfo = $pinfo
349 360
         $p.Start() | Out-Null
... ...
@@ -351,6 +363,8 @@ Function Run-IntegrationTests() {
351 351
         $err = $p.StandardError.ReadToEnd()
352 352
         if (($LASTEXITCODE -ne 0) -and ($err -notlike "*warning: no tests to run*")) {
353 353
             Throw "Integration tests failed: $err"
354
+        } else {
355
+            Write-Host "$err"
354 356
         }
355 357
     }
356 358
 }
... ...
@@ -363,6 +377,11 @@ Try {
363 363
     $root = $(Split-Path $MyInvocation.MyCommand.Definition -Parent | Split-Path -Parent)
364 364
     Push-Location $root
365 365
 
366
+    # Ensure the bundles directory exists
367
+    $bundlesDir = $root + "\bundles"
368
+    Set-Variable bundlesDir -option ReadOnly
369
+    New-Item -Force $bundlesDir -ItemType Directory | Out-Null
370
+
366 371
     # Handle the "-All" shortcut to turn on all things we can handle.
367 372
     # Note we expressly only include the items which can run in a container - the validations tests cannot
368 373
     # as they require the .git directory which is excluded from the image by .dockerignore
... ...
@@ -424,8 +443,6 @@ Try {
424 424
 
425 425
     # Build the binaries
426 426
     if ($Client -or $Daemon) {
427
-        # Create the bundles directory if it doesn't exist
428
-        if (-not (Test-Path ".\bundles")) { New-Item ".\bundles" -ItemType Directory | Out-Null }
429 427
 
430 428
         # Perform the actual build
431 429
         if ($Daemon) { Execute-Build "daemon" "daemon" "dockerd" }