live: make live_test.v more robust

pull/5009/head
Delyan Angelov 2020-05-24 17:45:53 +03:00
parent 7c4f2b535b
commit d16485c29e
1 changed files with 122 additions and 28 deletions

View File

@ -1,36 +1,111 @@
import os import os
import time import time
/*
The goal of this test, is to simulate a developer, that has run a program, compiled with -live flag.
It does so by writing a new generated program containing a [live] fn pmessage() string {...} function,
then runs the generated program at the start *in the background*,
waits some time, so that the program could run a few iterations, then modifies its source
(simulates a developer that has saved a new version of the program source),
then it waits some more, modifies it again and saves it once more.
On each modification, the running program, should detect that its source code has changed,
and recompile a shared library, which it then it should load, and thus modify its own
behavior at runtime (the pmessage function).
If everything works fine, the output of the generated program would have changed at least 1-2 times,
which then is detected by the test program (the histogram checks).
Since this test program is sensitive to coordination (or lack of) of several processes,
it tries to sidestep the coordination issue by polling the file system for the existance
of files, ORIGINAL.txt ... STOP.txt , which are appended to by the generated program.
NB: That approach of monitoring the state of the running generated program, is clearly not ideal,
but sidesteps the issue of coordinating processes through IPC or stdin/stdout in hopefully
not very flaky way.
TODO: Cleanup this when/if v has better process control/communication primitives.
*/
const ( const (
vexe = os.getenv('VEXE') vexe = os.getenv('VEXE')
tmp_file = os.join_path(os.temp_dir(), 'generated_live_program.tmp.v')
source_file = os.join_path(os.temp_dir(), 'generated_live_program.v') source_file = os.join_path(os.temp_dir(), 'generated_live_program.v')
genexe_file = os.join_path(os.temp_dir(), 'generated_live_program')
output_file = os.join_path(os.temp_dir(), 'generated_live_program.output.txt') output_file = os.join_path(os.temp_dir(), 'generated_live_program.output.txt')
res_original_file = os.join_path(os.temp_dir(), 'ORIGINAL.txt')
res_changed_file = os.join_path(os.temp_dir(), 'CHANGED.txt')
res_another_file = os.join_path(os.temp_dir(), 'ANOTHER.txt')
res_stop_file = os.join_path(os.temp_dir(), 'STOP.txt')
live_program_source = " live_program_source = "
module main module main
import time import time
import os
import live
fn append_to_file(fname, s string) {
f := os.open_append(fname) or {
println('>>>> could not open file \$fname for appending, err: \$err ')
return
}
f.writeln('\$s')
//info := live.info()
//f.writeln('>>> reloads: \${info.reloads} | ok reloads: \${info.reloads_ok}')
f.flush()
f.close()
}
fn myprintln(s string) {
append_to_file('$output_file', s)
println(s)
os.flush()
}
[live] [live]
fn pmessage() { fn pmessage() string {
println('ORIGINAL') s := 'ORIGINAL'
myprintln(s)
return s
} }
const (
delay = 5
)
fn main() { fn main() {
println('START') mut info := live.info()
info.recheck_period_ms = 5
myprintln('START')
myprintln('DATE: ' + time.now().str())
pmessage() pmessage()
time.sleep_ms(10)
pmessage() pmessage()
for i := 0; i<3*100; i++ { // NB: 1000 * 5 = maximum of ~5s runtime
pmessage() for i:=0; i<1000; i++ {
time.sleep_ms(10) s := pmessage()
append_to_file(os.resource_abs_path(s + '.txt'), s)
time.sleep_ms(delay)
if s == 'STOP' {
break
}
} }
pmessage() pmessage()
time.sleep_ms(10)
pmessage() pmessage()
println('END') myprintln('DATE: ' + time.now().str())
myprintln('END')
} }
" "
) )
fn atomic_write_source( source string ){
// NB: here wrtiting is done in 2 steps, since os.write_file can take some time,
// during which the file will be modified, but it will still be not completely written.
// The os.mv after that, guarantees that the reloader will see a complete valid V program.
os.write_file(tmp_file, source)
os.mv(tmp_file, source_file )
}
// //
fn testsuite_begin() { fn testsuite_begin() {
if os.user_os() !in ['linux', 'solaris'] && os.getenv('FORCE_LIVE_TEST').len == 0 { if os.user_os() !in ['linux', 'solaris'] && os.getenv('FORCE_LIVE_TEST').len == 0 {
@ -39,16 +114,15 @@ fn testsuite_begin() {
eprintln('You can still do it by setting FORCE_LIVE_TEST=1 .') eprintln('You can still do it by setting FORCE_LIVE_TEST=1 .')
exit(0) exit(0)
} }
os.write_file(source_file, live_program_source) for f in [ tmp_file, source_file, output_file, res_original_file, res_changed_file, res_another_file, res_stop_file] {
os.rm(f)
}
atomic_write_source( live_program_source )
} }
fn testsuite_end() { fn testsuite_end() {
os.rm(source_file)
eprintln('source: $source_file') eprintln('source: $source_file')
eprintln('output: $output_file') eprintln('output: $output_file')
$if !windows {
os.system('cat $output_file | sort | uniq -c | sort -n')
}
println('---------------------------------------------------------------------------') println('---------------------------------------------------------------------------')
output_lines := os.read_lines(output_file) or { output_lines := os.read_lines(output_file) or {
return return
@ -58,31 +132,51 @@ fn testsuite_end() {
histogram[line] = histogram[line] + 1 histogram[line] = histogram[line] + 1
} }
for k, v in histogram { for k, v in histogram {
println('> found ${k} $v times.') println('> found ${v:5d} times: ${k}')
} }
println('---------------------------------------------------------------------------') println('---------------------------------------------------------------------------')
assert histogram['START'] > 0 assert histogram['START'] > 0
assert histogram['END'] > 0
assert histogram['ORIGINAL'] > 0 assert histogram['ORIGINAL'] > 0
assert histogram['CHANGED'] + histogram['ANOTHER'] > 0 assert histogram['CHANGED'] + histogram['ANOTHER'] > 0
//assert histogram['END'] > 0
} }
fn change_source(new string) { fn change_source(new string) {
time.sleep_ms(250) time.sleep_ms(100)
eprintln('> change ORIGINAL to: $new') eprintln('> change ORIGINAL to: $new')
os.write_file(source_file, live_program_source.replace('ORIGINAL', new)) atomic_write_source( live_program_source.replace('ORIGINAL', new) )
time.sleep_ms(1000) wait_for_file(new)
}
fn wait_for_file(new string){
time.sleep_ms(100)
expected_file := os.join_path(os.temp_dir(), new + '.txt')
for i:=0 ; i <= 400 ; i++ {
if i % 25 == 0 {
eprintln(' checking ${i:-10d} for $expected_file ...')
}
if os.exists( expected_file ) {
assert true
eprintln('> done.') eprintln('> done.')
time.sleep_ms(100)
break
}
time.sleep_ms(5)
}
} }
// //
fn test_live_program_can_be_compiled() { fn test_live_program_can_be_compiled() {
cmd := '$vexe -live run $source_file > $output_file &' eprintln('Compiling...')
eprintln('Compiling and running with: $cmd') os.system('$vexe -live -o $genexe_file $source_file')
//
cmd := '$genexe_file > /dev/null &'
eprintln('Running with: $cmd')
res := os.system(cmd) res := os.system(cmd)
eprintln('... running in the background')
time.sleep_ms(1500)
assert res == 0 assert res == 0
time.sleep_ms(1000)
eprintln('... running in the background')
wait_for_file('ORIGINAL')
} }
fn test_live_program_can_be_changed_1() { fn test_live_program_can_be_changed_1() {
@ -95,7 +189,7 @@ fn test_live_program_can_be_changed_2() {
assert true assert true
} }
fn test_live_program_has_ended() { fn test_live_program_can_be_changed_3() {
time.sleep_ms(3500) change_source('STOP')
assert true assert true
} }