2020-04-26 13:49:31 +02:00
|
|
|
import os
|
|
|
|
import time
|
2020-03-21 06:37:58 +01:00
|
|
|
|
2020-05-24 16:45:53 +02:00
|
|
|
/*
|
|
|
|
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,
|
2021-01-16 14:05:01 +01:00
|
|
|
(that program is in `vlib/v/live/live_test_template.vv`)
|
2020-07-27 12:15:29 +02:00
|
|
|
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),
|
2020-05-24 16:45:53 +02:00
|
|
|
then it waits some more, modifies it again and saves it once more.
|
|
|
|
|
2020-07-27 12:15:29 +02:00
|
|
|
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
|
2020-05-24 16:45:53 +02:00
|
|
|
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).
|
|
|
|
|
2020-07-27 12:15:29 +02:00
|
|
|
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
|
2020-05-24 16:45:53 +02:00
|
|
|
of files, ORIGINAL.txt ... STOP.txt , which are appended to by the generated program.
|
|
|
|
|
2020-07-27 12:15:29 +02:00
|
|
|
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
|
2020-05-24 16:45:53 +02:00
|
|
|
not very flaky way.
|
|
|
|
|
|
|
|
TODO: Cleanup this when/if v has better process control/communication primitives.
|
|
|
|
*/
|
2020-03-21 06:37:58 +01:00
|
|
|
const (
|
2020-04-12 01:41:26 +02:00
|
|
|
vexe = os.getenv('VEXE')
|
2020-05-24 16:45:53 +02:00
|
|
|
tmp_file = os.join_path(os.temp_dir(), 'generated_live_program.tmp.v')
|
2020-04-12 01:41:26 +02:00
|
|
|
source_file = os.join_path(os.temp_dir(), 'generated_live_program.v')
|
2020-05-24 16:45:53 +02:00
|
|
|
genexe_file = os.join_path(os.temp_dir(), 'generated_live_program')
|
2020-04-12 01:41:26 +02:00
|
|
|
output_file = os.join_path(os.temp_dir(), 'generated_live_program.output.txt')
|
2020-05-24 16:45:53 +02:00
|
|
|
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')
|
2021-01-09 19:55:56 +01:00
|
|
|
cleanup_files = [tmp_file, source_file, genexe_file, output_file, res_original_file,
|
|
|
|
res_changed_file, res_another_file, res_stop_file]
|
2021-01-16 14:05:01 +01:00
|
|
|
live_program_source = get_source_template()
|
2020-05-24 16:45:53 +02:00
|
|
|
)
|
2021-01-16 14:05:01 +01:00
|
|
|
|
|
|
|
fn get_source_template() string {
|
2021-03-01 00:18:14 +01:00
|
|
|
src := os.read_file(os.join_path(os.dir(@FILE), 'live_test_template.vv')) or { panic(err) }
|
2021-01-16 14:05:01 +01:00
|
|
|
return src.replace('#OUTPUT_FILE#', output_file)
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
|
|
|
|
2020-10-02 10:38:49 +02:00
|
|
|
fn edefault(name string, default string) string {
|
|
|
|
res := os.getenv(name)
|
|
|
|
if res == '' {
|
|
|
|
return default
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2020-07-27 12:15:29 +02:00
|
|
|
fn atomic_write_source(source string) {
|
2020-05-24 16:45:53 +02:00
|
|
|
// 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.
|
2021-03-01 00:18:14 +01:00
|
|
|
os.write_file(tmp_file, source) or { panic(err) }
|
|
|
|
os.mv(tmp_file, source_file) or { panic(err) }
|
2020-05-24 16:45:53 +02:00
|
|
|
}
|
|
|
|
|
2020-03-21 06:37:58 +01:00
|
|
|
//
|
2020-04-12 01:41:26 +02:00
|
|
|
fn testsuite_begin() {
|
2020-10-18 18:45:49 +02:00
|
|
|
if os.user_os() !in ['linux', 'solaris'] && os.getenv('FORCE_LIVE_TEST').len == 0 {
|
2020-03-21 06:37:58 +01:00
|
|
|
eprintln('Testing the runtime behaviour of -live mode,')
|
2020-08-26 06:50:32 +02:00
|
|
|
eprintln('is reliable only on Linux/macOS for now.')
|
2020-03-21 06:37:58 +01:00
|
|
|
eprintln('You can still do it by setting FORCE_LIVE_TEST=1 .')
|
|
|
|
exit(0)
|
|
|
|
}
|
2021-03-22 23:06:12 +01:00
|
|
|
for f in [tmp_file, source_file, output_file, res_original_file, res_changed_file,
|
|
|
|
res_another_file, res_stop_file] {
|
2021-03-06 20:04:51 +01:00
|
|
|
os.rm(f) or {}
|
2020-05-24 16:45:53 +02:00
|
|
|
}
|
2020-07-27 12:15:29 +02:00
|
|
|
atomic_write_source(live_program_source)
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
|
|
|
|
2020-07-27 12:15:29 +02:00
|
|
|
[debuglivetest]
|
2020-05-24 17:38:43 +02:00
|
|
|
fn vprintln(s string) {
|
|
|
|
eprintln(s)
|
|
|
|
}
|
|
|
|
|
2020-04-12 01:41:26 +02:00
|
|
|
fn testsuite_end() {
|
2020-05-24 17:38:43 +02:00
|
|
|
vprintln('source: $source_file')
|
|
|
|
vprintln('output: $output_file')
|
|
|
|
vprintln('---------------------------------------------------------------------------')
|
2020-04-12 01:41:26 +02:00
|
|
|
output_lines := os.read_lines(output_file) or {
|
2020-05-28 02:20:55 +02:00
|
|
|
panic('could not read $output_file, error: $err')
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
2020-07-27 12:15:29 +02:00
|
|
|
mut histogram := map[string]int{}
|
2020-03-21 06:37:58 +01:00
|
|
|
for line in output_lines {
|
|
|
|
histogram[line] = histogram[line] + 1
|
|
|
|
}
|
2020-04-12 01:41:26 +02:00
|
|
|
for k, v in histogram {
|
2020-07-27 12:15:29 +02:00
|
|
|
eprintln('> found ${v:5d} times: $k')
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
2020-05-24 17:38:43 +02:00
|
|
|
vprintln('---------------------------------------------------------------------------')
|
2020-03-21 06:37:58 +01:00
|
|
|
assert histogram['START'] > 0
|
|
|
|
assert histogram['ORIGINAL'] > 0
|
2020-05-01 19:34:27 +02:00
|
|
|
assert histogram['CHANGED'] + histogram['ANOTHER'] > 0
|
2020-07-27 12:15:29 +02:00
|
|
|
// assert histogram['END'] > 0
|
2021-01-09 19:55:56 +01:00
|
|
|
for tfile in cleanup_files {
|
2021-03-06 20:04:51 +01:00
|
|
|
os.rm(tfile) or {}
|
2021-01-09 19:55:56 +01:00
|
|
|
}
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
|
|
|
|
2020-04-12 01:41:26 +02:00
|
|
|
fn change_source(new string) {
|
2021-02-27 18:41:06 +01:00
|
|
|
time.sleep(100 * time.millisecond)
|
2020-05-24 17:38:43 +02:00
|
|
|
vprintln('> change ORIGINAL to: $new')
|
2020-07-27 12:15:29 +02:00
|
|
|
atomic_write_source(live_program_source.replace('ORIGINAL', new))
|
2020-05-24 16:45:53 +02:00
|
|
|
wait_for_file(new)
|
2020-07-27 12:15:29 +02:00
|
|
|
}
|
2020-05-24 16:45:53 +02:00
|
|
|
|
2020-07-27 12:15:29 +02:00
|
|
|
fn wait_for_file(new string) {
|
2021-02-27 18:41:06 +01:00
|
|
|
time.sleep(100 * time.millisecond)
|
2020-05-24 16:45:53 +02:00
|
|
|
expected_file := os.join_path(os.temp_dir(), new + '.txt')
|
2020-05-24 17:38:43 +02:00
|
|
|
eprintln('waiting for $expected_file ...')
|
2020-10-02 10:38:49 +02:00
|
|
|
max_wait_cycles := edefault('WAIT_CYCLES', '1').int()
|
|
|
|
for i := 0; i <= max_wait_cycles; i++ {
|
2020-05-24 16:45:53 +02:00
|
|
|
if i % 25 == 0 {
|
2020-05-24 17:38:43 +02:00
|
|
|
vprintln(' checking ${i:-10d} for $expected_file ...')
|
2020-05-24 16:45:53 +02:00
|
|
|
}
|
2020-07-27 12:15:29 +02:00
|
|
|
if os.exists(expected_file) {
|
2020-05-24 16:45:53 +02:00
|
|
|
assert true
|
2020-05-24 17:38:43 +02:00
|
|
|
vprintln('> done.')
|
2021-02-27 18:41:06 +01:00
|
|
|
time.sleep(100 * time.millisecond)
|
2020-05-24 16:45:53 +02:00
|
|
|
break
|
|
|
|
}
|
2021-02-27 18:41:06 +01:00
|
|
|
time.sleep(5 * time.millisecond)
|
2020-05-24 16:45:53 +02:00
|
|
|
}
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
|
|
|
|
2020-10-02 10:38:49 +02:00
|
|
|
fn setup_cycles_environment() {
|
|
|
|
mut max_live_cycles := 1000
|
|
|
|
mut max_wait_cycles := 400
|
|
|
|
if os.user_os() == 'macos' {
|
2021-01-09 19:55:56 +01:00
|
|
|
// max_live_cycles *= 5
|
|
|
|
// max_wait_cycles *= 5
|
2020-10-02 10:38:49 +02:00
|
|
|
}
|
|
|
|
os.setenv('LIVE_CYCLES', '$max_live_cycles', true)
|
|
|
|
os.setenv('WAIT_CYCLES', '$max_wait_cycles', true)
|
|
|
|
}
|
|
|
|
|
2020-03-21 06:37:58 +01:00
|
|
|
//
|
2020-04-12 01:41:26 +02:00
|
|
|
fn test_live_program_can_be_compiled() {
|
2020-10-02 10:38:49 +02:00
|
|
|
setup_cycles_environment()
|
2020-05-24 16:45:53 +02:00
|
|
|
eprintln('Compiling...')
|
2021-01-16 14:05:01 +01:00
|
|
|
os.system('$vexe -nocolor -live -o $genexe_file $source_file')
|
2020-05-24 16:45:53 +02:00
|
|
|
//
|
|
|
|
cmd := '$genexe_file > /dev/null &'
|
|
|
|
eprintln('Running with: $cmd')
|
2020-03-21 06:37:58 +01:00
|
|
|
res := os.system(cmd)
|
|
|
|
assert res == 0
|
2020-05-24 16:45:53 +02:00
|
|
|
eprintln('... running in the background')
|
|
|
|
wait_for_file('ORIGINAL')
|
2020-03-21 06:37:58 +01:00
|
|
|
}
|
|
|
|
|
2020-04-12 01:41:26 +02:00
|
|
|
fn test_live_program_can_be_changed_1() {
|
2020-03-21 06:37:58 +01:00
|
|
|
change_source('CHANGED')
|
|
|
|
assert true
|
|
|
|
}
|
|
|
|
|
2020-04-12 01:41:26 +02:00
|
|
|
fn test_live_program_can_be_changed_2() {
|
2020-03-21 06:37:58 +01:00
|
|
|
change_source('ANOTHER')
|
|
|
|
assert true
|
|
|
|
}
|
|
|
|
|
2020-05-24 16:45:53 +02:00
|
|
|
fn test_live_program_can_be_changed_3() {
|
2021-01-16 14:05:01 +01:00
|
|
|
change_source('STOP')
|
|
|
|
change_source('STOP')
|
2020-05-24 16:45:53 +02:00
|
|
|
change_source('STOP')
|
2020-03-21 06:37:58 +01:00
|
|
|
assert true
|
|
|
|
}
|