| # This script enables you running RocksDB tests by running |
| # All the tests concurrently and utilizing all the cores |
| Param( |
| [switch]$EnableJE = $false, # Look for and use _je executable, append _je to listed exclusions |
| [switch]$RunAll = $false, # Will attempt discover all *_test[_je].exe binaries and run all |
| # of them as Google suites. I.e. It will run test cases concurrently |
| # except those mentioned as $Run, those will run as individual test cases |
| # And any execlued with $ExcludeExes or $ExcludeCases |
| # It will also not run any individual test cases |
| # excluded but $ExcludeCasese |
| [string]$SuiteRun = "", # Split test suites in test cases and run in parallel, not compatible with $RunAll |
| [string]$Run = "", # Run specified executables in parallel but do not split to test cases |
| [string]$ExcludeCases = "", # Exclude test cases, expects a comma separated list, no spaces |
| # Takes effect when $RunAll or $SuiteRun is specified. Must have full |
| # Test cases name including a group and a parameter if any |
| [string]$ExcludeExes = "", # Exclude exes from consideration, expects a comma separated list, |
| # no spaces. Takes effect only when $RunAll is specified |
| [string]$WorkFolder = "", # Direct tests to use that folder. SSD or Ram drive are better options. |
| # Number of async tasks that would run concurrently. Recommend a number below 64. |
| # However, CPU utlization really depends on the storage media. Recommend ram based disk. |
| # a value of 1 will run everything serially |
| [int]$Concurrency = 8, |
| [int]$Limit = -1 # -1 means do not limit for test purposes |
| ) |
| |
| # Folders and commands must be fullpath to run assuming |
| # the current folder is at the root of the git enlistment |
| $StartDate = (Get-Date) |
| $StartDate |
| |
| |
| $DebugPreference = "Continue" |
| |
| # These tests are not google test suites and we should guard |
| # Against running them as suites |
| $RunOnly = New-Object System.Collections.Generic.HashSet[string] |
| $RunOnly.Add("c_test") | Out-Null |
| $RunOnly.Add("compact_on_deletion_collector_test") | Out-Null |
| $RunOnly.Add("merge_test") | Out-Null |
| $RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written |
| $RunOnly.Add("backupable_db_test") | Out-Null # Disabled |
| |
| |
| if($RunAll -and $SuiteRun -ne "") { |
| Write-Error "$RunAll and $SuiteRun are not compatible" |
| exit 1 |
| } |
| |
| # If running under Appveyor assume that root |
| [string]$Appveyor = $Env:APPVEYOR_BUILD_FOLDER |
| if($Appveyor -ne "") { |
| $RootFolder = $Appveyor |
| } else { |
| $RootFolder = $PSScriptRoot -replace '\\build_tools', '' |
| } |
| |
| $LogFolder = -Join($RootFolder, "\db_logs\") |
| $BinariesFolder = -Join($RootFolder, "\build\Debug\") |
| |
| if($WorkFolder -eq "") { |
| |
| # If TEST_TMPDIR is set use it |
| [string]$var = $Env:TEST_TMPDIR |
| if($var -eq "") { |
| $WorkFolder = -Join($RootFolder, "\db_tests\") |
| $Env:TEST_TMPDIR = $WorkFolder |
| } else { |
| $WorkFolder = $var |
| } |
| } else { |
| # Override from a command line |
| $Env:TEST_TMPDIR = $WorkFolder |
| } |
| |
| Write-Output "Root: $RootFolder, WorkFolder: $WorkFolder" |
| Write-Output "BinariesFolder: $BinariesFolder, LogFolder: $LogFolder" |
| |
| # Create test directories in the current folder |
| md -Path $WorkFolder -ErrorAction Ignore | Out-Null |
| md -Path $LogFolder -ErrorAction Ignore | Out-Null |
| |
| |
| $ExcludeCasesSet = New-Object System.Collections.Generic.HashSet[string] |
| if($ExcludeCases -ne "") { |
| Write-Host "ExcludeCases: $ExcludeCases" |
| $l = $ExcludeCases -split ' ' |
| ForEach($t in $l) { |
| $ExcludeCasesSet.Add($t) | Out-Null |
| } |
| } |
| |
| $ExcludeExesSet = New-Object System.Collections.Generic.HashSet[string] |
| if($ExcludeExes -ne "") { |
| Write-Host "ExcludeExe: $ExcludeExes" |
| $l = $ExcludeExes -split ' ' |
| ForEach($t in $l) { |
| $ExcludeExesSet.Add($t) | Out-Null |
| } |
| } |
| |
| |
| # Extract the names of its tests by running db_test with --gtest_list_tests. |
| # This filter removes the "#"-introduced comments, and expands to |
| # fully-qualified names by changing input like this: |
| # |
| # DBTest. |
| # Empty |
| # WriteEmptyBatch |
| # MultiThreaded/MultiThreadedDBTest. |
| # MultiThreaded/0 # GetParam() = 0 |
| # MultiThreaded/1 # GetParam() = 1 |
| # |
| # into this: |
| # |
| # DBTest.Empty |
| # DBTest.WriteEmptyBatch |
| # MultiThreaded/MultiThreadedDBTest.MultiThreaded/0 |
| # MultiThreaded/MultiThreadedDBTest.MultiThreaded/1 |
| # |
| # Output into the parameter in a form TestName -> Log File Name |
| function ExtractTestCases([string]$GTestExe, $HashTable) { |
| |
| $Tests = @() |
| # Run db_test to get a list of tests and store it into $a array |
| &$GTestExe --gtest_list_tests | tee -Variable Tests | Out-Null |
| |
| # Current group |
| $Group="" |
| |
| ForEach( $l in $Tests) { |
| |
| # Leading whitespace is fine |
| $l = $l -replace '^\s+','' |
| # but no whitespace any other place |
| if($l -match "\s+") { |
| continue |
| } |
| # Trailing dot is a test group but no whitespace |
| elseif ( $l -match "\.$" ) { |
| $Group = $l |
| } else { |
| # Otherwise it is a test name, remove leading space |
| $test = $l |
| # remove trailing comment if any and create a log name |
| $test = $test -replace '\s+\#.*','' |
| $test = "$Group$test" |
| |
| if($ExcludeCasesSet.Contains($test)) { |
| Write-Warning "$test case is excluded" |
| continue |
| } |
| |
| $test_log = $test -replace '[\./]','_' |
| $test_log += ".log" |
| $log_path = -join ($LogFolder, $test_log) |
| |
| # Add to a hashtable |
| $HashTable.Add($test, $log_path); |
| } |
| } |
| } |
| |
| # The function removes trailing .exe siffix if any, |
| # creates a name for the log file |
| # Then adds the test name if it was not excluded into |
| # a HashTable in a form of test_name -> log_path |
| function MakeAndAdd([string]$token, $HashTable) { |
| |
| $test_name = $token -replace '.exe$', '' |
| $log_name = -join ($test_name, ".log") |
| $log_path = -join ($LogFolder, $log_name) |
| $HashTable.Add($test_name, $log_path) |
| } |
| |
| # This function takes a list of Suites to run |
| # Lists all the test cases in each of the suite |
| # and populates HashOfHashes |
| # Ordered by suite(exe) @{ Exe = @{ TestCase = LogName }} |
| function ProcessSuites($ListOfSuites, $HashOfHashes) { |
| |
| $suite_list = $ListOfSuites |
| # Problem: if you run --gtest_list_tests on |
| # a non Google Test executable then it will start executing |
| # and we will get nowhere |
| ForEach($suite in $suite_list) { |
| |
| if($RunOnly.Contains($suite)) { |
| Write-Warning "$suite is excluded from running as Google test suite" |
| continue |
| } |
| |
| if($EnableJE) { |
| $suite += "_je" |
| } |
| |
| $Cases = [ordered]@{} |
| $Cases.Clear() |
| $suite_exe = -Join ($BinariesFolder, $suite) |
| ExtractTestCases -GTestExe $suite_exe -HashTable $Cases |
| if($Cases.Count -gt 0) { |
| $HashOfHashes.Add($suite, $Cases); |
| } |
| } |
| |
| # Make logs and run |
| if($CasesToRun.Count -lt 1) { |
| Write-Error "Failed to extract tests from $SuiteRun" |
| exit 1 |
| } |
| |
| } |
| |
| # This will contain all test executables to run |
| |
| # Hash table that contains all non suite |
| # Test executable to run |
| $TestExes = [ordered]@{} |
| |
| # Check for test exe that are not |
| # Google Test Suites |
| # Since this is explicitely mentioned it is not subject |
| # for exclusions |
| if($Run -ne "") { |
| |
| $test_list = $Run -split ' ' |
| |
| ForEach($t in $test_list) { |
| |
| if($EnableJE) { |
| $t += "_je" |
| } |
| |
| MakeAndAdd -token $t -HashTable $TestExes |
| } |
| |
| if($TestExes.Count -lt 1) { |
| Write-Error "Failed to extract tests from $Run" |
| exit 1 |
| } |
| } |
| |
| # Ordered by exe @{ Exe = @{ TestCase = LogName }} |
| $CasesToRun = [ordered]@{} |
| |
| if($SuiteRun -ne "") { |
| $suite_list = $SuiteRun -split ' ' |
| ProcessSuites -ListOfSuites $suite_list -HashOfHashes $CasesToRun |
| } |
| |
| if($RunAll) { |
| # Discover all the test binaries |
| if($EnableJE) { |
| $pattern = "*_test_je.exe" |
| } else { |
| $pattern = "*_test.exe" |
| } |
| |
| |
| $search_path = -join ($BinariesFolder, $pattern) |
| Write-Host "Binaries Search Path: $search_path" |
| |
| $ListOfExe = @() |
| dir -Path $search_path | ForEach-Object { |
| $ListOfExe += ($_.Name) |
| } |
| |
| # Exclude those in RunOnly from running as suites |
| $ListOfSuites = @() |
| ForEach($e in $ListOfExe) { |
| |
| $e = $e -replace '.exe$', '' |
| $bare_name = $e -replace '_je$', '' |
| |
| if($ExcludeExesSet.Contains($bare_name)) { |
| Write-Warning "Test $e is excluded" |
| continue |
| } |
| |
| if($RunOnly.Contains($bare_name)) { |
| MakeAndAdd -token $e -HashTable $TestExes |
| } else { |
| $ListOfSuites += $bare_name |
| } |
| } |
| |
| ProcessSuites -ListOfSuites $ListOfSuites -HashOfHashes $CasesToRun |
| } |
| |
| |
| Write-Host "Attempting to start: $NumTestsToStart tests" |
| |
| # Invoke a test with a filter and redirect all output |
| $InvokeTestCase = { |
| param($exe, $test, $log); |
| &$exe --gtest_filter=$test > $log 2>&1 |
| } |
| |
| # Invoke all tests and redirect output |
| $InvokeTestAsync = { |
| param($exe, $log) |
| &$exe > $log 2>&1 |
| } |
| |
| # Hash that contains tests to rerun if any failed |
| # Those tests will be rerun sequentially |
| # $Rerun = [ordered]@{} |
| # Test limiting factor here |
| [int]$count = 0 |
| # Overall status |
| [bool]$success = $true; |
| |
| function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal) |
| { |
| # Array to wait for any of the running jobs |
| $jobs = @() |
| # Hash JobToLog |
| $JobToLog = @{} |
| |
| # Wait for all to finish and get the results |
| while(($JobToLog.Count -gt 0) -or |
| ($TestCmds.Count -gt 0) -or |
| ($Suites.Count -gt 0)) { |
| |
| # Make sure we have maximum concurrent jobs running if anything |
| # and the $Limit either not set or allows to proceed |
| while(($JobToLog.Count -lt $ConcurrencyVal) -and |
| ((($TestCmds.Count -gt 0) -or ($Suites.Count -gt 0)) -and |
| (($Limit -lt 0) -or ($count -lt $Limit)))) { |
| |
| # We always favore suites to run if available |
| [string]$exe_name = "" |
| [string]$log_path = "" |
| $Cases = @{} |
| |
| if($Suites.Count -gt 0) { |
| # Will the first one |
| ForEach($e in $Suites.Keys) { |
| $exe_name = $e |
| $Cases = $Suites[$e] |
| break |
| } |
| [string]$test_case = "" |
| [string]$log_path = "" |
| ForEach($c in $Cases.Keys) { |
| $test_case = $c |
| $log_path = $Cases[$c] |
| break |
| } |
| |
| Write-Host "Starting $exe_name::$test_case" |
| [string]$Exe = -Join ($BinariesFolder, $exe_name) |
| $job = Start-Job -Name "$exe_name::$test_case" -ArgumentList @($Exe,$test_case,$log_path) -ScriptBlock $InvokeTestCase |
| $JobToLog.Add($job, $log_path) |
| |
| $Cases.Remove($test_case) |
| if($Cases.Count -lt 1) { |
| $Suites.Remove($exe_name) |
| } |
| |
| } elseif ($TestCmds.Count -gt 0) { |
| |
| ForEach($e in $TestCmds.Keys) { |
| $exe_name = $e |
| $log_path = $TestCmds[$e] |
| break |
| } |
| |
| [string]$Exe = -Join ($BinariesFolder, $exe_name) |
| $job = Start-Job -Name $exe_name -ScriptBlock $InvokeTestAsync -ArgumentList @($Exe,$log_path) |
| $JobToLog.Add($job, $log_path) |
| |
| $TestCmds.Remove($exe_name) |
| |
| } else { |
| Write-Error "In the job loop but nothing to run" |
| exit 1 |
| } |
| |
| ++$count |
| } # End of Job starting loop |
| |
| if($JobToLog.Count -lt 1) { |
| break |
| } |
| |
| $jobs = @() |
| foreach($k in $JobToLog.Keys) { $jobs += $k } |
| |
| $completed = Wait-Job -Job $jobs -Any |
| $log = $JobToLog[$completed] |
| $JobToLog.Remove($completed) |
| |
| $message = -join @($completed.Name, " State: ", ($completed.State)) |
| |
| $log_content = @(Get-Content $log) |
| |
| if($completed.State -ne "Completed") { |
| $success = $false |
| Write-Warning $message |
| $log_content | Write-Warning |
| } else { |
| # Scan the log. If we find PASSED and no occurrence of FAILED |
| # then it is a success |
| [bool]$pass_found = $false |
| ForEach($l in $log_content) { |
| |
| if(($l -match "^\[\s+FAILED") -or |
| ($l -match "Assertion failed:")) { |
| $pass_found = $false |
| break |
| } |
| |
| if(($l -match "^\[\s+PASSED") -or |
| ($l -match " : PASSED$") -or |
| ($l -match "^PASS$") -or # Special c_test case |
| ($l -match "Passed all tests!") ) { |
| $pass_found = $true |
| } |
| } |
| |
| if(!$pass_found) { |
| $success = $false; |
| Write-Warning $message |
| $log_content | Write-Warning |
| } else { |
| Write-Host $message |
| } |
| } |
| |
| # Remove cached job info from the system |
| # Should be no output |
| Receive-Job -Job $completed | Out-Null |
| } |
| } |
| |
| RunJobs -Suites $CasesToRun -TestCmds $TestExes -ConcurrencyVal $Concurrency |
| |
| $EndDate = (Get-Date) |
| |
| New-TimeSpan -Start $StartDate -End $EndDate | |
| ForEach-Object { |
| "Elapsed time: {0:g}" -f $_ |
| } |
| |
| |
| if(!$success) { |
| # This does not succeed killing off jobs quick |
| # So we simply exit |
| # Remove-Job -Job $jobs -Force |
| # indicate failure using this exit code |
| exit 1 |
| } |
| |
| exit 0 |
| |
| |