Make REPL tests parallel too

pull/3522/head
Delyan Angelov 2020-01-20 18:06:36 +02:00 committed by Alexander Medvednikov
parent 84438c0139
commit 64a9f43405
4 changed files with 115 additions and 31 deletions

View File

@ -8,6 +8,8 @@ import (
os os
term term
readline readline
os.cmdline
filepath
) )
struct Repl { struct Repl {
@ -71,12 +73,13 @@ pub fn repl_help() {
') ')
} }
pub fn run_repl() []string { pub fn run_repl(workdir string, vrepl_prefix string) []string {
version := v_version() version := v_version()
println(version) println(version)
println('Use Ctrl-C or `exit` to exit') println('Use Ctrl-C or `exit` to exit')
file := '.vrepl.v'
temp_file := '.vrepl_temp.v' file := filepath.join( workdir, '.${vrepl_prefix}vrepl.v' )
temp_file := filepath.join( workdir, '.${vrepl_prefix}vrepl_temp.v')
mut prompt := '>>> ' mut prompt := '>>> '
defer { defer {
os.rm(file) os.rm(file)
@ -192,13 +195,19 @@ pub fn run_repl() []string {
fn print_output(s os.Result) { fn print_output(s os.Result) {
lines := s.output.split('\n') lines := s.output.split('\n')
for line in lines { for line in lines {
if line.starts_with('.vrepl_temp.v') { if line.contains('.vrepl_temp.v:') {
// Hide the temporary file name // Hide the temporary file name
idx := line.index(' ') or { sline := line.all_after('.vrepl_temp.v:')
println(line) idx := sline.index(' ') or {
println(sline)
return return
} }
println(line[idx+1..]) println(sline[idx+1..])
} else if line.contains('.vrepl.v:') {
// Ensure that .vrepl.v: is at the start, ignore the path
// This is needed to have stable .repl tests.
idx := line.index('.vrepl.v:') or { return }
println(line[idx..])
} else { } else {
println(line) println(line)
} }
@ -207,13 +216,20 @@ fn print_output(s os.Result) {
} }
fn main() { fn main() {
// Support for the parameters replfolder and replprefix is needed
// so that the repl can be launched in parallel by several different
// threads by the REPL test runner.
args := cmdline.after(os.args, ['repl'])
replfolder := os.realpath( cmdline.option(args, '-replfolder', '.') )
replprefix := cmdline.option(args, '-replprefix', 'noprefix.')
os.chdir( replfolder )
if !os.exists(os.getenv('VEXE')) { if !os.exists(os.getenv('VEXE')) {
println('Usage:') println('Usage:')
println(' VEXE=vexepath vrepl\n') println(' VEXE=vexepath vrepl\n')
println(' ... where vexepath is the full path to the v executable file') println(' ... where vexepath is the full path to the v executable file')
return return
} }
run_repl() run_repl( replfolder, replprefix )
} }
pub fn rerror(s string) { pub fn rerror(s string) {

View File

@ -3,6 +3,9 @@ module main
import os import os
import compiler.tests.repl.runner import compiler.tests.repl.runner
import benchmark import benchmark
import runtime
import sync
import filepath
fn test_the_v_compiler_can_be_invoked() { fn test_the_v_compiler_can_be_invoked() {
vexec := runner.full_path_to_v(5) vexec := runner.full_path_to_v(5)
@ -23,21 +26,79 @@ fn test_the_v_compiler_can_be_invoked() {
assert r_error.output == '`nonexisting.v` does not exist' assert r_error.output == '`nonexisting.v` does not exist'
} }
struct Session {
mut:
options runner.RunnerOptions
bmark benchmark.Benchmark
ntask int
ntask_mtx &sync.Mutex
waitgroup &sync.WaitGroup
}
fn test_all_v_repl_files() { fn test_all_v_repl_files() {
options := runner.new_options() mut session := &Session{
mut bmark := benchmark.new_benchmark() options: runner.new_options()
for file in options.files { bmark: benchmark.new_benchmark()
bmark.step() ntask: 0
fres := runner.run_repl_file(options.wd, options.vexec, file) or { ntask_mtx: sync.new_mutex()
bmark.fail() waitgroup: sync.new_waitgroup()
eprintln(bmark.step_message_fail(err)) }
// warmup, and ensure that the vrepl is compiled in single threaded mode if it does not exist
runner.run_repl_file(os.cachedir(), session.options.vexec, 'vlib/compiler/tests/repl/nothing.repl') or {
panic(err)
}
session.bmark.set_total_expected_steps( session.options.files.len )
mut ncpus := runtime.nr_cpus()
$if windows {
// See: https://docs.microsoft.com/en-us/cpp/build/reference/fs-force-synchronous-pdb-writes?view=vs-2019
ncpus = 1
}
session.waitgroup.add( ncpus )
for i:=0; i < ncpus; i++ {
go process_in_thread(session)
}
session.waitgroup.wait()
session.bmark.stop()
println(session.bmark.total_message('total time spent running REPL files'))
}
fn process_in_thread( session mut Session ){
cdir := os.cachedir()
mut tls_bench := benchmark.new_benchmark()
tls_bench.set_total_expected_steps( session.bmark.nexpected_steps )
for {
session.ntask_mtx.lock()
session.ntask++
idx := session.ntask-1
session.ntask_mtx.unlock()
if idx >= session.options.files.len { break }
tls_bench.cstep = idx
tfolder := filepath.join( cdir, 'vrepl_tests_$idx')
if os.is_dir( tfolder ) {
os.rmdir_recursive( tfolder )
}
os.mkdir( tfolder ) or { panic(err) }
file := os.realpath( filepath.join( session.options.wd, session.options.files[ idx ] ) )
session.bmark.step()
tls_bench.step()
fres := runner.run_repl_file(tfolder, session.options.vexec, file) or {
session.bmark.fail()
tls_bench.fail()
os.rmdir_recursive( tfolder )
eprintln(tls_bench.step_message_fail(err))
assert false assert false
continue continue
} }
bmark.ok() session.bmark.ok()
println(bmark.step_message_ok(fres)) tls_bench.ok()
os.rmdir_recursive( tfolder )
println(tls_bench.step_message_ok(fres))
assert true assert true
} }
bmark.stop() session.waitgroup.done()
println(bmark.total_message('total time spent running REPL files'))
} }

View File

@ -5,7 +5,7 @@ import (
filepath filepath
) )
struct RunnerOptions { pub struct RunnerOptions {
pub: pub:
wd string wd string
vexec string vexec string
@ -22,12 +22,12 @@ pub fn full_path_to_v(dirs_in int) string {
for i := 0; i < dirs_in; i++ { for i := 0; i < dirs_in; i++ {
path = filepath.dir(path) path = filepath.dir(path)
} }
vexec := path + os.path_separator + vname vexec := filepath.join( path, vname )
/* /*
args := os.args args := os.args
vreal := os.realpath('v') vreal := os.realpath('v')
myself := os.realpath( os.executable() ) myself := os.realpath( os.executable() )
wd := os.getwd() + os.path_separator wd := os.getwd()
println('args are: $args') println('args are: $args')
println('vreal : $vreal') println('vreal : $vreal')
println('myself : $myself') println('myself : $myself')
@ -56,16 +56,23 @@ pub fn run_repl_file(wd string, vexec string, file string) ?string {
input := content.all_before('===output===\n') input := content.all_before('===output===\n')
output := content.all_after('===output===\n') output := content.all_after('===output===\n')
input_temporary_filename := 'input_temporary_filename.txt' fname := filepath.filename( file )
input_temporary_filename := os.realpath(filepath.join( wd, 'input_temporary_filename.txt'))
os.write_file(input_temporary_filename, input) os.write_file(input_temporary_filename, input)
rcmd := '"$vexec" repl < $input_temporary_filename' os.write_file( os.realpath(filepath.join( wd, 'original.txt' ) ), fcontent )
rcmd := '"$vexec" repl -replfolder "$wd" -replprefix "${fname}." < $input_temporary_filename'
r := os.exec(rcmd) or { r := os.exec(rcmd) or {
os.rm(input_temporary_filename) os.rm(input_temporary_filename)
return error('Could not execute: $rcmd') return error('Could not execute: $rcmd')
} }
os.rm(input_temporary_filename) os.rm(input_temporary_filename)
result := r.output.replace('\r','').replace('>>> ', '').replace('>>>', '').replace('... ', '').all_after('Use Ctrl-C or `exit` to exit\n').replace(wd, '' ) result := r.output.replace('\r','')
.replace('>>> ', '')
.replace('>>>', '')
.replace('... ', '')
.all_after('Use Ctrl-C or `exit` to exit\n')
.replace(wd + os.path_separator, '' )
if result != output { if result != output {
file_result := '${file}.result.txt' file_result := '${file}.result.txt'
@ -120,7 +127,7 @@ $diff
} }
pub fn new_options() RunnerOptions { pub fn new_options() RunnerOptions {
wd := os.getwd() + os.path_separator wd := os.getwd()
vexec := full_path_to_v(5) vexec := full_path_to_v(5)
mut files := []string mut files := []string
if os.args.len > 1 { if os.args.len > 1 {
@ -136,7 +143,7 @@ pub fn new_options() RunnerOptions {
} }
pub fn new_prod_options() RunnerOptions { pub fn new_prod_options() RunnerOptions {
wd := os.getwd() + os.path_separator wd := os.getwd()
vexec := full_path_to_v(4) vexec := full_path_to_v(4)
mut files := []string mut files := []string
if os.args.len > 1 { if os.args.len > 1 {