3277d172ba
PowerShell seems to have a hard time when a flood of async tasks is scheduled at the same time. I speculated that WaitForMultipleObjects() in Windows can only take up to 64 process handles and if you want to handle more than you should write some additional code which can be sub-optimal. I.e to implement Wait-Job -Any. I decided to test that suggestion and introduced a $Concurrency parameter with a default value of 62. So in the new version the script fires up up to $Concurrency value and wait for anything to complete before starting any more processes. This improved matters greatly. Individual tests against ramdrive now run in 8 minutes and all of the 200+ db_tests run in 9 minutes with concurrency values of 8-16. About 48 is required to load a CPU on my box running against HD but that does not improve running times much. Other changes include respect -EnableJE for the individual test exes. Enforce exclusions for the individual tests.
273 lines
7.4 KiB
PowerShell
273 lines
7.4 KiB
PowerShell
# This script enables you running RocksDB tests by running
|
|
# All the tests in paralell and utilizing all the cores
|
|
# For db_test the script first lists and parses the tests
|
|
# and then fires them up in parallel using async PS Job functionality
|
|
# Run the script from the enlistment
|
|
Param(
|
|
[switch]$EnableJE = $false, # Use je executable
|
|
[string]$WorkFolder = "", # Direct tests to use that folder
|
|
[int]$Limit = -1, # -1 means run all otherwise limit for testing purposes
|
|
[string]$Exclude = "", # Expect a comma separated list, no spaces
|
|
[string]$Run = "db_test", # Run db_test|tests
|
|
# 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.
|
|
[int]$Concurrency = 62
|
|
)
|
|
|
|
# Folders and commands must be fullpath to run assuming
|
|
# the current folder is at the root of the git enlistment
|
|
Get-Date
|
|
|
|
# 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
|
|
}
|
|
|
|
# Use JEMALLOC executables
|
|
if($EnableJE) {
|
|
$db_test = -Join ($BinariesFolder, "db_test_je.exe")
|
|
} else {
|
|
$db_test = -Join ($BinariesFolder, "db_test.exe")
|
|
}
|
|
|
|
Write-Output "Root: $RootFolder, WorkFolder: $WorkFolder"
|
|
Write-Output "Binaries: $BinariesFolder exe: $db_test"
|
|
|
|
#Exclusions that we do not want to run
|
|
$ExcludeTests = New-Object System.Collections.Generic.HashSet[string]
|
|
|
|
|
|
if($Exclude -ne "") {
|
|
Write-Host "Exclude: $Exclude"
|
|
$l = $Exclude -split ','
|
|
ForEach($t in $l) { $ExcludeTests.Add($t) | Out-Null }
|
|
}
|
|
|
|
# Create test directories in the current folder
|
|
md -Path $WorkFolder -ErrorAction Ignore | Out-Null
|
|
md -Path $LogFolder -ErrorAction Ignore | 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 Normalize-DbTests($HashTable) {
|
|
|
|
$Tests = @()
|
|
# Run db_test to get a list of tests and store it into $a array
|
|
&$db_test --gtest_list_tests | tee -Variable Tests | Out-Null
|
|
|
|
# Current group
|
|
$Group=""
|
|
|
|
ForEach( $l in $Tests) {
|
|
# Trailing dot is a test group
|
|
if( $l -match "\.$") {
|
|
$Group = $l
|
|
} else {
|
|
# Otherwise it is a test name, remove leading space
|
|
$test = $l -replace '^\s+',''
|
|
# remove trailing comment if any and create a log name
|
|
$test = $test -replace '\s+\#.*',''
|
|
$test = "$Group$test"
|
|
|
|
if($ExcludeTests.Contains($test)) {
|
|
continue
|
|
}
|
|
|
|
$test_log = $test -replace '[\./]','_'
|
|
$test_log += ".log"
|
|
|
|
# Add to a hashtable
|
|
$HashTable.Add($test, $test_log);
|
|
}
|
|
}
|
|
}
|
|
|
|
# The function scans build\Debug folder to discover
|
|
# Test executables. It then populates a table with
|
|
# Test executable name -> Log file
|
|
function Discover-TestBinaries($HashTable) {
|
|
|
|
$Exclusions = @("db_test*", "db_sanity_test*")
|
|
if($EnableJE) {
|
|
$p = -join ($BinariesFolder, "*_test_je.exe")
|
|
} else {
|
|
$p = -join ($BinariesFolder, "*_test.exe")
|
|
}
|
|
|
|
dir -Path $p -Exclude $Exclusions | ForEach-Object {
|
|
$t = ($_.Name) -replace '.exe$', ''
|
|
if($ExcludeTests.Contains($t)) {
|
|
continue
|
|
}
|
|
$test_log = -join ($t, ".log")
|
|
$HashTable.Add($t, $test_log)
|
|
}
|
|
}
|
|
|
|
$TestToLog = [ordered]@{}
|
|
|
|
if($Run -ceq "db_test") {
|
|
Normalize-DbTests -HashTable $TestToLog
|
|
} elseif($Run -ceq "tests") {
|
|
Discover-TestBinaries -HashTable $TestToLog
|
|
} else {
|
|
Write-Warning "Invalid -Run option value"
|
|
exit 2
|
|
}
|
|
|
|
|
|
Write-Host "Attempting to start: " ($TestToLog.Count) " 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
|
|
}
|
|
|
|
$jobs = @()
|
|
$JobToLog = @{}
|
|
# Test limiting factor here
|
|
$count = 0
|
|
|
|
[bool]$success = $true;
|
|
|
|
# Wait for all to finish and get the results
|
|
while(($JobToLog.Count -gt 0) -or
|
|
($TestToLog.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 $Concurrency) -and
|
|
(($TestToLog.Count -gt 0) -and
|
|
(($Limit -lt 0) -or ($count -lt $Limit)))) {
|
|
|
|
|
|
# We only need the first key
|
|
foreach($key in $TestToLog.keys) {
|
|
$k = $key
|
|
break
|
|
}
|
|
|
|
Write-Host "Starting $k"
|
|
$log_path = -join ($LogFolder, ($TestToLog.$k))
|
|
|
|
if($Run -ceq "db_test") {
|
|
$job = Start-Job -Name $k -ScriptBlock $InvokeTestCase -ArgumentList @($db_test,$k,$log_path)
|
|
} else {
|
|
[string]$Exe = -Join ($BinariesFolder, $k)
|
|
$job = Start-Job -Name $k -ScriptBlock $InvokeTestAsync -ArgumentList @($exe,$log_path)
|
|
}
|
|
|
|
$JobToLog.Add($job, $log_path)
|
|
$TestToLog.Remove($k)
|
|
|
|
++$count
|
|
}
|
|
|
|
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 occurence 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 "^PASSED") -or
|
|
($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
|
|
}
|
|
|
|
Get-Date
|
|
|
|
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 12345
|
|
}
|
|
|
|
|