github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/lock/lock_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package lock
    19  
    20  import (
    21  	"os"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  // Test lock fails.
    27  func TestLockFail(t *testing.T) {
    28  	f, err := os.CreateTemp("", "lock")
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  	f.Close()
    33  	defer func() {
    34  		err = os.Remove(f.Name())
    35  		if err != nil {
    36  			t.Fatal(err)
    37  		}
    38  	}()
    39  
    40  	_, err = LockedOpenFile(f.Name(), os.O_APPEND, 0o600)
    41  	if err == nil {
    42  		t.Fatal("Should fail here")
    43  	}
    44  }
    45  
    46  // Tests lock directory fail.
    47  func TestLockDirFail(t *testing.T) {
    48  	d := t.TempDir()
    49  
    50  	_, err := LockedOpenFile(d, os.O_APPEND, 0o600)
    51  	if err == nil {
    52  		t.Fatal("Should fail here")
    53  	}
    54  }
    55  
    56  // Tests rwlock methods.
    57  func TestRWLockedFile(t *testing.T) {
    58  	f, err := os.CreateTemp("", "lock")
    59  	if err != nil {
    60  		t.Fatal(err)
    61  	}
    62  	f.Close()
    63  	defer func() {
    64  		err = os.Remove(f.Name())
    65  		if err != nil {
    66  			t.Fatal(err)
    67  		}
    68  	}()
    69  
    70  	rlk, err := RLockedOpenFile(f.Name())
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	isClosed := rlk.IsClosed()
    75  	if isClosed {
    76  		t.Fatal("File ref count shouldn't be zero")
    77  	}
    78  
    79  	// Increase reference count to 2.
    80  	rlk.IncLockRef()
    81  
    82  	isClosed = rlk.IsClosed()
    83  	if isClosed {
    84  		t.Fatal("File ref count shouldn't be zero")
    85  	}
    86  
    87  	// Decrease reference count by 1.
    88  	if err = rlk.Close(); err != nil {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	isClosed = rlk.IsClosed()
    93  	if isClosed {
    94  		t.Fatal("File ref count shouldn't be zero")
    95  	}
    96  
    97  	// Decrease reference count by 1.
    98  	if err = rlk.Close(); err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	// Now file should be closed.
   103  	isClosed = rlk.IsClosed()
   104  	if !isClosed {
   105  		t.Fatal("File ref count should be zero")
   106  	}
   107  
   108  	// Closing a file again should result in invalid argument.
   109  	if err = rlk.Close(); err != os.ErrInvalid {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	_, err = newRLockedFile(nil)
   114  	if err != os.ErrInvalid {
   115  		t.Fatal("Unexpected error", err)
   116  	}
   117  }
   118  
   119  // Tests lock and unlock semantics.
   120  func TestLockAndUnlock(t *testing.T) {
   121  	f, err := os.CreateTemp("", "lock")
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  	f.Close()
   126  	defer func() {
   127  		err = os.Remove(f.Name())
   128  		if err != nil {
   129  			t.Fatal(err)
   130  		}
   131  	}()
   132  
   133  	// lock the file
   134  	l, err := LockedOpenFile(f.Name(), os.O_WRONLY, 0o600)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	// unlock the file
   140  	if err = l.Close(); err != nil {
   141  		t.Fatal(err)
   142  	}
   143  
   144  	// try lock the unlocked file
   145  	dupl, err := LockedOpenFile(f.Name(), os.O_WRONLY|os.O_CREATE, 0o600)
   146  	if err != nil {
   147  		t.Errorf("err = %v, want %v", err, nil)
   148  	}
   149  
   150  	// blocking on locked file
   151  	locked := make(chan struct{}, 1)
   152  	go func() {
   153  		bl, blerr := LockedOpenFile(f.Name(), os.O_WRONLY, 0o600)
   154  		if blerr != nil {
   155  			t.Error(blerr)
   156  			return
   157  		}
   158  		locked <- struct{}{}
   159  		if blerr = bl.Close(); blerr != nil {
   160  			t.Error(blerr)
   161  			return
   162  		}
   163  	}()
   164  
   165  	select {
   166  	case <-locked:
   167  		t.Error("unexpected unblocking")
   168  	case <-time.After(100 * time.Millisecond):
   169  	}
   170  
   171  	// unlock
   172  	if err = dupl.Close(); err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	// the previously blocked routine should be unblocked
   177  	select {
   178  	case <-locked:
   179  	case <-time.After(1 * time.Second):
   180  		t.Error("unexpected blocking")
   181  	}
   182  }