run_ci_db_test.ps1 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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("backupable_db_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. #
  105. # into this:
  106. #
  107. # DBTest.Empty
  108. # DBTest.WriteEmptyBatch
  109. # MultiThreaded/MultiThreadedDBTest.MultiThreaded/0
  110. # MultiThreaded/MultiThreadedDBTest.MultiThreaded/1
  111. #
  112. # Output into the parameter in a form TestName -> Log File Name
  113. function ExtractTestCases([string]$GTestExe, $HashTable) {
  114. $Tests = @()
  115. # Run db_test to get a list of tests and store it into $a array
  116. &$GTestExe --gtest_list_tests | tee -Variable Tests | Out-Null
  117. # Current group
  118. $Group=""
  119. ForEach( $l in $Tests) {
  120. # Leading whitespace is fine
  121. $l = $l -replace '^\s+',''
  122. # Trailing dot is a test group but no whitespace
  123. if ($l -match "\.$" -and $l -notmatch "\s+") {
  124. $Group = $l
  125. } else {
  126. # Otherwise it is a test name, remove leading space
  127. $test = $l
  128. # remove trailing comment if any and create a log name
  129. $test = $test -replace '\s+\#.*',''
  130. $test = "$Group$test"
  131. if($ExcludeCasesSet.Contains($test)) {
  132. Write-Warning "$test case is excluded"
  133. continue
  134. }
  135. $test_log = $test -replace '[\./]','_'
  136. $test_log += ".log"
  137. $log_path = -join ($LogFolder, $test_log)
  138. # Add to a hashtable
  139. $HashTable.Add($test, $log_path);
  140. }
  141. }
  142. }
  143. # The function removes trailing .exe siffix if any,
  144. # creates a name for the log file
  145. # Then adds the test name if it was not excluded into
  146. # a HashTable in a form of test_name -> log_path
  147. function MakeAndAdd([string]$token, $HashTable) {
  148. $test_name = $token -replace '.exe$', ''
  149. $log_name = -join ($test_name, ".log")
  150. $log_path = -join ($LogFolder, $log_name)
  151. $HashTable.Add($test_name, $log_path)
  152. }
  153. # This function takes a list of Suites to run
  154. # Lists all the test cases in each of the suite
  155. # and populates HashOfHashes
  156. # Ordered by suite(exe) @{ Exe = @{ TestCase = LogName }}
  157. function ProcessSuites($ListOfSuites, $HashOfHashes) {
  158. $suite_list = $ListOfSuites
  159. # Problem: if you run --gtest_list_tests on
  160. # a non Google Test executable then it will start executing
  161. # and we will get nowhere
  162. ForEach($suite in $suite_list) {
  163. if($RunOnly.Contains($suite)) {
  164. Write-Warning "$suite is excluded from running as Google test suite"
  165. continue
  166. }
  167. if($EnableJE) {
  168. $suite += "_je"
  169. }
  170. $Cases = [ordered]@{}
  171. $Cases.Clear()
  172. $suite_exe = -Join ($BinariesFolder, $suite)
  173. ExtractTestCases -GTestExe $suite_exe -HashTable $Cases
  174. if($Cases.Count -gt 0) {
  175. $HashOfHashes.Add($suite, $Cases);
  176. }
  177. }
  178. # Make logs and run
  179. if($CasesToRun.Count -lt 1) {
  180. Write-Error "Failed to extract tests from $SuiteRun"
  181. exit 1
  182. }
  183. }
  184. # This will contain all test executables to run
  185. # Hash table that contains all non suite
  186. # Test executable to run
  187. $TestExes = [ordered]@{}
  188. # Check for test exe that are not
  189. # Google Test Suites
  190. # Since this is explicitely mentioned it is not subject
  191. # for exclusions
  192. if($Run -ne "") {
  193. $test_list = $Run -split ' '
  194. ForEach($t in $test_list) {
  195. if($EnableJE) {
  196. $t += "_je"
  197. }
  198. MakeAndAdd -token $t -HashTable $TestExes
  199. }
  200. if($TestExes.Count -lt 1) {
  201. Write-Error "Failed to extract tests from $Run"
  202. exit 1
  203. }
  204. } elseif($RunAllExe) {
  205. # Discover all the test binaries
  206. if($EnableJE) {
  207. $pattern = "*_test_je.exe"
  208. } else {
  209. $pattern = "*_test.exe"
  210. }
  211. $search_path = -join ($BinariesFolder, $pattern)
  212. Write-Host "Binaries Search Path: $search_path"
  213. $DiscoveredExe = @()
  214. dir -Path $search_path | ForEach-Object {
  215. $DiscoveredExe += ($_.Name)
  216. }
  217. # Remove exclusions
  218. ForEach($e in $DiscoveredExe) {
  219. $e = $e -replace '.exe$', ''
  220. $bare_name = $e -replace '_je$', ''
  221. if($ExcludeExesSet.Contains($bare_name)) {
  222. Write-Warning "Test $e is excluded"
  223. continue
  224. }
  225. MakeAndAdd -token $e -HashTable $TestExes
  226. }
  227. if($TestExes.Count -lt 1) {
  228. Write-Error "Failed to discover test executables"
  229. exit 1
  230. }
  231. }
  232. # Ordered by exe @{ Exe = @{ TestCase = LogName }}
  233. $CasesToRun = [ordered]@{}
  234. if($SuiteRun -ne "") {
  235. $suite_list = $SuiteRun -split ' '
  236. ProcessSuites -ListOfSuites $suite_list -HashOfHashes $CasesToRun
  237. } elseif ($RunAll) {
  238. # Discover all the test binaries
  239. if($EnableJE) {
  240. $pattern = "*_test_je.exe"
  241. } else {
  242. $pattern = "*_test.exe"
  243. }
  244. $search_path = -join ($BinariesFolder, $pattern)
  245. Write-Host "Binaries Search Path: $search_path"
  246. $ListOfExe = @()
  247. dir -Path $search_path | ForEach-Object {
  248. $ListOfExe += ($_.Name)
  249. }
  250. # Exclude those in RunOnly from running as suites
  251. $ListOfSuites = @()
  252. ForEach($e in $ListOfExe) {
  253. $e = $e -replace '.exe$', ''
  254. $bare_name = $e -replace '_je$', ''
  255. if($ExcludeExesSet.Contains($bare_name)) {
  256. Write-Warning "Test $e is excluded"
  257. continue
  258. }
  259. if($RunOnly.Contains($bare_name)) {
  260. MakeAndAdd -token $e -HashTable $TestExes
  261. } else {
  262. $ListOfSuites += $bare_name
  263. }
  264. }
  265. ProcessSuites -ListOfSuites $ListOfSuites -HashOfHashes $CasesToRun
  266. }
  267. # Invoke a test with a filter and redirect all output
  268. $InvokeTestCase = {
  269. param($exe, $test, $log);
  270. &$exe --gtest_filter=$test > $log 2>&1
  271. }
  272. # Invoke all tests and redirect output
  273. $InvokeTestAsync = {
  274. param($exe, $log)
  275. &$exe > $log 2>&1
  276. }
  277. # Hash that contains tests to rerun if any failed
  278. # Those tests will be rerun sequentially
  279. # $Rerun = [ordered]@{}
  280. # Test limiting factor here
  281. [int]$count = 0
  282. # Overall status
  283. [bool]$script:success = $true;
  284. function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal)
  285. {
  286. # Array to wait for any of the running jobs
  287. $jobs = @()
  288. # Hash JobToLog
  289. $JobToLog = @{}
  290. # Wait for all to finish and get the results
  291. while(($JobToLog.Count -gt 0) -or
  292. ($TestCmds.Count -gt 0) -or
  293. ($Suites.Count -gt 0)) {
  294. # Make sure we have maximum concurrent jobs running if anything
  295. # and the $Limit either not set or allows to proceed
  296. while(($JobToLog.Count -lt $ConcurrencyVal) -and
  297. ((($TestCmds.Count -gt 0) -or ($Suites.Count -gt 0)) -and
  298. (($Limit -lt 0) -or ($count -lt $Limit)))) {
  299. # We always favore suites to run if available
  300. [string]$exe_name = ""
  301. [string]$log_path = ""
  302. $Cases = @{}
  303. if($Suites.Count -gt 0) {
  304. # Will the first one
  305. ForEach($e in $Suites.Keys) {
  306. $exe_name = $e
  307. $Cases = $Suites[$e]
  308. break
  309. }
  310. [string]$test_case = ""
  311. [string]$log_path = ""
  312. ForEach($c in $Cases.Keys) {
  313. $test_case = $c
  314. $log_path = $Cases[$c]
  315. break
  316. }
  317. Write-Host "Starting $exe_name::$test_case"
  318. [string]$Exe = -Join ($BinariesFolder, $exe_name)
  319. $job = Start-Job -Name "$exe_name::$test_case" -ArgumentList @($Exe,$test_case,$log_path) -ScriptBlock $InvokeTestCase
  320. $JobToLog.Add($job, $log_path)
  321. $Cases.Remove($test_case)
  322. if($Cases.Count -lt 1) {
  323. $Suites.Remove($exe_name)
  324. }
  325. } elseif ($TestCmds.Count -gt 0) {
  326. ForEach($e in $TestCmds.Keys) {
  327. $exe_name = $e
  328. $log_path = $TestCmds[$e]
  329. break
  330. }
  331. Write-Host "Starting $exe_name"
  332. [string]$Exe = -Join ($BinariesFolder, $exe_name)
  333. $job = Start-Job -Name $exe_name -ScriptBlock $InvokeTestAsync -ArgumentList @($Exe,$log_path)
  334. $JobToLog.Add($job, $log_path)
  335. $TestCmds.Remove($exe_name)
  336. } else {
  337. Write-Error "In the job loop but nothing to run"
  338. exit 1
  339. }
  340. ++$count
  341. } # End of Job starting loop
  342. if($JobToLog.Count -lt 1) {
  343. break
  344. }
  345. $jobs = @()
  346. foreach($k in $JobToLog.Keys) { $jobs += $k }
  347. $completed = Wait-Job -Job $jobs -Any
  348. $log = $JobToLog[$completed]
  349. $JobToLog.Remove($completed)
  350. $message = -join @($completed.Name, " State: ", ($completed.State))
  351. $log_content = @(Get-Content $log)
  352. if($completed.State -ne "Completed") {
  353. $script:success = $false
  354. Write-Warning $message
  355. $log_content | Write-Warning
  356. } else {
  357. # Scan the log. If we find PASSED and no occurrence of FAILED
  358. # then it is a success
  359. [bool]$pass_found = $false
  360. ForEach($l in $log_content) {
  361. if(($l -match "^\[\s+FAILED") -or
  362. ($l -match "Assertion failed:")) {
  363. $pass_found = $false
  364. break
  365. }
  366. if(($l -match "^\[\s+PASSED") -or
  367. ($l -match " : PASSED$") -or
  368. ($l -match "^PASS$") -or # Special c_test case
  369. ($l -match "Passed all tests!") ) {
  370. $pass_found = $true
  371. }
  372. }
  373. if(!$pass_found) {
  374. $script:success = $false;
  375. Write-Warning $message
  376. $log_content | Write-Warning
  377. } else {
  378. Write-Host $message
  379. }
  380. }
  381. # Remove cached job info from the system
  382. # Should be no output
  383. Receive-Job -Job $completed | Out-Null
  384. }
  385. }
  386. RunJobs -Suites $CasesToRun -TestCmds $TestExes -ConcurrencyVal $Concurrency
  387. $EndDate = (Get-Date)
  388. New-TimeSpan -Start $StartDate -End $EndDate |
  389. ForEach-Object {
  390. "Elapsed time: {0:g}" -f $_
  391. }
  392. if(!$script:success) {
  393. # This does not succeed killing off jobs quick
  394. # So we simply exit
  395. # Remove-Job -Job $jobs -Force
  396. # indicate failure using this exit code
  397. exit 1
  398. }
  399. exit 0