add runtime/test.el — El test framework with assertions and runner

Provides assert_true/false, assert_eq, assert_int_eq, assert_neq,
assert_contains, assert_starts_with, assert_ends_with, and fail.
Test cases are registered by name+fn_name, executed sequentially via
the thread/dlsym dispatch mechanism, with results tracked in state_.
Includes tests/runtime/string_test.el covering all 23 string.el exports.
This commit is contained in:
Will Anderson
2026-05-03 15:49:01 -05:00
parent 3cc9b1cc3d
commit 282df712a8
2 changed files with 661 additions and 0 deletions
+320
View File
@@ -0,0 +1,320 @@
// runtime/test.el El test framework: assertions, registration, and runner.
//
// Provides a minimal but complete test harness for El programs. No external
// dependencies. Written entirely in El using existing runtime primitives.
//
// Quick-start
//
// 1. Write a test function with the standard test signature:
//
// fn test_str_eq_works(_: String) -> String {
// assert_true(str_eq("a", "a"), "same strings are equal")
// assert_false(str_eq("a", "b"), "different strings are not equal")
// return ""
// }
//
// 2. Register it and run:
//
// fn main() -> Void {
// test_case("str_eq works", "test_str_eq_works")
// test_run_all()
// }
//
// Test functions must have the signature (String) -> String. The argument is
// a dummy passed by the threading mechanism (see runtime/thread.el) and should
// be ignored. The return value is likewise ignored results flow through the
// state-based assertion primitives.
//
// State keys
//
// _test_cases JSON array of {"name":"...","fn":"..."}
// _test_pass_count Int as string total passing assertions
// _test_fail_count Int as string total failing assertions
// _test_failures JSON array of failure message strings
// _test_current Name of the test case currently executing
//
// Dependencies
//
// runtime/string.el str_eq, str_concat, str_contains, int_to_str, ...
// runtime/state.el state_set, state_get
// runtime/json.el json_array_len, json_array_get_string, json_get,
// json_escape_string
// runtime/thread.el spawn, join (for dynamic dispatch via dlsym)
// Internal: JSON array helpers
//
// _test_json_append append a quoted, escaped string element to a JSON array.
//
// Given an existing JSON array string (e.g. '["a","b"]') and a plain string
// value, returns a new array with the value appended (e.g. '["a","b","c"]').
//
// The array must be non-empty always init with "[]" before calling.
fn _test_json_append(arr: String, val: String) -> String {
let escaped: String = json_escape_string(val)
let inner: String = str_slice(arr, 1, str_len(arr) - 1)
if str_eq(inner, "") {
return "[\"" + escaped + "\"]"
}
return "[" + inner + ",\"" + escaped + "\"]"
}
// _test_json_obj_append append a raw JSON object string to a JSON array.
//
// Used to build the _test_cases list where each element is already a
// JSON object (not a plain string).
fn _test_json_obj_append(arr: String, obj: String) -> String {
let inner: String = str_slice(arr, 1, str_len(arr) - 1)
if str_eq(inner, "") {
return "[" + obj + "]"
}
return "[" + inner + "," + obj + "]"
}
// Test registration
// test_case register a named test case.
//
// name: Human-readable test case name (shown in output).
// fn_name: Name of a top-level El function with signature
// (String) -> String. The function should call assertion
// primitives from this module. The String arg it receives is ""
// and its return value is ignored.
//
// Test cases are stored in the _test_cases state key and executed in
// registration order by test_run_all().
fn test_case(name: String, fn_name: String) {
let arr: String = state_get("_test_cases")
if str_eq(arr, "") { arr = "[]" }
let escaped_name: String = json_escape_string(name)
let escaped_fn: String = json_escape_string(fn_name)
let obj: String = "{\"name\":\"" + escaped_name + "\",\"fn\":\"" + escaped_fn + "\"}"
let arr = _test_json_obj_append(arr, obj)
state_set("_test_cases", arr)
}
// Test state helpers
// _test_init reset all test counters and failure lists.
//
// Called at the start of test_run_all(). Safe to call multiple times.
fn _test_init() {
state_set("_test_pass_count", "0")
state_set("_test_fail_count", "0")
state_set("_test_failures", "[]")
state_set("_test_current", "")
}
// _test_inc_pass increment the global pass counter by 1.
fn _test_inc_pass() {
let n: Int = str_to_int(state_get("_test_pass_count"))
state_set("_test_pass_count", int_to_str(n + 1))
}
// _test_inc_fail increment the global fail counter by 1.
fn _test_inc_fail() {
let n: Int = str_to_int(state_get("_test_fail_count"))
state_set("_test_fail_count", int_to_str(n + 1))
}
// test_pass record a passing assertion for the current test case.
//
// Increments the pass counter. Called internally by assertions.
fn test_pass(name: String) {
_test_inc_pass()
}
// test_fail record a failing assertion for the current test case.
//
// name: assertion label or description (usually the msg parameter)
// msg: detailed failure message including expected/got values
//
// Increments the fail counter and appends the message to _test_failures.
// Also prints the failure immediately for visibility.
fn test_fail(name: String, msg: String) {
_test_inc_fail()
let failures: String = state_get("_test_failures")
if str_eq(failures, "") { failures = "[]" }
let entry: String = " " + msg
let failures = _test_json_append(failures, entry)
state_set("_test_failures", failures)
println(" FAIL: " + msg)
}
// Assertions
// assert_true assert that condition is true.
//
// condition: the boolean value to test
// msg: description shown on failure
fn assert_true(condition: Bool, msg: String) {
if condition {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": expected true, got false")
}
// assert_false assert that condition is false.
//
// condition: the boolean value to test
// msg: description shown on failure
fn assert_false(condition: Bool, msg: String) {
if !condition {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": expected false, got true")
}
// assert_eq assert that two strings are equal.
//
// a, b: strings to compare
// msg: description shown on failure (quoted values appended automatically)
fn assert_eq(a: String, b: String, msg: String) {
if str_eq(a, b) {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": expected \"" + b + "\", got \"" + a + "\"")
}
// assert_int_eq assert that two integers are equal.
//
// a, b: integers to compare
// msg: description shown on failure
fn assert_int_eq(a: Int, b: Int, msg: String) {
if a == b {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": expected " + int_to_str(b) + ", got " + int_to_str(a))
}
// assert_neq assert that two strings are NOT equal.
//
// a, b: strings to compare
// msg: description shown on failure
fn assert_neq(a: String, b: String, msg: String) {
if !str_eq(a, b) {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": expected values to differ, but both are \"" + a + "\"")
}
// assert_contains assert that string s contains substring sub.
//
// s: haystack string
// sub: needle substring
// msg: description shown on failure
fn assert_contains(s: String, sub: String, msg: String) {
if str_contains(s, sub) {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": \"" + s + "\" does not contain \"" + sub + "\"")
}
// assert_starts_with assert that string s starts with prefix.
//
// s: string to inspect
// prefix: expected prefix
// msg: description shown on failure
fn assert_starts_with(s: String, prefix: String, msg: String) {
if str_starts_with(s, prefix) {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": \"" + s + "\" does not start with \"" + prefix + "\"")
}
// assert_ends_with assert that string s ends with suffix.
//
// s: string to inspect
// suffix: expected suffix
// msg: description shown on failure
fn assert_ends_with(s: String, suffix: String, msg: String) {
if str_ends_with(s, suffix) {
test_pass(msg)
return
}
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg + ": \"" + s + "\" does not end with \"" + suffix + "\"")
}
// fail unconditional test failure.
//
// msg: failure message shown in output
//
// Use when a code path that must not be reached is reached, or when an
// expected exception did not occur.
fn fail(msg: String) {
let test_name: String = state_get("_test_current")
test_fail(msg, "[" + test_name + "] " + msg)
}
// Runner
// _test_run_one execute a single registered test case by name.
//
// name: the human-readable test case name (set as _test_current)
// fn_name: the El function to invoke via the thread mechanism
//
// Sets _test_current so that assertions inside the test function know which
// test they belong to. Spawns and immediately joins the test function in a
// child thread (same dlsym mechanism as parallel_map) so dynamic dispatch
// works without needing closures.
fn _test_run_one(name: String, fn_name: String) {
state_set("_test_current", name)
let before_fail: Int = str_to_int(state_get("_test_fail_count"))
let tid: Int = __thread_create(fn_name, "")
__thread_join(tid)
let after_fail: Int = str_to_int(state_get("_test_fail_count"))
if after_fail == before_fail {
println("[test] " + name + " ... PASS")
} else {
println("[test] " + name + " ... FAIL")
}
}
// test_run_all execute all registered test cases and print a summary.
//
// Iterates through every test case registered via test_case(), runs each one,
// prints per-test PASS/FAIL status, then prints a summary line.
//
// Returns the total number of failing assertions. Exit with this value to
// signal CI failure:
//
// fn main() -> Int {
// test_case("str_eq", "test_str_eq")
// return test_run_all()
// }
fn test_run_all() -> Int {
_test_init()
let cases: String = state_get("_test_cases")
if str_eq(cases, "") { cases = "[]" }
let n: Int = json_array_len(cases)
let i: Int = 0
while i < n {
let entry: String = json_array_get(cases, i)
let name: String = json_get(entry, "name")
let fn_name: String = json_get(entry, "fn")
_test_run_one(name, fn_name)
i = i + 1
}
let pass_count: Int = str_to_int(state_get("_test_pass_count"))
let fail_count: Int = str_to_int(state_get("_test_fail_count"))
println("[test] Summary: " + int_to_str(pass_count) + " passed, " + int_to_str(fail_count) + " failed")
return fail_count
}
+341
View File
@@ -0,0 +1,341 @@
// tests/runtime/string_test.el Test suite for runtime/string.el
//
// Exercises every public function exported by runtime/string.el using the
// runtime/test.el framework. Each test function covers one string primitive
// or a tight family of related functions.
//
// Build and run
//
// # Combine runtime modules + test framework + this file, then compile:
// cat runtime/string.el runtime/math.el runtime/state.el runtime/env.el \
// runtime/fs.el runtime/exec.el runtime/time.el runtime/json.el \
// runtime/http.el runtime/engram.el runtime/thread.el \
// runtime/test.el \
// tests/runtime/string_test.el > /tmp/string_test_combined.el
//
// ./dist/platform/elc /tmp/string_test_combined.el > /tmp/string_test.c
// cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
// -o /tmp/string_test /tmp/string_test.c el-compiler/runtime/el_seed.c
// /tmp/string_test; echo "exit: $?"
//
// Exit code equals the number of failing assertions (0 = all pass).
//
// Coverage
//
// str_eq str_neq (via assert_neq)
// str_len str_concat
// str_starts_with str_ends_with
// str_contains str_index_of
// str_slice str_replace
// str_to_upper str_to_lower
// str_trim str_lstrip / str_rstrip
// str_split str_join
// int_to_str str_to_int
// str_repeat str_reverse
// str_count
// str_eq
fn test_str_eq(_: String) -> String {
assert_true(str_eq("hello", "hello"), "identical strings are equal")
assert_false(str_eq("hello", "world"), "different strings are not equal")
assert_true(str_eq("", ""), "empty strings are equal")
assert_false(str_eq("a", ""), "non-empty vs empty is not equal")
assert_false(str_eq("", "a"), "empty vs non-empty is not equal")
assert_false(str_eq("Hello", "hello"), "case-sensitive comparison")
return ""
}
// str_len
fn test_str_len(_: String) -> String {
assert_int_eq(str_len(""), 0, "empty string has length 0")
assert_int_eq(str_len("a"), 1, "single char has length 1")
assert_int_eq(str_len("hello"), 5, "hello has length 5")
assert_int_eq(str_len("hello world"), 11, "space included in length")
return ""
}
// str_concat
fn test_str_concat(_: String) -> String {
assert_eq(str_concat("hello", " world"), "hello world", "basic concat")
assert_eq(str_concat("", "world"), "world", "empty prefix")
assert_eq(str_concat("hello", ""), "hello", "empty suffix")
assert_eq(str_concat("", ""), "", "both empty")
return ""
}
// str_starts_with
fn test_str_starts_with(_: String) -> String {
assert_true(str_starts_with("hello world", "hello"), "prefix present")
assert_false(str_starts_with("hello world", "world"), "not a prefix")
assert_true(str_starts_with("hello", "hello"), "string is its own prefix")
assert_true(str_starts_with("hello", ""), "empty prefix always true")
assert_false(str_starts_with("", "a"), "empty string has no prefix")
assert_false(str_starts_with("hi", "hello"), "prefix longer than string")
return ""
}
// str_ends_with
fn test_str_ends_with(_: String) -> String {
assert_true(str_ends_with("hello world", "world"), "suffix present")
assert_false(str_ends_with("hello world", "hello"), "not a suffix")
assert_true(str_ends_with("hello", "hello"), "string is its own suffix")
assert_true(str_ends_with("hello", ""), "empty suffix always true")
assert_false(str_ends_with("", "a"), "empty string has no suffix")
assert_false(str_ends_with("hi", "world"), "suffix longer than string")
return ""
}
// str_contains
fn test_str_contains(_: String) -> String {
assert_true(str_contains("hello world", "world"), "contains at end")
assert_true(str_contains("hello world", "hello"), "contains at start")
assert_true(str_contains("hello world", "lo wo"), "contains in middle")
assert_false(str_contains("hello world", "xyz"), "not contained")
assert_true(str_contains("hello", ""), "empty sub always contained")
assert_false(str_contains("", "a"), "empty string contains nothing")
assert_true(str_contains("hello", "hello"), "string contains itself")
return ""
}
// str_index_of
fn test_str_index_of(_: String) -> String {
assert_int_eq(str_index_of("hello world", "world"), 6, "index of suffix")
assert_int_eq(str_index_of("hello world", "hello"), 0, "index of prefix")
assert_int_eq(str_index_of("hello world", "o"), 4, "index of first occurrence")
assert_int_eq(str_index_of("hello world", "xyz"), -1, "not found returns -1")
assert_int_eq(str_index_of("hello", ""), 0, "empty sub returns 0")
assert_int_eq(str_index_of("", "a"), -1, "search in empty string")
assert_int_eq(str_index_of("aababc", "ab"), 1, "finds first occurrence")
return ""
}
// str_replace
fn test_str_replace(_: String) -> String {
assert_eq(str_replace("hello world", "world", "there"), "hello there", "basic replace")
assert_eq(str_replace("aaa", "a", "b"), "bbb", "replace all occurrences")
assert_eq(str_replace("hello", "xyz", "abc"), "hello", "no match is identity")
assert_eq(str_replace("", "a", "b"), "", "empty string unchanged")
assert_eq(str_replace("hello", "", "x"), "hello", "empty from is identity")
assert_eq(str_replace("hello hello", "hello", "bye"), "bye bye", "replace multiple")
assert_eq(str_replace("aXbXc", "X", "-"), "a-b-c", "single-char delimiter")
return ""
}
// str_slice
fn test_str_slice(_: String) -> String {
assert_eq(str_slice("hello world", 0, 5), "hello", "slice from start")
assert_eq(str_slice("hello world", 6, 11), "world", "slice from middle")
assert_eq(str_slice("hello world", 0, 0), "", "zero-length slice")
assert_eq(str_slice("hello", 0, 100), "hello", "end beyond length clamped")
assert_eq(str_slice("hello", 3, 3), "", "start == end is empty")
assert_eq(str_slice("hello world", 2, 7), "llo w", "interior slice")
return ""
}
// str_to_upper / str_to_lower
fn test_str_to_upper(_: String) -> String {
assert_eq(str_to_upper("hello"), "HELLO", "lowercase to uppercase")
assert_eq(str_to_upper("HELLO"), "HELLO", "already uppercase unchanged")
assert_eq(str_to_upper("Hello World"), "HELLO WORLD", "mixed case")
assert_eq(str_to_upper(""), "", "empty string unchanged")
assert_eq(str_to_upper("hello123"), "HELLO123", "digits unchanged")
return ""
}
fn test_str_to_lower(_: String) -> String {
assert_eq(str_to_lower("HELLO"), "hello", "uppercase to lowercase")
assert_eq(str_to_lower("hello"), "hello", "already lowercase unchanged")
assert_eq(str_to_lower("Hello World"), "hello world", "mixed case")
assert_eq(str_to_lower(""), "", "empty string unchanged")
assert_eq(str_to_lower("HELLO123"), "hello123", "digits unchanged")
return ""
}
// str_trim
fn test_str_trim(_: String) -> String {
assert_eq(str_trim(" hello "), "hello", "trims spaces both sides")
assert_eq(str_trim("hello"), "hello", "no whitespace unchanged")
assert_eq(str_trim(" "), "", "all-space string becomes empty")
assert_eq(str_trim(""), "", "empty string unchanged")
assert_eq(str_trim("\t hello \n"), "hello", "trims tabs and newlines")
assert_eq(str_trim(" hello world "), "hello world", "internal spaces preserved")
return ""
}
// str_split
fn test_str_split(_: String) -> String {
let parts: [String] = str_split("a,b,c", ",")
assert_int_eq(el_list_len(parts), 3, "split yields 3 parts")
assert_eq(el_list_get(parts, 0), "a", "first part is a")
assert_eq(el_list_get(parts, 1), "b", "second part is b")
assert_eq(el_list_get(parts, 2), "c", "third part is c")
let single: [String] = str_split("hello", ",")
assert_int_eq(el_list_len(single), 1, "no sep found yields 1 part")
assert_eq(el_list_get(single, 0), "hello", "single part is the full string")
let trailing: [String] = str_split("a,b,", ",")
assert_int_eq(el_list_len(trailing), 3, "trailing sep yields empty last element")
assert_eq(el_list_get(trailing, 2), "", "last element is empty")
let empty_str: [String] = str_split("", ",")
assert_int_eq(el_list_len(empty_str), 1, "splitting empty string yields one empty element")
let multi: [String] = str_split("one::two::three", "::")
assert_int_eq(el_list_len(multi), 3, "multi-char separator works")
assert_eq(el_list_get(multi, 0), "one", "multi-sep first part")
assert_eq(el_list_get(multi, 2), "three", "multi-sep last part")
return ""
}
// str_join
fn test_str_join(_: String) -> String {
let parts: [String] = el_list_empty()
let parts = el_list_append(parts, "a")
let parts = el_list_append(parts, "b")
let parts = el_list_append(parts, "c")
assert_eq(str_join(parts, ","), "a,b,c", "basic join with comma")
assert_eq(str_join(parts, ""), "abc", "join with empty separator")
assert_eq(str_join(parts, " | "), "a | b | c", "join with multi-char sep")
let empty_list: [String] = el_list_empty()
assert_eq(str_join(empty_list, ","), "", "joining empty list yields empty string")
let one: [String] = el_list_empty()
let one = el_list_append(one, "solo")
assert_eq(str_join(one, ","), "solo", "joining single element yields that element")
return ""
}
// int_to_str / str_to_int
fn test_int_to_str(_: String) -> String {
assert_eq(int_to_str(0), "0", "zero")
assert_eq(int_to_str(42), "42", "positive integer")
assert_eq(int_to_str(-1), "-1", "negative integer")
assert_eq(int_to_str(1000000), "1000000", "large integer")
return ""
}
fn test_str_to_int(_: String) -> String {
assert_int_eq(str_to_int("0"), 0, "zero")
assert_int_eq(str_to_int("42"), 42, "positive integer")
assert_int_eq(str_to_int("-1"), -1, "negative integer")
assert_int_eq(str_to_int("1000000"), 1000000, "large integer")
return ""
}
// str_repeat
fn test_str_repeat(_: String) -> String {
assert_eq(str_repeat("ab", 3), "ababab", "repeat 3 times")
assert_eq(str_repeat("x", 1), "x", "repeat once")
assert_eq(str_repeat("x", 0), "", "repeat zero times yields empty")
assert_eq(str_repeat("", 5), "", "repeating empty string yields empty")
assert_eq(str_repeat("-", 4), "----", "single char repeat")
return ""
}
// str_reverse
fn test_str_reverse(_: String) -> String {
assert_eq(str_reverse("hello"), "olleh", "basic reverse")
assert_eq(str_reverse("a"), "a", "single char is its own reverse")
assert_eq(str_reverse(""), "", "empty string reverses to empty")
assert_eq(str_reverse("abcd"), "dcba", "even-length reverse")
assert_eq(str_reverse("racecar"), "racecar", "palindrome is unchanged")
return ""
}
// str_count
fn test_str_count(_: String) -> String {
assert_int_eq(str_count("hello world hello", "hello"), 2, "two occurrences")
assert_int_eq(str_count("aaa", "a"), 3, "adjacent single chars")
assert_int_eq(str_count("aaa", "aa"), 1, "non-overlapping: one match")
assert_int_eq(str_count("hello", "xyz"), 0, "no match")
assert_int_eq(str_count("", "a"), 0, "empty string has no occurrences")
assert_int_eq(str_count("hello", ""), 0, "empty sub returns 0")
return ""
}
// str_strip_prefix / str_strip_suffix
fn test_str_strip_prefix(_: String) -> String {
assert_eq(str_strip_prefix("foobar", "foo"), "bar", "strips matching prefix")
assert_eq(str_strip_prefix("foobar", "baz"), "foobar", "no-match is identity")
assert_eq(str_strip_prefix("hello", ""), "hello", "empty prefix is identity")
assert_eq(str_strip_prefix("hello", "hello"), "", "full match yields empty")
return ""
}
fn test_str_strip_suffix(_: String) -> String {
assert_eq(str_strip_suffix("hello.md", ".md"), "hello", "strips matching suffix")
assert_eq(str_strip_suffix("hello.md", ".txt"), "hello.md", "no-match is identity")
assert_eq(str_strip_suffix("hello", ""), "hello", "empty suffix is identity")
assert_eq(str_strip_suffix("hello", "hello"), "", "full match yields empty")
return ""
}
// str_find_chars
fn test_str_find_chars(_: String) -> String {
assert_int_eq(str_find_chars("hello world", " "), 5, "finds space at index 5")
assert_int_eq(str_find_chars("hello", "xyz"), -1, "not found returns -1")
assert_int_eq(str_find_chars("hello", ""), -1, "empty charset returns -1")
assert_int_eq(str_find_chars("hello", "aeiou"), 1, "finds first vowel at index 1")
assert_int_eq(str_find_chars("", "a"), -1, "search in empty string returns -1")
return ""
}
// str_last_index_of
fn test_str_last_index_of(_: String) -> String {
assert_int_eq(str_last_index_of("hello hello", "hello"), 6, "last occurrence")
assert_int_eq(str_last_index_of("hello", "hello"), 0, "single occurrence")
assert_int_eq(str_last_index_of("hello", "xyz"), -1, "not found returns -1")
assert_int_eq(str_last_index_of("aababc", "ab"), 3, "last ab in aababc")
return ""
}
// Entry point
fn main() -> Int {
test_case("str_eq", "test_str_eq")
test_case("str_len", "test_str_len")
test_case("str_concat", "test_str_concat")
test_case("str_starts_with", "test_str_starts_with")
test_case("str_ends_with", "test_str_ends_with")
test_case("str_contains", "test_str_contains")
test_case("str_index_of", "test_str_index_of")
test_case("str_replace", "test_str_replace")
test_case("str_slice", "test_str_slice")
test_case("str_to_upper", "test_str_to_upper")
test_case("str_to_lower", "test_str_to_lower")
test_case("str_trim", "test_str_trim")
test_case("str_split", "test_str_split")
test_case("str_join", "test_str_join")
test_case("int_to_str", "test_int_to_str")
test_case("str_to_int", "test_str_to_int")
test_case("str_repeat", "test_str_repeat")
test_case("str_reverse", "test_str_reverse")
test_case("str_count", "test_str_count")
test_case("str_strip_prefix", "test_str_strip_prefix")
test_case("str_strip_suffix", "test_str_strip_suffix")
test_case("str_find_chars", "test_str_find_chars")
test_case("str_last_index_of", "test_str_last_index_of")
return test_run_all()
}