Replace AnyError in tests with a specific error

The `AnyError` placeholder error can cause tests to succeed for errors
other than what was expected. That is, the use of `AnyError` can mask
bugs in a unit test. So replace it with the specific error, or error type,
the test expects to be raised.

This does not remove the anyError structure because it is used in
the TestCase.DoesNotCompile() method. To keep the size of this change
as small as possible I want to defer updating that use to a separate
change. However, remove the public AnyError var so future test writers
don't attempt to use it.
This commit is contained in:
Kurtis Rader 2021-07-14 19:31:01 -07:00 committed by Qi Xiao
parent 95baad069e
commit 87656c99fa
26 changed files with 109 additions and 77 deletions

View File

@ -193,7 +193,7 @@ func TestSmartEnter_AcceptsCodeWhenWholeBufferIsComplete(t *testing.T) {
func TestWordify(t *testing.T) {
TestWithSetup(t, setupWordify,
That("wordify 'ls str [list]'").Puts("ls", "str", "[list]"),
That("wordify foo >&-").Throws(AnyError),
That("wordify foo >&-").Throws(eval.ErrNoValueOutput),
)
}

View File

@ -6,6 +6,7 @@ import (
"src.elv.sh/pkg/eval"
"src.elv.sh/pkg/eval/errs"
. "src.elv.sh/pkg/eval/evaltest"
"src.elv.sh/pkg/eval/vals"
)
func TestAddVar(t *testing.T) {
@ -20,7 +21,7 @@ func TestAddVar(t *testing.T) {
What: "name argument to edit:add-var",
Valid: "unqualified variable name", Actual: "a:b"}),
// Bad type
That("add-var a~ ''").Throws(AnyError),
That("add-var a~ ''").Throws(ErrorWithType(vals.WrongType{})),
)
}
@ -43,6 +44,6 @@ func TestAddVars(t *testing.T) {
What: "key of argument to edit:add-vars",
Valid: "unqualified variable name", Actual: "a:b"}),
// Bad type
That("add-vars [&a~='']").Throws(AnyError),
That("add-vars [&a~='']").Throws(ErrorWithType(vals.WrongType{})),
)
}

View File

@ -4,6 +4,7 @@
package eval_test
import (
"os/exec"
"testing"
. "src.elv.sh/pkg/eval/evaltest"
@ -32,7 +33,7 @@ func TestSearchExternal(t *testing.T) {
// TODO: Replace the raw Go runtime `exec.LookPath` error with an
// Elvish error; possibly wrapping the Go runtime error. Then tighten
// this test to that specific error.
That("search-external random-invalid-command").Throws(AnyError),
That("search-external random-invalid-command").Throws(ErrorWithType(&exec.Error{})),
)
}

View File

@ -618,7 +618,7 @@ func one(fm *Frame, inputs Inputs) error {
if n == 1 {
return fm.ValueOutput().Put(val)
}
return fmt.Errorf("expect a single value, got %d", n)
return errs.ArityMismatch{What: "values", ValidLow: 1, ValidHigh: 1, Actual: n}
}
//elvdoc:fn take

View File

@ -161,11 +161,11 @@ func TestAll(t *testing.T) {
func TestOne(t *testing.T) {
Test(t,
That(`put foo | one`).Puts("foo"),
That(`put | one`).Throws(AnyError),
That(`put foo bar | one`).Throws(AnyError),
That(`put | one`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`put foo bar | one`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`one [foo]`).Puts("foo"),
That(`one []`).Throws(AnyError),
That(`one [foo bar]`).Throws(AnyError),
That(`one []`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`one [foo bar]`).Throws(ErrorWithType(errs.ArityMismatch{})),
thatOutputErrorIsBubbled("one [foo]"),
)
}

View File

@ -28,7 +28,7 @@ func TestEach(t *testing.T) {
That(`range 10 | each {|x| if (== $x 4) { continue }; put $x }`).
Puts(0, 1, 2, 3, 5, 6, 7, 8, 9),
That(`range 10 | each {|x| if (== $x 4) { fail haha }; put $x }`).
Puts(0, 1, 2, 3).Throws(AnyError),
Puts(0, 1, 2, 3).Throws(FailError{"haha"}),
// TODO(xiaq): Test that "each" does not close the stdin.
)
}

View File

@ -1,6 +1,7 @@
package eval_test
import (
"encoding/json"
"os"
"testing"
@ -132,7 +133,7 @@ func TestFromJson(t *testing.T) {
"foo"),
That(`echo '[null, "foo"]' | from-json`).Puts(
vals.MakeList(nil, "foo")),
That(`echo 'invalid' | from-json`).Throws(AnyError),
That(`echo 'invalid' | from-json`).Throws(ErrorWithType(&json.SyntaxError{})),
thatOutputErrorIsBubbled(`echo '[]' | from-json`),
)
}

View File

@ -5,7 +5,9 @@ import (
"testing"
"time"
"src.elv.sh/pkg/diag"
. "src.elv.sh/pkg/eval"
"src.elv.sh/pkg/parse"
. "src.elv.sh/pkg/eval/evaltest"
"src.elv.sh/pkg/eval/vals"
@ -48,9 +50,9 @@ func TestEval(t *testing.T) {
// namespace using &on-end.
That("eval &on-end={|n| put $n[x] } 'x = foo'").Puts("foo"),
// Parse error.
That("eval '['").Throws(AnyError),
That("eval '['").Throws(ErrorWithType(&parse.Error{})),
// Compilation error.
That("eval 'put $x'").Throws(AnyError),
That("eval 'put $x'").Throws(ErrorWithType(&diag.Error{})),
// Exception.
That("eval 'fail x'").Throws(FailError{"x"}),
)

View File

@ -37,8 +37,8 @@ func TestBase(t *testing.T) {
Test(t,
That(`base 2 1 3 4 16 255`).Puts("1", "11", "100", "10000", "11111111"),
That(`base 16 42 233`).Puts("2a", "e9"),
That(`base 1 1`).Throws(AnyError), // no base-1
That(`base 37 10`).Throws(AnyError), // no letter for base-37
That(`base 1 1`).Throws(ErrBadBase),
That(`base 37 10`).Throws(ErrBadBase),
thatOutputErrorIsBubbled("base 2 1"),
)
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"testing"
"src.elv.sh/pkg/diag"
. "src.elv.sh/pkg/eval"
"src.elv.sh/pkg/eval/errs"
"src.elv.sh/pkg/eval/vals"
@ -292,7 +293,7 @@ func TestWhile(t *testing.T) {
"while (< $x 4) { put $x; set x = (+ $x 1); continue; put bad }").
Puts(0, 1, 2, 3),
// Exception in body
That("var x = 0; while (< $x 4) { fail haha }").Throws(AnyError),
That("var x = 0; while (< $x 4) { fail haha }").Throws(FailError{"haha"}),
// Exception in condition
That("while (fail x) { }").Throws(FailError{"x"}, "fail x"),
@ -459,7 +460,7 @@ func TestUse(t *testing.T) {
// Variables defined in the default global scope is invisible from
// modules
That("x = foo; use put-x").Throws(AnyError),
That("x = foo; use put-x").Throws(ErrorWithType(&diag.Error{})),
// Using an unknown module spec fails.
That("use unknown").Throws(ErrorWithType(NoSuchModule{})),

View File

@ -479,9 +479,9 @@ type redirOp struct {
flag int
}
type invalidFD struct{ fd int }
type InvalidFD struct{ Fd int }
func (err invalidFD) Error() string { return fmt.Sprintf("invalid fd: %d", err.fd) }
func (err InvalidFD) Error() string { return fmt.Sprintf("invalid fd: %d", err.Fd) }
func (op *redirOp) exec(fm *Frame) Exception {
var dst int
@ -519,7 +519,7 @@ func (op *redirOp) exec(fm *Frame) Exception {
// Ensure that writing to value output throws an exception
sendStop: closedSendStop, sendError: &ErrNoValueOutput}
case src >= len(fm.ports) || fm.ports[src] == nil:
return fm.errorp(op, invalidFD{src})
return fm.errorp(op, InvalidFD{Fd: src})
default:
fm.ports[dst] = fm.ports[src].fork()
}

View File

@ -19,7 +19,7 @@ func TestChunk(t *testing.T) {
// Outputs of pipelines in a chunk are concatenated
That("put x; put y; put z").Puts("x", "y", "z"),
// A failed pipeline cause the whole chunk to fail
That("put a; e:false; put b").Puts("a").Throws(AnyError),
That("put a; e:false; put b").Puts("a").Throws(ErrorWithType(ExternalCmdExit{})),
)
}
@ -284,7 +284,7 @@ func TestCommand_Redir(t *testing.T) {
That(`{ echo foobar >&stderr } stderr> out4`, `slurp < out4`).
Puts("foobar\n"),
// Using a new FD as source throws an exception.
That(`echo foo >&4`).Throws(AnyError),
That(`echo foo >&4`).Throws(InvalidFD{Fd: 4}),
// Using a new FD as destination is OK, and makes it available.
That(`{ echo foo >&4 } 4>out5`, `slurp < out5`).Puts("foo\n"),

View File

@ -42,10 +42,6 @@ func matchRegexp(p, s string) bool {
type errorMatcher interface{ matchError(error) bool }
// AnyError is an error that can be passed to Case.Throws to match any non-nil
// error.
var AnyError = anyError{}
// An errorMatcher for any error.
type anyError struct{}

View File

@ -2,6 +2,7 @@ package eval_test
import (
"os"
"os/exec"
"testing"
. "src.elv.sh/pkg/eval"
@ -30,6 +31,6 @@ func TestBuiltinFnExternal(t *testing.T) {
//
// The command shouldn't be found when run so we should get an
// exception along the lines of "executable file not found in $PATH".
That(`e = (external true); E:PATH=/ $e`).Throws(AnyError),
That(`e = (external true); E:PATH=/ $e`).Throws(ErrorWithType(&exec.Error{})),
)
}

View File

@ -17,6 +17,15 @@ var (
ErrNoOptAccepted = errors.New("function does not accept any options")
)
type WrongArgType struct {
argNum int
typeError string
}
func (e WrongArgType) Error() string {
return fmt.Sprintf("wrong type for arg #%d: %s", e.argNum, e.typeError)
}
type goFn struct {
name string
impl interface{}
@ -201,7 +210,7 @@ func (b *goFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) e
ptr := reflect.New(typ)
err := vals.ScanToGo(arg, ptr.Interface())
if err != nil {
return fmt.Errorf("wrong type of argument %d: %v", i, err)
return WrongArgType{i, err.Error()}
}
in = append(in, ptr.Elem())
}

View File

@ -9,6 +9,7 @@ import (
. "src.elv.sh/pkg/eval"
"src.elv.sh/pkg/eval/errs"
. "src.elv.sh/pkg/eval/evaltest"
"src.elv.sh/pkg/eval/vals"
)
type someOptions struct {
@ -51,12 +52,12 @@ func TestGoFn_RawOptions(t *testing.T) {
}
})),
// Invalid option; regression test for #958.
That("f &bad=bar").Throws(AnyError).
That("f &bad=bar").Throws(BadOption{"bad"}).
WithSetup(f(func(opts someOptions) {
t.Errorf("function called when there are invalid options")
})),
// Invalid option type; regression test for #958.
That("f &foo=[]").Throws(AnyError).
That("f &foo=[]").Throws(ErrorWithType(vals.WrongType{})).
WithSetup(f(func(opts someOptions) {
t.Errorf("function called when there are invalid options")
})),
@ -115,11 +116,11 @@ func TestGoFn_RawOptions(t *testing.T) {
t.Errorf("Function called when there are too few arguments")
})),
// Wrong argument type
That("f (num 1)").Throws(AnyError).
That("f (num 1)").Throws(ErrorWithType(WrongArgType{})).
WithSetup(f(func(x string) {
t.Errorf("Function called when arguments have wrong type")
})),
That("f str").Throws(AnyError).
That("f str").Throws(ErrorWithType(WrongArgType{})).
WithSetup(f(func(x int) {
t.Errorf("Function called when arguments have wrong type")
})),

View File

@ -9,6 +9,14 @@ import (
"src.elv.sh/pkg/strutil"
)
type BadOption struct {
OptName string
}
func (e BadOption) Error() string {
return "unknown option: " + parse.Quote(e.OptName)
}
// RawOptions is the type of an argument a Go-native function can take to
// declare that it wants to parse options itself. See the doc of NewGoFn for
// details.
@ -44,7 +52,7 @@ func scanOptions(rawOpts RawOptions, ptr interface{}) error {
for k, v := range rawOpts {
fieldIdx, ok := fieldIdxForOpt[k]
if !ok {
return fmt.Errorf("unknown option %s", parse.Quote(k))
return BadOption{k}
}
err := vals.ScanToGo(v, struc.Field(fieldIdx).Addr().Interface())
if err != nil {

View File

@ -1,7 +1,6 @@
package eval
import (
"errors"
"testing"
)
@ -24,7 +23,7 @@ var scanOptionsTests = []struct {
opts{}, opts{POSIX: true}, nil},
// Since "ignore" is not exported it will result in an error when used.
{RawOptions{"ignore": true},
opts{}, opts{ignore: false}, errors.New("unknown option ignore")},
opts{}, opts{ignore: false}, BadOption{"ignore"}},
}
func TestScanOptions(t *testing.T) {

View File

@ -28,12 +28,12 @@ import (
// entirely go away, as there might always be some mismatch between Elvish's
// type system and Go's.
type wrongType struct {
type WrongType struct {
wantKind string
gotKind string
}
func (err wrongType) Error() string {
func (err WrongType) Error() string {
return fmt.Sprintf("wrong type: need %s, got %s", err.wantKind, err.gotKind)
}
@ -100,7 +100,7 @@ func ScanToGo(src interface{}, ptr interface{}) error {
} else {
dstKind = Kind(reflect.Zero(dstType).Interface())
}
return wrongType{dstKind, Kind(src)}
return WrongType{dstKind, Kind(src)}
}
ValueOf(ptr).Elem().Set(ValueOf(src))
return nil

View File

@ -50,7 +50,7 @@ func TestScanToGo_ConcreteTypeDst(t *testing.T) {
Args("foo", "").Rets("foo"),
Args(someType{"foo"}, someType{}).Rets(someType{"foo"}),
Args(nil, nil).Rets(nil),
Args("x", someType{}).Rets(Any, wrongType{"!!vals.someType", "string"}),
Args("x", someType{}).Rets(Any, WrongType{"!!vals.someType", "string"}),
})
}
@ -88,7 +88,7 @@ func TestScanToGo_InterfaceDst(t *testing.T) {
Test(t, Fn("ScanToGo", scanToGo), Table{
Args(EmptyList).Rets(EmptyList),
Args("foo").Rets(Any, wrongType{"!!vector.Vector", "string"}),
Args("foo").Rets(Any, WrongType{"!!vector.Vector", "string"}),
})
}

View File

@ -40,7 +40,7 @@ func TestFile(t *testing.T) {
echo Legolas > $p
file:close $p[r]
slurp < $p
`).Throws(AnyError),
`).Throws(ErrorWithType(&os.PathError{})),
// Verify that input redirection from a closed pipe throws an exception. That exception is a
// Go stdlib error whose stringified form looks something like "read |0: file already

View File

@ -114,7 +114,7 @@ func TestPath_Symlink(t *testing.T) {
That("path:eval-symlinks d/f").Puts(filepath.Join("d", "f")),
That("path:eval-symlinks d/s-f").Puts(filepath.Join("d", "f")),
That("path:eval-symlinks s-d/f").Puts(filepath.Join("d", "f")),
That("path:eval-symlinks s-bad").Throws(AnyError),
That("path:eval-symlinks s-bad").Throws(ErrorWithType(&os.PathError{})),
That("path:is-dir s-d").Puts(false),
That("path:is-dir s-d &follow-symlink").Puts(true),

View File

@ -53,6 +53,14 @@ var Ns = eval.BuildNsNamed("re").
// ▶ $false
// ```
type ReplaceError struct {
What string
}
func (err *ReplaceError) Error() string {
return err.What
}
type matchOpts struct{ Posix bool }
func (*matchOpts) SetDefaultOptions() {}
@ -187,9 +195,9 @@ func replace(fm *eval.Frame, opts replaceOpts, argPattern string, argRepl interf
if opts.Literal {
repl, ok := argRepl.(string)
if !ok {
return "", fmt.Errorf(
return "", &ReplaceError{fmt.Sprintf(
"replacement must be string when literal is set, got %s",
vals.Kind(argRepl))
vals.Kind(argRepl))}
}
return pattern.ReplaceAllLiteralString(source, repl), nil
}
@ -210,23 +218,25 @@ func replace(fm *eval.Frame, opts replaceOpts, argPattern string, argRepl interf
return ""
}
if len(values) != 1 {
errReplace = fmt.Errorf("replacement function must output exactly one value, got %d", len(values))
msg := fmt.Sprintf("replacement function must output one value, got %d",
len(values))
errReplace = &ReplaceError{msg}
return ""
}
output, ok := values[0].(string)
if !ok {
errReplace = fmt.Errorf(
"replacement function must output one string, got %s",
msg := fmt.Sprintf("replacement function must output one string, got %q",
vals.Kind(values[0]))
errReplace = &ReplaceError{msg}
return ""
}
return output
}
return pattern.ReplaceAllStringFunc(source, replFunc), errReplace
default:
return "", fmt.Errorf(
return "", &ReplaceError{fmt.Sprintf(
"replacement must be string or function, got %s",
vals.Kind(argRepl))
vals.Kind(argRepl))}
}
}

View File

@ -1,6 +1,7 @@
package re
import (
"regexp/syntax"
"testing"
"src.elv.sh/pkg/eval"
@ -18,7 +19,7 @@ func TestRe(t *testing.T) {
That("re:match '[a-z]' A").Puts(false),
// Invalid pattern in re:match
That("re:match '(' x").Throws(AnyError),
That("re:match '(' x").Throws(ErrorWithType(&syntax.Error{})),
That("re:find . ab").Puts(
matchStruct{"a", 0, 1, vals.MakeList(submatchStruct{"a", 0, 1})},
@ -36,7 +37,7 @@ func TestRe(t *testing.T) {
Puts("a", 0, 1, vals.MakeList(submatchStruct{"a", 0, 1})),
// Invalid pattern in re:find
That("re:find '(' x").Throws(AnyError),
That("re:find '(' x").Throws(ErrorWithType(&syntax.Error{})),
// Without any flag, finds ax
That("put (re:find 'a(x|xy)' AaxyZ)[text]").Puts("ax"),
@ -53,21 +54,21 @@ func TestRe(t *testing.T) {
That("re:replace '(ba|z)sh' {|x| put [&bash=BaSh &zsh=ZsH][$x] } 'bash and zsh'").Puts("BaSh and ZsH"),
// Invalid pattern in re:replace
That("re:replace '(' x bash").Throws(AnyError),
That("re:replace &posix '[[:argle:]]' x bash").Throws(AnyError),
That("re:replace '(' x bash").Throws(ErrorWithType(&syntax.Error{})),
That("re:replace &posix '[[:argle:]]' x bash").Throws(ErrorWithType(&syntax.Error{})),
// Replacement function outputs more than one value
That("re:replace x {|x| put a b } xx").Throws(AnyError),
That("re:replace x {|x| put a b } xx").Throws(ErrorWithType(&ReplaceError{})),
// Replacement function outputs non-string value
That("re:replace x {|x| put [] } xx").Throws(AnyError),
That("re:replace x {|x| put [] } xx").Throws(ErrorWithType(&ReplaceError{})),
// Replacement is not string or function
That("re:replace x [] xx").Throws(AnyError),
That("re:replace x [] xx").Throws(ErrorWithType(&ReplaceError{})),
// Replacement is function when &literal is set
That("re:replace &literal x {|_| put y } xx").Throws(AnyError),
That("re:replace &literal x {|_| put y } xx").Throws(ErrorWithType(&ReplaceError{})),
That("re:split : /usr/sbin:/usr/bin:/bin").Puts("/usr/sbin", "/usr/bin", "/bin"),
That("re:split &max=2 : /usr/sbin:/usr/bin:/bin").Puts("/usr/sbin", "/usr/bin:/bin"),
// Invalid pattern in re:split
That("re:split '(' x").Throws(AnyError),
That("re:split '(' x").Throws(ErrorWithType(&syntax.Error{})),
// re:split bubbles output error
That("re:split . ab >&-").Throws(eval.ErrNoValueOutput),

View File

@ -58,7 +58,7 @@ func TestStore(t *testing.T) {
That("store:shared-var bar").Puts("ipsum"),
// Delete shared variables
That("store:del-shared-var foo").DoesNothing(),
That("store:shared-var foo").Throws(AnyError),
That("store:shared-var foo").Throws(store.ErrNoSharedVar),
)
}

View File

@ -16,21 +16,21 @@ func TestStr(t *testing.T) {
ev.ExtendGlobal(eval.BuildNs().AddNs("str", Ns))
}
TestWithSetup(t, setup,
That(`str:compare abc`).Throws(AnyError),
That(`str:compare abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:compare abc abc`).Puts(0),
That(`str:compare abc def`).Puts(-1),
That(`str:compare def abc`).Puts(1),
That(`str:contains abc`).Throws(AnyError),
That(`str:contains abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:contains abcd x`).Puts(false),
That(`str:contains abcd bc`).Puts(true),
That(`str:contains abcd cde`).Puts(false),
That(`str:contains-any abc`).Throws(AnyError),
That(`str:contains-any abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:contains-any abcd x`).Puts(false),
That(`str:contains-any abcd xcy`).Puts(true),
That(`str:equal-fold abc`).Throws(AnyError),
That(`str:equal-fold abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:equal-fold ABC abc`).Puts(true),
That(`str:equal-fold abc ABC`).Puts(true),
That(`str:equal-fold abc A`).Puts(false),
@ -67,19 +67,19 @@ func TestStr(t *testing.T) {
Valid: "valid UTF-8 sequence",
Actual: "[255 3 170]"}),
That(`str:has-prefix abc`).Throws(AnyError),
That(`str:has-prefix abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:has-prefix abcd ab`).Puts(true),
That(`str:has-prefix abcd cd`).Puts(false),
That(`str:has-suffix abc`).Throws(AnyError),
That(`str:has-suffix abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:has-suffix abcd ab`).Puts(false),
That(`str:has-suffix abcd cd`).Puts(true),
That(`str:index abc`).Throws(AnyError),
That(`str:index abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:index abcd cd`).Puts(2),
That(`str:index abcd de`).Puts(-1),
That(`str:index-any abc`).Throws(AnyError),
That(`str:index-any abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:index-any "chicken" "aeiouy"`).Puts(2),
That(`str:index-any l33t aeiouy`).Puts(-1),
@ -88,7 +88,7 @@ func TestStr(t *testing.T) {
That(`str:join : [(float64 1) 2]`).Throws(
errs.BadValue{What: "input to str:join", Valid: "string", Actual: "number"}),
That(`str:last-index abc`).Throws(AnyError),
That(`str:last-index abc`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:last-index "elven speak elvish" "elv"`).Puts(12),
That(`str:last-index "elven speak elvish" "romulan"`).Puts(-1),
@ -111,38 +111,39 @@ func TestStr(t *testing.T) {
That(`str:title abc`).Puts("Abc"),
That(`str:title "abc def"`).Puts("Abc Def"),
That(`str:to-lower abc def`).Throws(AnyError),
That(`str:to-lower abc def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:to-lower abc`).Puts("abc"),
That(`str:to-lower ABC`).Puts("abc"),
That(`str:to-lower ABC def`).Throws(AnyError),
That(`str:to-lower ABC def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:to-title "her royal highness"`).Puts("HER ROYAL HIGHNESS"),
That(`str:to-title "хлеб"`).Puts("ХЛЕБ"),
That(`str:to-upper abc`).Puts("ABC"),
That(`str:to-upper ABC`).Puts("ABC"),
That(`str:to-upper ABC def`).Throws(AnyError),
That(`str:to-upper ABC def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:trim "¡¡¡Hello, Elven!!!" "!¡"`).Puts("Hello, Elven"),
That(`str:trim def`).Throws(AnyError),
That(`str:trim def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:trim-left "¡¡¡Hello, Elven!!!" "!¡"`).Puts("Hello, Elven!!!"),
That(`str:trim-left def`).Throws(AnyError),
That(`str:trim-left def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:trim-prefix "¡¡¡Hello, Elven!!!" "¡¡¡Hello, "`).Puts("Elven!!!"),
That(`str:trim-prefix "¡¡¡Hello, Elven!!!" "¡¡¡Hola, "`).Puts("¡¡¡Hello, Elven!!!"),
That(`str:trim-prefix def`).Throws(AnyError),
That(`str:trim-prefix def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:trim-right "¡¡¡Hello, Elven!!!" "!¡"`).Puts("¡¡¡Hello, Elven"),
That(`str:trim-right def`).Throws(AnyError),
That(`str:trim-right def`).Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:trim-space " \t\n Hello, Elven \n\t\r\n"`).Puts("Hello, Elven"),
That(`str:trim-space " \t\n Hello Elven \n\t\r\n"`).Puts("Hello Elven"),
That(`str:trim-space " \t\n Hello Elven \n\t\r\n" argle`).Throws(AnyError),
That(`str:trim-space " \t\n Hello Elven \n\t\r\n" argle`).
Throws(ErrorWithType(errs.ArityMismatch{})),
That(`str:trim-suffix "¡¡¡Hello, Elven!!!" ", Elven!!!"`).Puts("¡¡¡Hello"),
That(`str:trim-suffix "¡¡¡Hello, Elven!!!" ", Klingons!!!"`).Puts("¡¡¡Hello, Elven!!!"),
That(`str:trim-suffix "¡¡¡Hello, Elven!!!"`).Throws(AnyError),
That(`str:trim-suffix "¡¡¡Hello, Elven!!!"`).Throws(ErrorWithType(errs.ArityMismatch{})),
)
}