├── go.mod ├── README.md ├── assert_test.go ├── LICENSE └── assert.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tidwall/assert 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Assert 2 | 3 | [![GoDoc](https://godoc.org/github.com/tidwall/assert?status.svg)](https://godoc.org/github.com/tidwall/assert) 4 | 5 | 6 | This package provides an assert function for Go. 7 | It's designed to work like [assert](https://man7.org/linux/man-pages/man3/assert.3.html) in C. 8 | 9 | ## Example 10 | 11 | ```go 12 | package my_test 13 | 14 | import "github.com/tidwall/assert" 15 | 16 | func TestMyThing(t *testing.T) { 17 | assert.Assert("hello" == "jello") 18 | } 19 | ``` 20 | 21 | This will print the following message and abort the program. 22 | 23 | ``` 24 | Assertion failed: ("hello" == "jello"), function TestMyThing, file my_test.go, line 6. 25 | ``` 26 | -------------------------------------------------------------------------------- /assert_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | type vv struct { 10 | } 11 | 12 | func (v *vv) do() { 13 | Assert(false) 14 | } 15 | 16 | var alt = func() string { 17 | var buf bytes.Buffer 18 | stderr = &buf 19 | exit = func(code int) {} 20 | Assert(false) 21 | if !strings.Contains(buf.String(), `function [anonymous]`) { 22 | panic("missing function") 23 | } 24 | return buf.String() 25 | } 26 | 27 | func TestAssert(t *testing.T) { 28 | var buf bytes.Buffer 29 | stderr = &buf 30 | exit = func(code int) {} 31 | var myval bool 32 | Assert( 33 | (myval == true || 34 | true == false || 35 | "h(el)\"" == "hello")) 36 | if !strings.HasPrefix(buf.String(), "Assertion failed:") { 37 | t.Fatal("missing failed text") 38 | } 39 | if !strings.Contains(buf.String(), 40 | `((myval == true || true == false || "h(el)\"" == "hello"))`) { 41 | t.Fatal("missing condition text") 42 | } 43 | buf.Reset() 44 | var v vv 45 | v.do() 46 | if !strings.Contains(buf.String(), `function vv.do`) { 47 | t.Fatal("missing function") 48 | } 49 | alt() 50 | } 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Josh Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /assert.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "path" 8 | "runtime" 9 | "strings" 10 | ) 11 | 12 | var stderr = io.Writer(os.Stderr) 13 | var exit = os.Exit 14 | 15 | // Assert aborts the program if assertion is false. 16 | func Assert(expression bool) { 17 | if !expression { 18 | atext := "at " 19 | ftext := "" 20 | _, file, ln, ok := runtime.Caller(1) 21 | if ok { 22 | data, err := os.ReadFile(file) 23 | if err == nil { 24 | lines := strings.Split(string(data), "\n") 25 | if ln-1 < len(lines) { 26 | if strings.Contains(lines[ln-1], "Assert") { 27 | atext = strings.Split(lines[ln-1], "Assert")[1] 28 | if len(atext) > 0 && atext[0] == '(' { 29 | // create a clean, single line expression 30 | var cond string 31 | body := []byte( 32 | atext + strings.Join(lines[ln:], "\n"), 33 | ) 34 | depth := 1 35 | for i := 1; i < len(body); i++ { 36 | if body[i] == '\n' { 37 | continue 38 | } else if body[i] == '\t' { 39 | body[i] = ' ' 40 | } 41 | if body[i] == ' ' && body[i-1] == ' ' { 42 | continue 43 | } 44 | cond += string(body[i]) 45 | if body[i] == '"' { 46 | i++ 47 | for ; i < len(body); i++ { 48 | cond += string(body[i]) 49 | if body[i] == '"' { 50 | if body[i-1] == '\\' { 51 | continue 52 | } 53 | break 54 | } 55 | } 56 | } else if body[i] == '(' { 57 | depth++ 58 | } else if body[i] == ')' { 59 | depth-- 60 | if depth == 0 { 61 | cond = cond[:len(cond)-1] 62 | break 63 | } 64 | } 65 | } 66 | if cond != "" { 67 | cond = strings.TrimSpace(cond) 68 | cond = strings.TrimSuffix(cond, ",") 69 | atext = "(" + cond + "), " 70 | } 71 | } 72 | } 73 | } 74 | 75 | fpcs := make([]uintptr, 1) 76 | // get the function name 77 | n := runtime.Callers(2, fpcs) 78 | if n > 0 { 79 | caller := runtime.FuncForPC(fpcs[0] - 1) 80 | if caller != nil { 81 | parts := strings.Split(caller.Name(), "/") 82 | ftext = parts[len(parts)-1] 83 | parts = strings.Split(ftext, ".") 84 | if len(parts) > 1 { 85 | ftext = strings.Join(parts[1:], ".") 86 | } 87 | ftext = strings.Replace(ftext, "(", "", -1) 88 | ftext = strings.Replace(ftext, ")", "", -1) 89 | ftext = strings.TrimPrefix(ftext, "*") 90 | if strings.Contains(ftext, "..") { 91 | ftext = "[anonymous]" 92 | } 93 | ftext = "function " + ftext + ", " 94 | } 95 | } 96 | } 97 | } 98 | // make file name relative, if possible 99 | if path.IsAbs(file) { 100 | dir, err := os.Getwd() 101 | if err == nil && strings.HasPrefix(file, dir) { 102 | tfile := file[len(dir):] 103 | if len(tfile) > 0 && (tfile[0] == '/' || tfile[0] == '\\') { 104 | file = tfile[1:] 105 | } 106 | } 107 | } 108 | fmt.Fprintf(stderr, "Assertion failed: %s%sfile %s, line %d.\n", 109 | atext, ftext, file, ln) 110 | exit(6) // SIGABRT 111 | } 112 | } 113 | --------------------------------------------------------------------------------