live: make live_test.v more robust
parent
7c4f2b535b
commit
d16485c29e
|
@ -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
|
|
||||||
|
|
||||||
[live]
|
import time
|
||||||
fn pmessage() {
|
import os
|
||||||
println('ORIGINAL')
|
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]
|
||||||
|
fn pmessage() string {
|
||||||
|
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)
|
||||||
eprintln('> done.')
|
}
|
||||||
|
|
||||||
|
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.')
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue