lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/exc/error_test.go (about)

     1  // Copyright (C) 2015-2020  Nexedi SA and Contributors.
     2  //                          Kirill Smelkov <kirr@nexedi.com>
     3  //
     4  // This program is free software: you can Use, Study, Modify and Redistribute
     5  // it under the terms of the GNU General Public License version 3, or (at your
     6  // option) any later version, as published by the Free Software Foundation.
     7  //
     8  // You can also Link and Combine this program with other software covered by
     9  // the terms of any of the Free Software licenses or any of the Open Source
    10  // Initiative approved licenses and Convey the resulting work. Corresponding
    11  // source of such a combination shall include the source code for all other
    12  // software used.
    13  //
    14  // This program is distributed WITHOUT ANY WARRANTY; without even the implied
    15  // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    16  //
    17  // See COPYING file for full licensing terms.
    18  // See https://www.nexedi.com/licensing for rationale and options.
    19  
    20  package exc
    21  
    22  import (
    23  	"errors"
    24  	"reflect"
    25  	"runtime"
    26  	"testing"
    27  
    28  	"lab.nexedi.com/kirr/go123/my"
    29  )
    30  
    31  func do_raise1() {
    32  	Raise(1)
    33  }
    34  
    35  func TestErrRaiseCatch(t *testing.T) {
    36  	defer Catch(func(e *Error) {
    37  		if !(e.arg == 1 && e.link == nil) {
    38  			t.Fatalf("error caught but unexpected: %#v  ; want {1, nil}", e)
    39  		}
    40  	})
    41  	do_raise1()
    42  	t.Fatal("error not caught")
    43  }
    44  
    45  // verify err chain has .arg(s) as expected
    46  func verifyErrChain(t *testing.T, e *Error, argv ...interface{}) {
    47  	i := 0
    48  	for ; e != nil; i, e = i+1, e.link {
    49  		if i >= len(argv) {
    50  			t.Fatal("too long error chain")
    51  		}
    52  		if e.arg != argv[i] {
    53  			t.Fatalf("error caught but unexpected %vth arg: %v  ; want %v", i, e.arg, argv[i])
    54  		}
    55  	}
    56  	if i < len(argv) {
    57  		t.Fatal("too small error chain")
    58  	}
    59  }
    60  
    61  func do_onunwind1(t *testing.T) {
    62  	defer Onunwind(func(e *Error) *Error {
    63  		t.Fatal("on unwind called without raise")
    64  		return nil
    65  	})
    66  }
    67  
    68  func do_onunwind2() {
    69  	defer Onunwind(func(e *Error) *Error {
    70  		return &Error{2, e}
    71  	})
    72  	do_raise1()
    73  }
    74  
    75  func TestErrOnUnwind(t *testing.T) {
    76  	defer Catch(func(e *Error) {
    77  		verifyErrChain(t, e, 2, 1)
    78  	})
    79  	do_onunwind1(t)
    80  	do_onunwind2()
    81  	t.Fatal("error not caught")
    82  }
    83  
    84  func do_context1(t *testing.T) {
    85  	defer Context(func() interface{} {
    86  		t.Fatal("on context called without raise")
    87  		return nil
    88  	})
    89  }
    90  
    91  func do_context2() {
    92  	defer Context(func() interface{} {
    93  		return 3
    94  	})
    95  	do_raise1()
    96  }
    97  
    98  func TestErrContext(t *testing.T) {
    99  	defer Catch(func(e *Error) {
   100  		verifyErrChain(t, e, 3, 1)
   101  	})
   102  	do_context1(t)
   103  	do_context2()
   104  	t.Fatal("error not caught")
   105  }
   106  
   107  func do_contextf1() {
   108  	defer Contextf("must not be added")
   109  	return // no exception
   110  }
   111  
   112  func do_contextf2() {
   113  	defer Contextf("hello %d world", 123)
   114  	do_raise1()
   115  }
   116  
   117  func TestErrContextf(t *testing.T) {
   118  	defer Catch(func(e *Error) {
   119  		verifyErrChain(t, e, "hello 123 world", 1)
   120  	})
   121  	do_contextf1()
   122  	do_contextf2()
   123  	t.Fatal("error not caught")
   124  }
   125  
   126  func do_raise11() {
   127  	do_raise1()
   128  }
   129  
   130  func do_raise3if() {
   131  	Raiseif(errors.New("3"))
   132  }
   133  
   134  func do_raise3if1() {
   135  	do_raise3if()
   136  }
   137  
   138  func do_raise4f() {
   139  	Raisef("%d", 4)
   140  }
   141  
   142  func do_raise4f1() {
   143  	do_raise4f()
   144  }
   145  
   146  // get name of a function
   147  func funcname(f interface{}) string {
   148  	fentry := reflect.ValueOf(f).Pointer()
   149  	ffunc := runtime.FuncForPC(fentry)
   150  	return ffunc.Name()
   151  }
   152  
   153  func TestErrAddCallingContext(t *testing.T) {
   154  	var tests = []struct { f func(); wanterrcontext string } {
   155  		{do_raise11,	"do_raise11: do_raise1: 1"},
   156  		{do_raise3if1,	"do_raise3if1: do_raise3if: 3"},
   157  		{do_raise4f1,	"do_raise4f1: do_raise4f: 4"},
   158  	}
   159  
   160  	for _, tt := range tests {
   161  		func() {
   162  			myfunc := my.FuncName()
   163  			defer Catch(func(e *Error) {
   164  				e = Addcallingcontext(myfunc, e)
   165  				msg := e.Error()
   166  				if msg != tt.wanterrcontext {
   167  					t.Fatalf("%v: err + calling context: %q  ; want %q", funcname(tt.f), msg, tt.wanterrcontext)
   168  				}
   169  			})
   170  			tt.f()
   171  			t.Fatalf("%v: error not caught", funcname(tt.f))
   172  		}()
   173  	}
   174  }
   175  
   176  func TestRunx(t *testing.T) {
   177  	var tests = []struct { f func(); wanterr string } {
   178  		{func() {},	""},
   179  		{do_raise11,	"do_raise11: do_raise1: 1"},
   180  	}
   181  
   182  	for _, tt := range tests {
   183  		err := Runx(tt.f)
   184  		if err == nil {
   185  			if tt.wanterr != "" {
   186  				t.Errorf("runx(%v) -> nil  ; want %q error", funcname(tt.f), tt.wanterr)
   187  			}
   188  			continue
   189  		}
   190  		msg := err.Error()
   191  		if msg != tt.wanterr {
   192  			t.Errorf("runx(%v) -> %q  ; want %q", funcname(tt.f), msg, tt.wanterr)
   193  		}
   194  	}
   195  }
   196  
   197  func TestXRun(t *testing.T) {
   198  	var tests = []struct { f func() error; wanterr string } {
   199  		{func() error { return nil },			""},
   200  		{func() error { return errors.New("abc") },	"X abc"},
   201  	}
   202  
   203  	for _, tt := range tests {
   204  		errStr := ""
   205  		func() {
   206  			defer Catch(func(e *Error) {
   207  				errStr = "X " + e.Error()
   208  			})
   209  			XRun(tt.f)
   210  		}()
   211  
   212  		if errStr != tt.wanterr {
   213  			t.Errorf("xrun(%v) -> %q  ; want %q", funcname(tt.f), errStr, tt.wanterr)
   214  		}
   215  	}
   216  }