gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/errors/linuxerr/linuxerr_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package linuxerr_test
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"io/fs"
    22  	"syscall"
    23  	"testing"
    24  
    25  	"golang.org/x/sys/unix"
    26  	gErrors "gvisor.dev/gvisor/pkg/errors"
    27  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    28  )
    29  
    30  var globalError error
    31  
    32  func BenchmarkAssignUnix(b *testing.B) {
    33  	for i := b.N; i > 0; i-- {
    34  		globalError = unix.EINVAL
    35  	}
    36  }
    37  
    38  func BenchmarkAssignLinuxerr(b *testing.B) {
    39  	for i := b.N; i > 0; i-- {
    40  		globalError = linuxerr.EINVAL
    41  	}
    42  }
    43  
    44  func BenchmarkCompareUnix(b *testing.B) {
    45  	globalError = unix.EAGAIN
    46  	j := 0
    47  	for i := b.N; i > 0; i-- {
    48  		if globalError == unix.EINVAL {
    49  			j++
    50  		}
    51  	}
    52  }
    53  
    54  func BenchmarkCompareLinuxerr(b *testing.B) {
    55  	globalError = linuxerr.E2BIG
    56  	j := 0
    57  	for i := b.N; i > 0; i-- {
    58  		if globalError == linuxerr.EINVAL {
    59  			j++
    60  		}
    61  	}
    62  }
    63  
    64  func BenchmarkSwitchUnix(b *testing.B) {
    65  	globalError = unix.EPERM
    66  	j := 0
    67  	for i := b.N; i > 0; i-- {
    68  		switch globalError {
    69  		case unix.EINVAL:
    70  			j++
    71  		case unix.EINTR:
    72  			j += 2
    73  		case unix.EAGAIN:
    74  			j += 3
    75  		}
    76  	}
    77  }
    78  
    79  func BenchmarkSwitchLinuxerr(b *testing.B) {
    80  	globalError = linuxerr.EPERM
    81  	j := 0
    82  	for i := b.N; i > 0; i-- {
    83  		switch globalError {
    84  		case linuxerr.EINVAL:
    85  			j++
    86  		case linuxerr.EINTR:
    87  			j += 2
    88  		case linuxerr.EAGAIN:
    89  			j += 3
    90  		}
    91  	}
    92  }
    93  
    94  func BenchmarkReturnUnix(b *testing.B) {
    95  	var localError error
    96  	f := func() error {
    97  		return unix.EINVAL
    98  	}
    99  	for i := b.N; i > 0; i-- {
   100  		localError = f()
   101  	}
   102  	if localError != nil {
   103  		return
   104  	}
   105  }
   106  
   107  func BenchmarkReturnLinuxerr(b *testing.B) {
   108  	var localError error
   109  	f := func() error {
   110  		return linuxerr.EINVAL
   111  	}
   112  	for i := b.N; i > 0; i-- {
   113  		localError = f()
   114  	}
   115  	if localError != nil {
   116  		return
   117  	}
   118  }
   119  
   120  func BenchmarkConvertUnixLinuxerr(b *testing.B) {
   121  	var localError error
   122  	for i := b.N; i > 0; i-- {
   123  		localError = linuxerr.ErrorFromUnix(unix.EINVAL)
   124  	}
   125  	if localError != nil {
   126  		return
   127  	}
   128  }
   129  
   130  func BenchmarkConvertUnixLinuxerrZero(b *testing.B) {
   131  	var localError error
   132  	for i := b.N; i > 0; i-- {
   133  		localError = linuxerr.ErrorFromUnix(unix.Errno(0))
   134  	}
   135  	if localError != nil {
   136  		return
   137  	}
   138  }
   139  
   140  type translationTestTable struct {
   141  	errIn               error
   142  	expectedBool        bool
   143  	expectedTranslation *gErrors.Error
   144  }
   145  
   146  func TestErrorTranslation(t *testing.T) {
   147  	testTable := []translationTestTable{
   148  		{
   149  			errIn: linuxerr.ENOENT,
   150  		},
   151  		{
   152  			errIn: unix.ENOENT,
   153  		},
   154  		{
   155  			errIn:               linuxerr.ErrInterrupted,
   156  			expectedBool:        true,
   157  			expectedTranslation: linuxerr.EINTR,
   158  		},
   159  		{
   160  			errIn: linuxerr.ERESTART_RESTARTBLOCK,
   161  		},
   162  		{
   163  			errIn: errors.New("some new error"),
   164  		},
   165  	}
   166  	for _, tt := range testTable {
   167  		t.Run(fmt.Sprintf("err: %v %T", tt.errIn, tt.errIn), func(t *testing.T) {
   168  			err, ok := linuxerr.TranslateError(tt.errIn)
   169  			if (!tt.expectedBool && err != nil) || (tt.expectedBool != ok) {
   170  				t.Fatalf("%v => %v %v expected %v err: nil", tt.errIn, err, ok, tt.expectedBool)
   171  			} else if err != tt.expectedTranslation {
   172  				t.Fatalf("%v => %v expected %v", tt.errIn, err, tt.expectedTranslation)
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func TestSyscallErrnoToErrors(t *testing.T) {
   179  	for _, tc := range []struct {
   180  		errno syscall.Errno
   181  		err   error
   182  	}{
   183  		{errno: syscall.EACCES, err: linuxerr.EACCES},
   184  		{errno: syscall.EAGAIN, err: linuxerr.EAGAIN},
   185  		{errno: syscall.EBADF, err: linuxerr.EBADF},
   186  		{errno: syscall.EBUSY, err: linuxerr.EBUSY},
   187  		{errno: syscall.EDOM, err: linuxerr.EDOM},
   188  		{errno: syscall.EEXIST, err: linuxerr.EEXIST},
   189  		{errno: syscall.EFAULT, err: linuxerr.EFAULT},
   190  		{errno: syscall.EFBIG, err: linuxerr.EFBIG},
   191  		{errno: syscall.EINTR, err: linuxerr.EINTR},
   192  		{errno: syscall.EINVAL, err: linuxerr.EINVAL},
   193  		{errno: syscall.EIO, err: linuxerr.EIO},
   194  		{errno: syscall.ENOTDIR, err: linuxerr.ENOTDIR},
   195  		{errno: syscall.ENOTTY, err: linuxerr.ENOTTY},
   196  		{errno: syscall.EPERM, err: linuxerr.EPERM},
   197  		{errno: syscall.EPIPE, err: linuxerr.EPIPE},
   198  		{errno: syscall.ESPIPE, err: linuxerr.ESPIPE},
   199  		{errno: syscall.EWOULDBLOCK, err: linuxerr.EAGAIN},
   200  	} {
   201  		t.Run(tc.errno.Error(), func(t *testing.T) {
   202  			e := linuxerr.ErrorFromUnix(unix.Errno(tc.errno))
   203  			if e != tc.err {
   204  				t.Fatalf("Mismatch errors: want: %+v %T got: %+v %T", tc.err, tc.err, e, e)
   205  			}
   206  		})
   207  	}
   208  }
   209  
   210  // TestEqualsMethod tests that the Equals method correctly compares syerror,
   211  // unix.Errno and linuxerr.
   212  // TODO (b/34162363): Remove this.
   213  func TestEqualsMethod(t *testing.T) {
   214  	noError := linuxerr.ErrorFromUnix(unix.Errno(0))
   215  	for _, tc := range []struct {
   216  		name     string
   217  		linuxErr []*gErrors.Error
   218  		err      []error
   219  		equal    bool
   220  	}{
   221  		{
   222  			name:     "compare nil",
   223  			linuxErr: []*gErrors.Error{nil},
   224  			err:      []error{nil, noError, unix.Errno(0)},
   225  			equal:    true,
   226  		},
   227  		{
   228  			name:     "linuxerr nil error not",
   229  			linuxErr: []*gErrors.Error{nil},
   230  			err:      []error{unix.Errno(1), linuxerr.EPERM, linuxerr.EACCES},
   231  			equal:    false,
   232  		},
   233  		{
   234  			name:     "linuxerr not nil error nil",
   235  			linuxErr: []*gErrors.Error{linuxerr.ENOENT},
   236  			err:      []error{nil, unix.Errno(0)},
   237  			equal:    false,
   238  		},
   239  		{
   240  			name:     "equal errors",
   241  			linuxErr: []*gErrors.Error{linuxerr.ESRCH},
   242  			err:      []error{linuxerr.ESRCH, linuxerr.ESRCH, unix.Errno(linuxerr.ESRCH.Errno())},
   243  			equal:    true,
   244  		},
   245  		{
   246  			name:     "unequal errors",
   247  			linuxErr: []*gErrors.Error{linuxerr.ENOENT},
   248  			err:      []error{linuxerr.ESRCH, linuxerr.ESRCH, unix.Errno(linuxerr.ESRCH.Errno())},
   249  			equal:    false,
   250  		},
   251  		{
   252  			name:     "other error",
   253  			linuxErr: []*gErrors.Error{nil, linuxerr.E2BIG, linuxerr.EINVAL},
   254  			err:      []error{fs.ErrInvalid, io.EOF},
   255  			equal:    false,
   256  		},
   257  	} {
   258  		t.Run(tc.name, func(t *testing.T) {
   259  			for _, le := range tc.linuxErr {
   260  				for _, e := range tc.err {
   261  					if linuxerr.Equals(le, e) != tc.equal {
   262  						t.Fatalf("Expected %t from Equals method for linuxerr: %s %T and error: %s %T", tc.equal, le, le, e, e)
   263  					}
   264  				}
   265  			}
   266  		})
   267  	}
   268  }