run_ci_db_test.ps1 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  2. # This script enables you running RocksDB tests by running
  3. # All the tests concurrently and utilizing all the cores
  4. Param(
  5. [switch]$EnableJE = $false, # Look for and use test executable, append _je to listed exclusions
  6. [switch]$RunAll = $false, # Will attempt discover all *_test[_je].exe binaries and run all
  7. # of them as Google suites. I.e. It will run test cases concurrently
  8. # except those mentioned as $Run, those will run as individual test cases
  9. # And any execlued with $ExcludeExes or $ExcludeCases
  10. # It will also not run any individual test cases
  11. # excluded but $ExcludeCasese
  12. [switch]$RunAllExe = $false, # Look for and use test exdcutables, append _je to exclusions automatically
  13. # It will attempt to run them in parallel w/o breaking them up on individual
  14. # test cases. Those listed with $ExcludeExes will be excluded
  15. [string]$SuiteRun = "", # Split test suites in test cases and run in parallel, not compatible with $RunAll
  16. [string]$Run = "", # Run specified executables in parallel but do not split to test cases
  17. [string]$ExcludeCases = "", # Exclude test cases, expects a comma separated list, no spaces
  18. # Takes effect when $RunAll or $SuiteRun is specified. Must have full
  19. # Test cases name including a group and a parameter if any
  20. [string]$ExcludeExes = "", # Exclude exes from consideration, expects a comma separated list,
  21. # no spaces. Takes effect only when $RunAll is specified
  22. [string]$WorkFolder = "", # Direct tests to use that folder. SSD or Ram drive are better options.
  23. # Number of async tasks that would run concurrently. Recommend a number below 64.
  24. # However, CPU utlization really depends on the storage media. Recommend ram based disk.
  25. # a value of 1 will run everything serially
  26. [int]$Concurrency = 8,
  27. [int]$Limit = -1 # -1 means do not limit for test purposes
  28. )
  29. # Folders and commands must be fullpath to run assuming
  30. # the current folder is at the root of the git enlistment
  31. $StartDate = (Get-Date)
  32. $StartDate
  33. $DebugPreference = "Continue"
  34. # These tests are not google test suites and we should guard
  35. # Against running them as suites
  36. $RunOnly = New-Object System.Collections.Generic.HashSet[string]
  37. $RunOnly.Add("c_test") | Out-Null
  38. $RunOnly.Add("compact_on_deletion_collector_test") | Out-Null
  39. $RunOnly.Add("merge_test") | Out-Null
  40. $RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written
  41. $RunOnly.Add("backup_engine_test") | Out-Null # Disabled
  42. $RunOnly.Add("timer_queue_test") | Out-Null # Not a gtest
  43. if($RunAll -and $SuiteRun -ne "") {
  44. Write-Error "$RunAll and $SuiteRun are not compatible"
  45. exit 1
  46. }
  47. if($RunAllExe -and $Run -ne "") {
  48. Write-Error "$RunAllExe and $Run are not compatible"
  49. exit 1
  50. }
  51. # If running under Appveyor assume that root
  52. [string]$Appveyor = $Env:APPVEYOR_BUILD_FOLDER
  53. if($Appveyor -ne "") {
  54. $RootFolder = $Appveyor
  55. } else {
  56. $RootFolder = $PSScriptRoot -replace '\\build_tools', ''
  57. }
  58. $LogFolder = -Join($RootFolder, "\db_logs\")
  59. $BinariesFolder = -Join($RootFolder, "\build\Debug\")
  60. if($WorkFolder -eq "") {
  61. # If TEST_TMPDIR is set use it
  62. [string]$var = $Env:TEST_TMPDIR
  63. if($var -eq "") {
  64. $WorkFolder = -Join($RootFolder, "\db_tests\")
  65. $Env:TEST_TMPDIR = $WorkFolder
  66. } else {
  67. $WorkFolder = $var
  68. }
  69. } else {
  70. # Override from a command line
  71. $Env:TEST_TMPDIR = $WorkFolder
  72. }
  73. Write-Output "Root: $RootFolder, WorkFolder: $WorkFolder"
  74. Write-Output "BinariesFolder: $BinariesFolder, LogFolder: $LogFolder"
  75. # Create test directories in the current folder
  76. md -Path $WorkFolder -ErrorAction Ignore | Out-Null
  77. md -Path $LogFolder -ErrorAction Ignore | Out-Null
  78. $ExcludeCasesSet = New-Object System.Collections.Generic.HashSet[string]
  79. if($ExcludeCases -ne "") {
  80. Write-Host "ExcludeCases: $ExcludeCases"
  81. $l = $ExcludeCases -split ' '
  82. ForEach($t in $l) {
  83. $ExcludeCasesSet.Add($t) | Out-Null
  84. }
  85. }
  86. $ExcludeExesSet = New-Object System.Collections.Generic.HashSet[string]
  87. if($ExcludeExes -ne "") {
  88. Write-Host "ExcludeExe: $ExcludeExes"
  89. $l = $ExcludeExes -split ' '
  90. ForEach($t in $l) {
  91. $ExcludeExesSet.Add($t) | Out-Null
  92. }
  93. }
  94. # Extract the names of its tests by running db_test with --gtest_list_tests.
  95. # This filter removes the "#"-introduced comments, and expands to
  96. # fully-qualified names by changing input like this:
  97. #
  98. # DBTest.
  99. # Empty
  100. # WriteEmptyBatch
  101. # MultiThreaded/MultiThreadedDBTest.
  102. # MultiThreaded/0 # GetParam() = 0
  103. # MultiThreaded/1 # GetParam() = 1
  104. # RibbonTypeParamTest/0. # TypeParam = struct DefaultTypesAndSettings
  105. # CompactnessAndBacktrackAndFpRate
  106. # Extremes
  107. # FindOccupancyForSuccessRate
  108. #
  109. # into this:
  110. #
  111. # DBTest.Empty
  112. # DBTest.WriteEmptyBatch
  113. # MultiThreaded/MultiThreadedDBTest.MultiThreaded/0
  114. # MultiThreaded/MultiThreadedDBTest.MultiThreaded/1
  115. # RibbonTypeParamTest/0.CompactnessAndBacktrackAndFpRate
  116. # RibbonTypeParamTest/0.Extremes
  117. # RibbonTypeParamTest/0.FindOccupancyForSuccessRate
  118. #
  119. # Output into the parameter in a form TestName -> Log File Name
  120. function ExtractTestCases([string]$GTestExe, $HashTable) {
  121. $Tests = @()
  122. # Run db_test to get a list of tests and store it into $a array
  123. &$GTestExe --gtest_list_tests | tee -Variable Tests | Out-Null
  124. # Current group
  125. $Group=""
  126. ForEach( $l in $Tests) {
  127. # remove trailing comment if any
  128. $l = $l -replace '\s+\#.*',''
  129. # Leading whitespace is fine
  130. $l = $l -replace '^\s+',''
  131. # Trailing dot is a test group but no whitespace
  132. if ($l -match "\.$" -and $l -notmatch "\s+") {
  133. $Group = $l
  134. } else {
  135. # Otherwise it is a test name, remove leading space
  136. $test = $l
  137. # create a log name
  138. $test = "$Group$test"
  139. if($ExcludeCasesSet.Contains($test)) {
  140. Write-Warning "$test case is excluded"
  141. continue
  142. }
  143. $test_log = $test -replace '[\./]','_'
  144. $test_log += ".log"
  145. $log_path = -join ($LogFolder, $test_log)
  146. # Add to a hashtable
  147. $HashTable.Add($test, $log_path);
  148. }
  149. }
  150. }
  151. # The function removes trailing .exe siffix if any,
  152. # creates a name for the log file
  153. # Then adds the test name if it was not excluded into
  154. # a HashTable in a form of test_name -> log_path
  155. function MakeAndAdd([string]$token, $HashTable) {
  156. $test_name = $token -replace '.exe$', ''
  157. $log_name = -join ($test_name, ".log")
  158. $log_path = -join ($LogFolder, $log_name)
  159. $HashTable.Add($test_name, $log_path)
  160. }
  161. # This function takes a list of Suites to run
  162. # Lists all the test cases in each of the suite
  163. # and populates HashOfHashes
  164. # Ordered by suite(exe) @{ Exe = @{ TestCase = LogName }}
  165. function ProcessSuites($ListOfSuites, $HashOfHashes) {
  166. $suite_list = $ListOfSuites
  167. # Problem: if you run --gtest_list_tests on
  168. # a non Google Test executable then it will start executing
  169. # and we will get nowhere
  170. ForEach($suite in $suite_list) {
  171. if($RunOnly.Contains($suite)) {
  172. Write-Warning "$suite is excluded from running as Google test suite"
  173. continue
  174. }
  175. if($EnableJE) {
  176. $suite += "_je"
  177. }
  178. $Cases = [ordered]@{}
  179. $Cases.Clear()
  180. $suite_exe = -Join ($BinariesFolder, $suite)
  181. ExtractTestCases -GTestExe $suite_exe -HashTable $Cases
  182. if($Cases.Count -gt 0) {
  183. $HashOfHashes.Add($suite, $Cases);
  184. }
  185. }
  186. # Make logs and run
  187. if($CasesToRun.Count -lt 1) {
  188. Write-Error "Failed to extract tests from $SuiteRun"
  189. exit 1
  190. }
  191. }
  192. # This will contain all test executables to run
  193. # Hash table that contains all non suite
  194. # Test executable to run
  195. $TestExes = [ordered]@{}
  196. # Check for test exe that are not
  197. # Google Test Suites
  198. # Since this is explicitely mentioned it is not subject
  199. # for exclusions
  200. if($Run -ne "") {
  201. $test_list = $Run -split ' '
  202. ForEach($t in $test_list) {
  203. if($EnableJE) {
  204. $t += "_je"
  205. }
  206. MakeAndAdd -token $t -HashTable $TestExes
  207. }
  208. if($TestExes.Count -lt 1) {
  209. Write-Error "Failed to extract tests from $Run"
  210. exit 1
  211. }
  212. } elseif($RunAllExe) {
  213. # Discover all the test binaries
  214. if($EnableJE) {
  215. $pattern = "*_test_je.exe"
  216. } else {
  217. $pattern = "*_test.exe"
  218. }
  219. $search_path = -join ($BinariesFolder, $pattern)
  220. Write-Host "Binaries Search Path: $search_path"
  221. $DiscoveredExe = @()
  222. dir -Path $search_path | ForEach-Object {
  223. $DiscoveredExe += ($_.Name)
  224. }
  225. # Remove exclusions
  226. ForEach($e in $DiscoveredExe) {
  227. $e = $e -replace '.exe$', ''
  228. $bare_name = $e -replace '_je$', ''
  229. if($ExcludeExesSet.Contains($bare_name)) {
  230. Write-Warning "Test $e is excluded"
  231. continue
  232. }
  233. MakeAndAdd -token $e -HashTable $TestExes
  234. }
  235. if($TestExes.Count -lt 1) {
  236. Write-Error "Failed to discover test executables"
  237. exit 1
  238. }
  239. }
  240. # Ordered by exe @{ Exe = @{ TestCase = LogName }}
  241. $CasesToRun = [ordered]@{}
  242. if($SuiteRun -ne "") {
  243. $suite_list = $SuiteRun -split ' '
  244. ProcessSuites -ListOfSuites $suite_list -HashOfHashes $CasesToRun
  245. } elseif ($RunAll) {
  246. # Discover all the test binaries
  247. if($EnableJE) {
  248. $pattern = "*_test_je.exe"
  249. } else {
  250. $pattern = "*_test.exe"
  251. }
  252. $search_path = -join ($BinariesFolder, $pattern)
  253. Write-Host "Binaries Search Path: $search_path"
  254. $ListOfExe = @()
  255. dir -Path $search_path | ForEach-Object {
  256. $ListOfExe += ($_.Name)
  257. }
  258. # Exclude those in RunOnly from running as suites
  259. $ListOfSuites = @()
  260. ForEach($e in $ListOfExe) {
  261. $e = $e -replace '.exe$', ''
  262. $bare_name = $e -replace '_je$', ''
  263. if($ExcludeExesSet.Contains($bare_name)) {
  264. Write-Warning "Test $e is excluded"
  265. continue
  266. }
  267. if($RunOnly.Contains($bare_name)) {
  268. MakeAndAdd -token $e -HashTable $TestExes
  269. } else {
  270. $ListOfSuites += $bare_name
  271. }
  272. }
  273. ProcessSuites -ListOfSuites $ListOfSuites -HashOfHashes $CasesToRun
  274. }
  275. # Invoke a test with a filter and redirect all output
  276. $InvokeTestCase = {
  277. param($exe, $test, $log);
  278. &$exe --gtest_filter=$test > $log 2>&1
  279. }
  280. # Invoke all tests and redirect output
  281. $InvokeTestAsync = {
  282. param($exe, $log)
  283. &$exe > $log 2>&1
  284. }
  285. # Hash that contains tests to rerun if any failed
  286. # Those tests will be rerun sequentially
  287. # $Rerun = [ordered]@{}
  288. # Test limiting factor here
  289. [int]$count = 0
  290. # Overall status
  291. [bool]$script:success = $true;
  292. function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal)
  293. {
  294. # Array to wait for any of the running jobs
  295. $jobs = @()
  296. # Hash JobToLog
  297. $JobToLog = @{}
  298. # Wait for all to finish and get the results
  299. while(($JobToLog.Count -gt 0) -or
  300. ($TestCmds.Count -gt 0) -or
  301. ($Suites.Count -gt 0)) {
  302. # Make sure we have maximum concurrent jobs running if anything
  303. # and the $Limit either not set or allows to proceed
  304. while(($JobToLog.Count -lt $ConcurrencyVal) -and
  305. ((($TestCmds.Count -gt 0) -or ($Suites.Count -gt 0)) -and
  306. (($Limit -lt 0) -or ($count -lt $Limit)))) {
  307. # We always favore suites to run if available
  308. [string]$exe_name = ""
  309. [string]$log_path = ""
  310. $Cases = @{}
  311. if($Suites.Count -gt 0) {
  312. # Will the first one
  313. ForEach($e in $Suites.Keys) {
  314. $exe_name = $e
  315. $Cases = $Suites[$e]
  316. break
  317. }
  318. [string]$test_case = ""
  319. [string]$log_path = ""
  320. ForEach($c in $Cases.Keys) {
  321. $test_case = $c
  322. $log_path = $Cases[$c]
  323. break
  324. }
  325. Write-Host "Starting $exe_name::$test_case"
  326. [string]$Exe = -Join ($BinariesFolder, $exe_name)
  327. $job = Start-Job -Name "$exe_name::$test_case" -ArgumentList @($Exe,$test_case,$log_path) -ScriptBlock $InvokeTestCase
  328. $JobToLog.Add($job, $log_path)
  329. $Cases.Remove($test_case)
  330. if($Cases.Count -lt 1) {
  331. $Suites.Remove($exe_name)
  332. }
  333. } elseif ($TestCmds.Count -gt 0) {
  334. ForEach($e in $TestCmds.Keys) {
  335. $exe_name = $e
  336. $log_path = $TestCmds[$e]
  337. break
  338. }
  339. Write-Host "Starting $exe_name"
  340. [string]$Exe = -Join ($BinariesFolder, $exe_name)
  341. $job = Start-Job -Name $exe_name -ScriptBlock $InvokeTestAsync -ArgumentList @($Exe,$log_path)
  342. $JobToLog.Add($job, $log_path)
  343. $TestCmds.Remove($exe_name)
  344. } else {
  345. Write-Error "In the job loop but nothing to run"
  346. exit 1
  347. }
  348. ++$count
  349. } # End of Job starting loop
  350. if($JobToLog.Count -lt 1) {
  351. break
  352. }
  353. $jobs = @()
  354. foreach($k in $JobToLog.Keys) { $jobs += $k }
  355. $completed = Wait-Job -Job $jobs -Any
  356. $log = $JobToLog[$completed]
  357. $JobToLog.Remove($completed)
  358. $message = -join @($completed.Name, " State: ", ($completed.State))
  359. $log_content = @(Get-Content $log)
  360. if($completed.State -ne "Completed") {
  361. $script:success = $false
  362. Write-Warning $message
  363. $log_content | Write-Warning
  364. } else {
  365. # Scan the log. If we find PASSED and no occurrence of FAILED
  366. # then it is a success
  367. [bool]$pass_found = $false
  368. ForEach($l in $log_content) {
  369. if(($l -match "^\[\s+FAILED") -or
  370. ($l -match "Assertion failed:")) {
  371. $pass_found = $false
  372. break
  373. }
  374. if(($l -match "^\[\s+PASSED") -or
  375. ($l -match " : PASSED$") -or
  376. ($l -match "^PASS$") -or # Special c_test case
  377. ($l -match "Passed all tests!") ) {
  378. $pass_found = $true
  379. }
  380. }
  381. if(!$pass_found) {
  382. $script:success = $false;
  383. Write-Warning $message
  384. $log_content | Write-Warning
  385. } else {
  386. Write-Host $message
  387. }
  388. }
  389. # Remove cached job info from the system
  390. # Should be no output
  391. Receive-Job -Job $completed | Out-Null
  392. }
  393. }
  394. RunJobs -Suites $CasesToRun -TestCmds $TestExes -ConcurrencyVal $Concurrency
  395. $EndDate = (Get-Date)
  396. New-TimeSpan -Start $StartDate -End $EndDate |
  397. ForEach-Object {
  398. "Elapsed time: {0:g}" -f $_
  399. }
  400. if(!$script:success) {
  401. # This does not succeed killing off jobs quick
  402. # So we simply exit
  403. # Remove-Job -Job $jobs -Force
  404. # indicate failure using this exit code
  405. exit 1
  406. }
  407. exit 0