| /** |
| # Copyright 2024 NVIDIA CORPORATION |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| **/ |
| |
| package nvsandboxutils |
| |
| import ( |
| "errors" |
| "fmt" |
| "testing" |
| |
| "github.com/stretchr/testify/require" |
| ) |
| |
| func newTestLibrary(dl dynamicLibrary) *library { |
| return &library{dl: dl} |
| } |
| |
| func TestLookupFromDefault(t *testing.T) { |
| errClose := errors.New("close error") |
| errOpen := errors.New("open error") |
| errLookup := errors.New("lookup error") |
| |
| testCases := []struct { |
| description string |
| dl dynamicLibrary |
| skipLoadLibrary bool |
| expectedLoadError error |
| expectedLookupErrror error |
| expectedCloseError error |
| }{ |
| { |
| description: "library not loaded yields error", |
| dl: &dynamicLibraryMock{}, |
| skipLoadLibrary: true, |
| expectedLookupErrror: errLibraryNotLoaded, |
| }, |
| { |
| description: "open error is returned", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return errOpen |
| }, |
| }, |
| |
| expectedLoadError: errOpen, |
| expectedLookupErrror: errLibraryNotLoaded, |
| }, |
| { |
| description: "lookup error is returned", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| LookupFunc: func(s string) error { |
| return fmt.Errorf("%w: %s", errLookup, s) |
| }, |
| CloseFunc: func() error { |
| return nil |
| }, |
| }, |
| |
| expectedLookupErrror: errLookup, |
| }, |
| { |
| description: "lookup succeeds", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| LookupFunc: func(s string) error { |
| return nil |
| }, |
| CloseFunc: func() error { |
| return nil |
| }, |
| }, |
| }, |
| { |
| description: "lookup succeeds", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| LookupFunc: func(s string) error { |
| return nil |
| }, |
| CloseFunc: func() error { |
| return nil |
| }, |
| }, |
| }, |
| { |
| description: "close error is returned", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| LookupFunc: func(s string) error { |
| return nil |
| }, |
| CloseFunc: func() error { |
| return errClose |
| }, |
| }, |
| expectedCloseError: errClose, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.description, func(t *testing.T) { |
| l := newTestLibrary(tc.dl) |
| if !tc.skipLoadLibrary { |
| require.ErrorIs(t, l.load(), tc.expectedLoadError) |
| } |
| require.ErrorIs(t, l.LookupSymbol("symbol"), tc.expectedLookupErrror) |
| require.ErrorIs(t, l.close(), tc.expectedCloseError) |
| if tc.expectedCloseError == nil { |
| require.Equal(t, 0, int(l.refcount)) |
| } else { |
| require.Equal(t, 1, int(l.refcount)) |
| } |
| }) |
| } |
| } |
| |
| func TestLoadAndCloseNesting(t *testing.T) { |
| dl := &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| CloseFunc: func() error { |
| return nil |
| }, |
| } |
| |
| l := newTestLibrary(dl) |
| |
| // When calling close before opening the library nothing happens. |
| require.Equal(t, 0, len(dl.calls.Close)) |
| require.Nil(t, l.close()) |
| require.Equal(t, 0, len(dl.calls.Close)) |
| |
| // When calling load twice, the library was only opened once |
| require.Equal(t, 0, len(dl.calls.Open)) |
| require.Nil(t, l.load()) |
| require.Equal(t, 1, len(dl.calls.Open)) |
| require.Nil(t, l.load()) |
| require.Equal(t, 1, len(dl.calls.Open)) |
| |
| // Only after calling close twice, was the library closed |
| require.Equal(t, 0, len(dl.calls.Close)) |
| require.Nil(t, l.close()) |
| require.Equal(t, 0, len(dl.calls.Close)) |
| require.Nil(t, l.close()) |
| require.Equal(t, 1, len(dl.calls.Close)) |
| |
| // Calling close again doesn't attempt to close the library again |
| require.Nil(t, l.close()) |
| require.Equal(t, 1, len(dl.calls.Close)) |
| } |
| |
| func TestLoadAndCloseWithErrors(t *testing.T) { |
| testCases := []struct { |
| description string |
| dl dynamicLibrary |
| expectedLoadRefcount refcount |
| expectedCloseRefcount refcount |
| }{ |
| { |
| description: "regular flow", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| CloseFunc: func() error { |
| return nil |
| }, |
| }, |
| expectedLoadRefcount: 1, |
| expectedCloseRefcount: 0, |
| }, |
| { |
| description: "open error", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return errors.New("") |
| }, |
| CloseFunc: func() error { |
| return nil |
| }, |
| }, |
| expectedLoadRefcount: 0, |
| expectedCloseRefcount: 0, |
| }, |
| { |
| description: "close error", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return nil |
| }, |
| CloseFunc: func() error { |
| return errors.New("") |
| }, |
| }, |
| expectedLoadRefcount: 1, |
| expectedCloseRefcount: 1, |
| }, |
| { |
| description: "open and close error", |
| dl: &dynamicLibraryMock{ |
| OpenFunc: func() error { |
| return errors.New("") |
| }, |
| CloseFunc: func() error { |
| return errors.New("") |
| }, |
| }, |
| expectedLoadRefcount: 0, |
| expectedCloseRefcount: 0, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.description, func(t *testing.T) { |
| l := newTestLibrary(tc.dl) |
| _ = l.load() |
| require.Equal(t, tc.expectedLoadRefcount, l.refcount) |
| _ = l.close() |
| require.Equal(t, tc.expectedCloseRefcount, l.refcount) |
| }) |
| } |
| } |