diff --git a/tools/vrepl.v b/tools/vrepl.v index ad885fbe0e..a14ad511e7 100644 --- a/tools/vrepl.v +++ b/tools/vrepl.v @@ -8,6 +8,8 @@ import ( os term readline + os.cmdline + filepath ) struct Repl { @@ -71,13 +73,14 @@ pub fn repl_help() { ') } -pub fn run_repl() []string { +pub fn run_repl(workdir string, vrepl_prefix string) []string { version := v_version() println(version) println('Use Ctrl-C or `exit` to exit') - file := '.vrepl.v' - temp_file := '.vrepl_temp.v' - mut prompt := '>>> ' + + file := filepath.join( workdir, '.${vrepl_prefix}vrepl.v' ) + temp_file := filepath.join( workdir, '.${vrepl_prefix}vrepl_temp.v') + mut prompt := '>>> ' defer { os.rm(file) os.rm(temp_file) @@ -192,14 +195,20 @@ pub fn run_repl() []string { fn print_output(s os.Result) { lines := s.output.split('\n') for line in lines { - if line.starts_with('.vrepl_temp.v') { + if line.contains('.vrepl_temp.v:') { // Hide the temporary file name - idx := line.index(' ') or { - println(line) + sline := line.all_after('.vrepl_temp.v:') + idx := sline.index(' ') or { + println(sline) return } - println(line[idx+1..]) - } else { + 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 { println(line) } } @@ -207,13 +216,20 @@ fn print_output(s os.Result) { } 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')) { println('Usage:') println(' VEXE=vexepath vrepl\n') println(' ... where vexepath is the full path to the v executable file') return } - run_repl() + run_repl( replfolder, replprefix ) } pub fn rerror(s string) { diff --git a/vlib/compiler/tests/repl/repl_test.v b/vlib/compiler/tests/repl/repl_test.v index dfd2b492be..107f7ff83c 100644 --- a/vlib/compiler/tests/repl/repl_test.v +++ b/vlib/compiler/tests/repl/repl_test.v @@ -3,6 +3,9 @@ module main import os import compiler.tests.repl.runner import benchmark +import runtime +import sync +import filepath fn test_the_v_compiler_can_be_invoked() { 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' } +struct Session { +mut: + options runner.RunnerOptions + bmark benchmark.Benchmark + ntask int + ntask_mtx &sync.Mutex + waitgroup &sync.WaitGroup +} + fn test_all_v_repl_files() { - options := runner.new_options() - mut bmark := benchmark.new_benchmark() - for file in options.files { - bmark.step() - fres := runner.run_repl_file(options.wd, options.vexec, file) or { - bmark.fail() - eprintln(bmark.step_message_fail(err)) + mut session := &Session{ + options: runner.new_options() + bmark: benchmark.new_benchmark() + ntask: 0 + ntask_mtx: sync.new_mutex() + waitgroup: sync.new_waitgroup() + } + + // 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 continue } - bmark.ok() - println(bmark.step_message_ok(fres)) + session.bmark.ok() + tls_bench.ok() + os.rmdir_recursive( tfolder ) + println(tls_bench.step_message_ok(fres)) assert true } - bmark.stop() - println(bmark.total_message('total time spent running REPL files')) + session.waitgroup.done() } diff --git a/vlib/compiler/tests/repl/runner/runner.v b/vlib/compiler/tests/repl/runner/runner.v index f7b3be59fa..7962a7d9c4 100644 --- a/vlib/compiler/tests/repl/runner/runner.v +++ b/vlib/compiler/tests/repl/runner/runner.v @@ -5,7 +5,7 @@ import ( filepath ) -struct RunnerOptions { +pub struct RunnerOptions { pub: wd string vexec string @@ -22,12 +22,12 @@ pub fn full_path_to_v(dirs_in int) string { for i := 0; i < dirs_in; i++ { path = filepath.dir(path) } - vexec := path + os.path_separator + vname + vexec := filepath.join( path, vname ) /* args := os.args vreal := os.realpath('v') myself := os.realpath( os.executable() ) - wd := os.getwd() + os.path_separator + wd := os.getwd() println('args are: $args') println('vreal : $vreal') println('myself : $myself') @@ -55,17 +55,24 @@ pub fn run_repl_file(wd string, vexec string, file string) ?string { content := fcontent.replace('\r', '') input := content.all_before('===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) - 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 { os.rm(input_temporary_filename) return error('Could not execute: $rcmd') } 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 { file_result := '${file}.result.txt' @@ -120,7 +127,7 @@ $diff } pub fn new_options() RunnerOptions { - wd := os.getwd() + os.path_separator + wd := os.getwd() vexec := full_path_to_v(5) mut files := []string if os.args.len > 1 { @@ -136,7 +143,7 @@ pub fn new_options() RunnerOptions { } pub fn new_prod_options() RunnerOptions { - wd := os.getwd() + os.path_separator + wd := os.getwd() vexec := full_path_to_v(4) mut files := []string if os.args.len > 1 { diff --git a/vlib/runtime/runtime_win.v b/vlib/runtime/runtime_windows.v similarity index 100% rename from vlib/runtime/runtime_win.v rename to vlib/runtime/runtime_windows.v