aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/v/tests/valgrind
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/v/tests/valgrind')
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/1.strings_and_arrays.v410
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/array_init_with_string_variable.v11
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/base64.v59
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/buffer_passed_in_fn_that_uses_tos_on_it.v7
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/fn_returning_string_param.v13
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/fn_with_return_should_free_local_vars.v22
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/if_expr.v11
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/if_expr_skip.v13
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/logging.v18
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/option_reassigned.v7
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/option_simple.v6
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/rune_str_method.v4
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/simple_interpolation.v5
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode.v4
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode_more_scopes.v7
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/string_str_method.v4
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/struct_field.v17
-rw-r--r--v_windows/v/vlib/v/tests/valgrind/valgrind_test.v113
18 files changed, 731 insertions, 0 deletions
diff --git a/v_windows/v/vlib/v/tests/valgrind/1.strings_and_arrays.v b/v_windows/v/vlib/v/tests/valgrind/1.strings_and_arrays.v
new file mode 100644
index 0000000..1802eb2
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/1.strings_and_arrays.v
@@ -0,0 +1,410 @@
+import os
+import strings
+
+// This program is built and run via Valgrind to ensure there are no leaks with -autofree
+fn simple() {
+ nums := [1, 2, 3] // local array must be freed
+ println(nums)
+ nums_copy := nums.clone() // array assignments call .clone()
+ println(nums_copy)
+ name := 'Peter' // string literals mustn't be freed
+ str_inter := 'hello, $name' // concatenated strings must be freed
+ // nums.free() // this should result in a double free and a CI error
+ if true {
+ // test the freeing of local vars in a new scope
+ nums2 := [4, 5, 6]
+ str_inter2 := 'hello, $name'
+ println(nums2)
+ }
+ arr := return_array([])
+ println(arr)
+}
+
+fn return_array(array_arg []string) []int { // array argument must not be freed
+ s := [1, 2, 3] // escaping array must not be freed
+ return s
+}
+
+fn handle_strings(s string, p string) int {
+ return 0
+}
+
+fn handle_int(n int) {
+}
+
+fn add_strings(a string, b string) string {
+ return a + b
+}
+
+fn str_tmp_expr() {
+ println('a' + 'b') // tmp expression result must be freed
+ handle_strings('c' + 'd', 'e' + 'f') // multiple tmp expressions must be freed
+ handle_int(handle_strings('x' + 'y', 'f')) // exprs 2 levels deep must bee freed
+ handle_strings('1', add_strings('2', '3')) // test a fn that returns a string
+}
+
+fn str_tmp_expr_advanced() {
+ // t1 = 'c' + 'd'
+ // t2 = 'e + f'
+ // t3 = add_strings(t2, 'g')
+ // handle_strings(t1, t3)
+ // t1.free()
+ // t2.free()
+ // t3.free()
+ //
+ handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed
+}
+
+fn str_tmp_expr_advanced_var_decl() {
+ a := handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed
+ println(a)
+}
+
+struct Foo {
+ a int
+ b string
+}
+
+fn str_inter() {
+ a := 10
+ println('a = $a')
+ // foo := Foo{10, 'x' + 'x'}
+ // println('foo = $foo') // TODO
+}
+
+fn str_replace() {
+ mut s := 'hello world'
+ s = s.replace('hello', 'hi') // s can't be freed as usual before the assignment, since it's used in the right expr
+ println(s)
+ //
+ mut s2 := 'aa' + 'bb'
+ s2 = s2.replace('a', 'c')
+ println(s2)
+ /*
+ r := s.replace('hello', 'hi')
+ cloned := s.replace('hello', 'hi').clone()
+ cloned2 := r.clone()
+ println(s)
+ println(r)
+ */
+}
+
+fn fooo(s string) {
+}
+
+fn str_replace2() {
+ mut s := 'hello world'
+ s = s.replace('hello', 'hi').replace('world', 'planet')
+ println(s)
+}
+
+fn reassign_str() {
+ mut z := '1' + '2'
+ if true {
+ println('KEK')
+ z = 'foo'
+ }
+ mut x := 'a'
+ x = 'b' // nothing has to be freed here
+ //
+ mut s := 'a' + 'b'
+ s = 'x' + 'y' // 'a' + 'b' must be freed before the re-assignment
+ s = s + '!' // old s ref must be copied and freed after the assignment, since s is still used in the right expr
+}
+
+struct Foo2 {
+mut:
+ nums []int
+}
+
+fn reassign_arr() {
+ mut x := [1, 2, 3]
+ // x must be freed before the re-assignment
+ x = [4, 5, 6]
+ mut foo := Foo2{[10, 20, 30]}
+ foo.nums = [40, 50, 60] // same with struct fields
+ foo.nums = [70, 80, 90]
+ // TODO remove this once structs are freed automatically
+ foo.nums.free()
+}
+
+fn match_expr() string {
+ x := 2
+ res := match x {
+ 1 { 'one' }
+ 2 { 'two' }
+ else { 'unknown' }
+ }
+ return res
+}
+
+fn opt(s string) ?int {
+ return 1
+}
+
+fn optional_str() {
+ q := 'select'
+ s := 'query: select'
+ // optional fn args must be freed
+ pos2 := opt('query:$q') or {
+ // pos := s.index('query: $q') or {
+ println('exiting')
+ return
+ }
+ println(pos2 + 1)
+ // optional method args must be freed
+ pos := s.index('query: $q') or {
+ println('exiting')
+ return
+ }
+ println(pos + 1)
+ // test assigning an optional to an existing var
+ mut p := 0
+ for {
+ p = opt('query:$q') or { break }
+ break
+ }
+}
+
+fn return_error_with_freed_expr() ?string {
+ if true {
+ msg := 'oops'
+ return error('hm $msg')
+ }
+ return 'ok'
+}
+
+fn optional_return() {
+ return_error_with_freed_expr() or { return }
+}
+
+fn handle_string(s string) bool {
+ return true
+}
+
+fn if_cond() {
+ // handle_string('1' + '2')
+ if handle_string('1' + '2') {
+ // if '1' + '2' == '12' {
+ println('yes')
+ } else {
+ println('no')
+ }
+}
+
+fn addition_with_tmp_expr() {
+ x := 1 + handle_strings('a' + 'b', 'c')
+ println(x)
+}
+
+fn tt() {
+ // time.parse_rfc2822('1234')
+}
+
+fn get_string(s string) string {
+ return s.clone() // TODO handle returning the argument without clone()
+}
+
+fn if_expr() string {
+ a := if true { get_string('a' + 'b') } else { get_string('c' + 'd') }
+ return a
+}
+
+fn return_if_expr() string {
+ return if true { get_string('a' + 'b') } else { get_string('c' + 'd') }
+}
+
+fn loop_map() {
+ m := {
+ 'UK': 'London'
+ 'France': 'Paris'
+ }
+ // keys must be freed
+ for country, capital in m {
+ println(country + capital)
+ }
+}
+
+fn free_map() {
+ nums := [1, 2, 3]
+ /*
+ nums2 := nums.map(it + handle_strings('a' + 'b', 'c'))
+ println(nums2)
+ */
+}
+
+fn free_inside_opt_block() {
+ x := opt('a' + 'b') or {
+ get_string('c' + 'd') // c+d must be freed before a+b
+ return
+ }
+}
+
+fn free_before_return() {
+ s := 'a' + 'b'
+ println(s)
+ if true {
+ return
+ }
+}
+
+fn free_before_return_bool() bool {
+ s := 'a' + 'b'
+ println(s)
+ return true
+}
+
+fn free_before_break() {
+ s := 'a' + 'b'
+ for {
+ q := [1, 2, 3]
+ break
+ }
+ for {
+ aa := [1, 2, 3]
+ if true {
+ // breaking should free only vars in the closest for loop's scope
+ // `qq`, not `s`
+ break
+ }
+ // nested 1
+ for {
+ bb := [4, 5, 6]
+ if true {
+ break
+ }
+ // nested 2
+ for {
+ cc := [7, 8, 9]
+ if true {
+ if true {
+ break
+ }
+ break
+ }
+ }
+ }
+ }
+ mut i := 0
+ for {
+ i++
+ qq := [1, 2, 3]
+ if i > 10 {
+ break
+ }
+ if true {
+ continue
+ }
+ }
+ x := ['1', '2', '3']
+ for n in x {
+ f := 'f'
+ println('$n => $f')
+ }
+}
+
+struct User {
+ name string
+ age int
+}
+
+fn free_array_except_returned_element() {
+ user := get_user()
+ println(user)
+}
+
+fn get_user() User {
+ users := [User{'Peter', 25}, User{'Alice', 21}]
+ user := users[0] // has to be cloned, since `users` are going to be freed at the end of the function
+ return user
+}
+
+fn get_user2() User {
+ users := [User{'Peter', 25}, User{'Alice', 21}]
+ return users[0] // has to be cloned, since `users` are going to be freed at the end of the function
+}
+
+fn string_array_get() {
+ s := ['a', 'b', 'c']
+ x := s[0]
+ println(s)
+}
+
+fn comp_if() {
+ // compif pos used to be 0, if it was the first statement in a block, vars wouldn't be freed
+ $if macos {
+ println('macos')
+ }
+ s := 'a' + 'b'
+ println(s)
+}
+
+fn anon_fn() {
+}
+
+fn return_sb_str() string {
+ mut sb := strings.new_builder(100)
+ sb.write_string('hello')
+ return sb.str() // sb should be freed, but only after .str() is called
+}
+
+fn parse_header0(s string) ?string {
+ if !s.contains(':') {
+ return error('missing colon in header')
+ }
+ words := s.split_nth(':', 2)
+ x := words[0]
+ return x
+}
+
+fn parse_header1(s string) ?string {
+ if !s.contains(':') {
+ return error('missing colon in header')
+ }
+ words := s.split_nth(':', 2)
+ return words[0]
+}
+
+fn advanced_optionals() {
+ s := parse_header0('foo:bar') or { return }
+ s2 := parse_header1('foo:bar') or { return }
+}
+
+fn main() {
+ println('start')
+ simple()
+ reassign_str()
+ reassign_arr()
+ str_tmp_expr()
+ str_tmp_expr_advanced()
+ str_tmp_expr_advanced_var_decl()
+ str_inter()
+ match_expr()
+ optional_str()
+ // optional_return()
+ str_replace()
+ str_replace2()
+ if_cond()
+ addition_with_tmp_expr()
+ q := if_expr()
+ s := return_if_expr()
+ free_inside_opt_block()
+ comp_if()
+ free_before_return()
+ free_before_return_bool()
+ free_before_break()
+ s2 := return_sb_str()
+ // free_map()
+ // loop_map()
+ advanced_optionals()
+ free_array_except_returned_element()
+ println('end')
+}
+
+/*
+s := s.replace().replace()
+tmp := s.replace()
+s.free()
+s = tmp.replace()
+tmp.free()
+*/
diff --git a/v_windows/v/vlib/v/tests/valgrind/array_init_with_string_variable.v b/v_windows/v/vlib/v/tests/valgrind/array_init_with_string_variable.v
new file mode 100644
index 0000000..8c8f4f3
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/array_init_with_string_variable.v
@@ -0,0 +1,11 @@
+module main
+
+fn main() {
+ a := 'Hello, '
+ b := 'World!'
+ c := a + b
+
+ d := [a, b, c]
+
+ println(d.len)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/base64.v b/v_windows/v/vlib/v/tests/valgrind/base64.v
new file mode 100644
index 0000000..14acce2
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/base64.v
@@ -0,0 +1,59 @@
+import encoding.base64
+
+fn main() {
+ repeats := 1000
+ input_size := 3000
+
+ s_original := []byte{len: input_size, init: `a`}
+ s_encoded := base64.encode(s_original)
+ s_encoded_bytes := s_encoded.bytes()
+ s_decoded := base64.decode(s_encoded)
+
+ assert s_encoded.len > s_original.len
+ assert s_original == s_decoded
+
+ ebuffer := unsafe { malloc(s_encoded.len) }
+ dbuffer := unsafe { malloc(s_decoded.len) }
+ defer {
+ unsafe { free(ebuffer) }
+ unsafe { free(dbuffer) }
+ }
+ //
+ encoded_size := base64.encode_in_buffer(s_original, ebuffer)
+ mut encoded_in_buf := []byte{len: encoded_size}
+ unsafe { C.memcpy(encoded_in_buf.data, ebuffer, encoded_size) }
+ assert input_size * 4 / 3 == encoded_size
+ assert encoded_in_buf[0] == `Y`
+ assert encoded_in_buf[1] == `W`
+ assert encoded_in_buf[2] == `F`
+ assert encoded_in_buf[3] == `h`
+
+ assert encoded_in_buf[encoded_size - 4] == `Y`
+ assert encoded_in_buf[encoded_size - 3] == `W`
+ assert encoded_in_buf[encoded_size - 2] == `F`
+ assert encoded_in_buf[encoded_size - 1] == `h`
+
+ assert encoded_in_buf == s_encoded_bytes
+
+ decoded_size := base64.decode_in_buffer(s_encoded, dbuffer)
+ assert decoded_size == input_size
+ mut decoded_in_buf := []byte{len: decoded_size}
+ unsafe { C.memcpy(decoded_in_buf.data, dbuffer, decoded_size) }
+ assert decoded_in_buf == s_original
+
+ mut s := 0
+ for _ in 0 .. repeats {
+ resultsize := base64.encode_in_buffer(s_original, ebuffer)
+ s += resultsize
+ assert resultsize == s_encoded.len
+ }
+
+ for _ in 0 .. repeats {
+ resultsize := base64.decode_in_buffer(s_encoded, dbuffer)
+ s += resultsize
+ assert resultsize == s_decoded.len
+ }
+
+ println('Final s: $s')
+ // assert s == 39147008
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/buffer_passed_in_fn_that_uses_tos_on_it.v b/v_windows/v/vlib/v/tests/valgrind/buffer_passed_in_fn_that_uses_tos_on_it.v
new file mode 100644
index 0000000..24b5344
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/buffer_passed_in_fn_that_uses_tos_on_it.v
@@ -0,0 +1,7 @@
+fn main() {
+ unsafe {
+ mut buffer := malloc_noscan(5)
+ s := utf32_to_str_no_malloc(77, buffer)
+ println(s)
+ }
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/fn_returning_string_param.v b/v_windows/v/vlib/v/tests/valgrind/fn_returning_string_param.v
new file mode 100644
index 0000000..d040c9d
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/fn_returning_string_param.v
@@ -0,0 +1,13 @@
+fn identity(s string) string {
+ return s
+}
+
+fn main() {
+ sa := 'abc'
+ sb := 'def'
+ sc := sa + sb
+ sd := identity(sc)
+ if sc != sd {
+ exit(1)
+ }
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/fn_with_return_should_free_local_vars.v b/v_windows/v/vlib/v/tests/valgrind/fn_with_return_should_free_local_vars.v
new file mode 100644
index 0000000..35ca7a9
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/fn_with_return_should_free_local_vars.v
@@ -0,0 +1,22 @@
+fn many_strings() []string {
+ mut res := []string{}
+ for i in 0 .. 10000 {
+ res << '${i:05}'
+ }
+ return res
+}
+
+fn abc() int {
+ x := many_strings()
+ n := x.len
+ // NB: the local `x` is no longer used
+ // it should be freed *before* the return
+ return n
+}
+
+fn main() {
+ for i in 0 .. 5 {
+ nstrings := abc()
+ eprintln('nstrings ${i:10}: $nstrings')
+ }
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/if_expr.v b/v_windows/v/vlib/v/tests/valgrind/if_expr.v
new file mode 100644
index 0000000..e741f86
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/if_expr.v
@@ -0,0 +1,11 @@
+fn main() {
+ mut a := true
+ b := a && if true {
+ a = false
+ true
+ } else {
+ false
+ }
+ println(b)
+ assert b == true
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/if_expr_skip.v b/v_windows/v/vlib/v/tests/valgrind/if_expr_skip.v
new file mode 100644
index 0000000..be1d65d
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/if_expr_skip.v
@@ -0,0 +1,13 @@
+fn main() {
+ mut a := false
+ b := a && if true {
+ a = true
+ true
+ } else {
+ false
+ }
+ println(b)
+ assert b == false
+ println(a)
+ assert a == false
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/logging.v b/v_windows/v/vlib/v/tests/valgrind/logging.v
new file mode 100644
index 0000000..ec2f016
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/logging.v
@@ -0,0 +1,18 @@
+import log
+import time
+
+fn main() {
+ mut l := log.Log{}
+ defer {
+ l.close()
+ }
+ l.set_level(.info)
+ l.info('info')
+ l.warn('warn')
+ l.error('an error')
+ l.debug('some debug info')
+ for i := 0; i < 100; i++ {
+ l.info('123456')
+ time.sleep(1 * time.millisecond)
+ }
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/option_reassigned.v b/v_windows/v/vlib/v/tests/valgrind/option_reassigned.v
new file mode 100644
index 0000000..50812e2
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/option_reassigned.v
@@ -0,0 +1,7 @@
+import os
+
+fn main() {
+ mut a := 'abc'
+ a = os.find_abs_path_of_executable('ls') or { '' }
+ eprintln(a)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/option_simple.v b/v_windows/v/vlib/v/tests/valgrind/option_simple.v
new file mode 100644
index 0000000..5b9d8fc
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/option_simple.v
@@ -0,0 +1,6 @@
+import os
+
+fn main() {
+ a := os.find_abs_path_of_executable('ls') or { '' }
+ eprintln(a)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/rune_str_method.v b/v_windows/v/vlib/v/tests/valgrind/rune_str_method.v
new file mode 100644
index 0000000..e04c78b
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/rune_str_method.v
@@ -0,0 +1,4 @@
+fn main() {
+ a := `Y`.str()
+ println(a)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/simple_interpolation.v b/v_windows/v/vlib/v/tests/valgrind/simple_interpolation.v
new file mode 100644
index 0000000..16acab5
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/simple_interpolation.v
@@ -0,0 +1,5 @@
+fn main() {
+ v := 't'
+ s := '${v}.tmp'
+ println(s)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode.v b/v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode.v
new file mode 100644
index 0000000..54ea943
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode.v
@@ -0,0 +1,4 @@
+// TODO: @medvednikov: autofree on script mode does not work when first variable is on position 0 due to the code in `cgen.v:1115`
+v := 't'
+s := '${v}.tmp'
+println(s)
diff --git a/v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode_more_scopes.v b/v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode_more_scopes.v
new file mode 100644
index 0000000..0856646
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/simple_interpolation_script_mode_more_scopes.v
@@ -0,0 +1,7 @@
+{
+ {
+ v := 't'
+ s := '${v}.tmp'
+ println(s)
+ }
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/string_str_method.v b/v_windows/v/vlib/v/tests/valgrind/string_str_method.v
new file mode 100644
index 0000000..111e6b1
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/string_str_method.v
@@ -0,0 +1,4 @@
+fn main() {
+ a := 'Y'.str()
+ println(a)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/struct_field.v b/v_windows/v/vlib/v/tests/valgrind/struct_field.v
new file mode 100644
index 0000000..6972818
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/struct_field.v
@@ -0,0 +1,17 @@
+struct LeakStruct {
+mut:
+ some_bytes []byte
+}
+
+fn (mut l LeakStruct) free() {
+ unsafe {
+ l.some_bytes.free()
+ }
+}
+
+fn main() {
+ z := &LeakStruct{
+ some_bytes: []byte{len: 1000}
+ }
+ println(z.some_bytes.len)
+}
diff --git a/v_windows/v/vlib/v/tests/valgrind/valgrind_test.v b/v_windows/v/vlib/v/tests/valgrind/valgrind_test.v
new file mode 100644
index 0000000..fc18276
--- /dev/null
+++ b/v_windows/v/vlib/v/tests/valgrind/valgrind_test.v
@@ -0,0 +1,113 @@
+import os
+import term
+import benchmark
+import v.util.vtest
+
+const turn_off_vcolors = os.setenv('VCOLORS', 'never', true)
+
+fn bold(s string) string {
+ return term.colorize(term.bold, s)
+}
+
+//
+// NB: skip_compile_files can be used for totally skipping .v files temporarily.
+// .v files in skip_valgrind_files will be compiled, but will not be run under
+// valgrind. This ensures that at least the generated code does not have C syntax
+// errors.
+// Use: `./v -d noskipcompile vlib/v/tests/valgrind/valgrind_test.v` to ignore the
+// skip_compile_files list.
+// Use: `./v -d noskip vlib/v/tests/valgrind/valgrind_test.v` to ignore skip_valgrind_files
+// Use: `./v -d noskipcompile -d noskip vlib/v/tests/valgrind/valgrind_test.v` to ignore both
+//
+const skip_compile_files = [
+ 'vlib/v/tests/valgrind/option_reassigned.v',
+]
+
+const skip_valgrind_files = [
+ 'vlib/v/tests/valgrind/struct_field.v',
+ 'vlib/v/tests/valgrind/fn_returning_string_param.v',
+ 'vlib/v/tests/valgrind/fn_with_return_should_free_local_vars.v',
+ 'vlib/v/tests/valgrind/option_simple.v',
+]
+
+fn vprintln(s string) {
+ $if verbose ? {
+ eprintln(s)
+ }
+}
+
+fn test_all() {
+ if os.user_os() != 'linux' && os.getenv('FORCE_VALGRIND_TEST').len == 0 {
+ eprintln('Valgrind tests can only be run reliably on Linux for now.')
+ eprintln('You can still do it by setting FORCE_VALGRIND_TEST=1 .')
+ exit(0)
+ }
+ if os.getenv('V_CI_UBUNTU_MUSL').len > 0 {
+ eprintln('This test is disabled for musl.')
+ exit(0)
+ }
+ bench_message := 'memory leak checking with valgrind'
+ mut bench := benchmark.new_benchmark()
+ eprintln(term.header(bench_message, '-'))
+ vexe := os.getenv('VEXE')
+ vroot := os.dir(vexe)
+ valgrind_test_path := 'vlib/v/tests/valgrind'
+ dir := os.join_path(vroot, valgrind_test_path)
+ files := os.ls(dir) or { panic(err) }
+ //
+ wrkdir := os.join_path(os.temp_dir(), 'vtests', 'valgrind')
+ os.mkdir_all(wrkdir) or { panic(err) }
+ os.chdir(wrkdir) or {}
+ //
+ only_ordinary_v_files := files.filter(it.ends_with('.v') && !it.ends_with('_test.v'))
+ tests := vtest.filter_vtest_only(only_ordinary_v_files, basepath: valgrind_test_path)
+ bench.set_total_expected_steps(tests.len)
+ for test in tests {
+ bench.step()
+ if test in skip_compile_files {
+ $if !noskipcompile ? {
+ bench.skip()
+ eprintln(bench.step_message_skip(test))
+ continue
+ }
+ }
+ //
+ exe_filename := '$wrkdir/x'
+ full_path_to_source_file := os.join_path(vroot, test)
+ compile_cmd := '$vexe -o $exe_filename -cg -cflags "-w" -autofree "$full_path_to_source_file"'
+ vprintln('compile cmd: ${bold(compile_cmd)}')
+ res := os.execute(compile_cmd)
+ if res.exit_code != 0 {
+ bench.fail()
+ eprintln(bench.step_message_fail('file: $test could not be compiled.'))
+ eprintln(res.output)
+ eprintln('You can reproduce the failure with:\n$compile_cmd')
+ continue
+ }
+ if test in skip_valgrind_files {
+ $if !noskip ? {
+ bench.skip()
+ eprintln(bench.step_message_skip(test))
+ continue
+ }
+ }
+ valgrind_cmd := 'valgrind --error-exitcode=1 --leak-check=full $exe_filename'
+ vprintln('valgrind cmd: ${bold(valgrind_cmd)}')
+ valgrind_res := os.execute(valgrind_cmd)
+ if valgrind_res.exit_code != 0 {
+ bench.fail()
+ eprintln(bench.step_message_fail('failed valgrind check for ${bold(test)}'))
+ eprintln(valgrind_res.output)
+ eprintln('You can reproduce the failure with:\n$compile_cmd && $valgrind_cmd')
+ continue
+ }
+ bench.ok()
+ eprintln(bench.step_message_ok(test))
+ }
+ bench.stop()
+ eprintln(term.h_divider('-'))
+ eprintln(bench.total_message(bench_message))
+ if bench.nfail > 0 {
+ exit(1)
+ }
+}