github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/db/db_test.go (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package db
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"os"
    10  	"reflect"
    11  	"testing"
    12  
    13  	"github.com/google/syzkaller/pkg/osutil"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestBasic(t *testing.T) {
    18  	fn := tempFile(t)
    19  	defer os.Remove(fn)
    20  	db, err := Open(fn, false)
    21  	if err != nil {
    22  		t.Fatalf("failed to open db: %v", err)
    23  	}
    24  	if len(db.Records) != 0 {
    25  		t.Fatalf("empty db contains records")
    26  	}
    27  	db.Save("", nil, 0)
    28  	db.Save("1", []byte("ab"), 1)
    29  	db.Save("23", []byte("abcd"), 2)
    30  
    31  	want := map[string]Record{
    32  		"":   {Val: nil, Seq: 0},
    33  		"1":  {Val: []byte("ab"), Seq: 1},
    34  		"23": {Val: []byte("abcd"), Seq: 2},
    35  	}
    36  	if !reflect.DeepEqual(db.Records, want) {
    37  		t.Fatalf("bad db after save: %v, want: %v", db.Records, want)
    38  	}
    39  	if err := db.Flush(); err != nil {
    40  		t.Fatalf("failed to flush db: %v", err)
    41  	}
    42  	if !reflect.DeepEqual(db.Records, want) {
    43  		t.Fatalf("bad db after flush: %v, want: %v", db.Records, want)
    44  	}
    45  	db, err = Open(fn, false)
    46  	if err != nil {
    47  		t.Fatalf("failed to open db: %v", err)
    48  	}
    49  	if !reflect.DeepEqual(db.Records, want) {
    50  		t.Fatalf("bad db after reopen: %v, want: %v", db.Records, want)
    51  	}
    52  }
    53  
    54  func TestModify(t *testing.T) {
    55  	fn := tempFile(t)
    56  	defer os.Remove(fn)
    57  	db, err := Open(fn, false)
    58  	if err != nil {
    59  		t.Fatalf("failed to open db: %v", err)
    60  	}
    61  	db.Save("1", []byte("ab"), 0)
    62  	db.Save("23", nil, 1)
    63  	db.Save("456", []byte("abcd"), 1)
    64  	db.Save("7890", []byte("a"), 0)
    65  	db.Delete("23")
    66  	db.Save("1", nil, 5)
    67  	db.Save("456", []byte("ef"), 6)
    68  	db.Delete("7890")
    69  	db.Save("456", []byte("efg"), 0)
    70  	db.Save("7890", []byte("bc"), 0)
    71  
    72  	want := map[string]Record{
    73  		"1":    {Val: nil, Seq: 5},
    74  		"456":  {Val: []byte("efg"), Seq: 0},
    75  		"7890": {Val: []byte("bc"), Seq: 0},
    76  	}
    77  	if !reflect.DeepEqual(db.Records, want) {
    78  		t.Fatalf("bad db after modification: %v, want: %v", db.Records, want)
    79  	}
    80  	if err := db.Flush(); err != nil {
    81  		t.Fatalf("failed to flush db: %v", err)
    82  	}
    83  	if !reflect.DeepEqual(db.Records, want) {
    84  		t.Fatalf("bad db after flush: %v, want: %v", db.Records, want)
    85  	}
    86  	db, err = Open(fn, false)
    87  	if err != nil {
    88  		t.Fatalf("failed to open db: %v", err)
    89  	}
    90  	if !reflect.DeepEqual(db.Records, want) {
    91  		t.Fatalf("bad db after reopen: %v, want: %v", db.Records, want)
    92  	}
    93  }
    94  
    95  func TestLarge(t *testing.T) {
    96  	fn := tempFile(t)
    97  	defer os.Remove(fn)
    98  	db, err := Open(fn, false)
    99  	if err != nil {
   100  		t.Fatalf("failed to open db: %v", err)
   101  	}
   102  	const nrec = 1000
   103  	val := make([]byte, 1000)
   104  	for i := range val {
   105  		val[i] = byte(rand.Intn(256))
   106  	}
   107  	for i := 0; i < nrec; i++ {
   108  		db.Save(fmt.Sprintf("%v", i), val, 0)
   109  	}
   110  	if err := db.Flush(); err != nil {
   111  		t.Fatalf("failed to flush db: %v", err)
   112  	}
   113  	db, err = Open(fn, false)
   114  	if err != nil {
   115  		t.Fatalf("failed to open db: %v", err)
   116  	}
   117  	if len(db.Records) != nrec {
   118  		t.Fatalf("wrong record count: %v, want %v", len(db.Records), nrec)
   119  	}
   120  }
   121  
   122  func TestDiscardData(t *testing.T) {
   123  	fn := tempFile(t)
   124  	defer os.Remove(fn)
   125  	db, err := Open(fn, false)
   126  	if err != nil {
   127  		t.Fatalf("failed to open db: %v", err)
   128  	}
   129  	db.DiscardData()
   130  
   131  	db.Save("1", []byte("11"), 1)
   132  	db.Save("2", []byte("22"), 2)
   133  	db.Save("3", []byte("33"), 3)
   134  	db.Delete("2")
   135  	if err := db.Flush(); err != nil {
   136  		t.Fatalf("failed to flush db: %v", err)
   137  	}
   138  	assert.Nil(t, db.Records["1"].Val)
   139  	assert.Nil(t, db.Records["2"].Val)
   140  	assert.Nil(t, db.Records["3"].Val)
   141  
   142  	db, err = Open(fn, false)
   143  	if err != nil {
   144  		t.Fatalf("failed to open db: %v", err)
   145  	}
   146  	want := map[string]Record{
   147  		"1": {Val: []byte("11"), Seq: 1},
   148  		"3": {Val: []byte("33"), Seq: 3},
   149  	}
   150  	if !reflect.DeepEqual(db.Records, want) {
   151  		t.Fatalf("bad db after save: %v, want: %v", db.Records, want)
   152  	}
   153  	db.Save("3", []byte("333"), 33)
   154  	db.Save("4", []byte("44"), 4)
   155  	db.Delete("1")
   156  	db.DiscardData()
   157  	assert.Nil(t, db.Records["3"].Val)
   158  	assert.Nil(t, db.Records["4"].Val)
   159  	// Force compaction.
   160  	for i := 0; i < 200; i++ {
   161  		db.Save("5", []byte("55"), 5)
   162  	}
   163  	if err := db.Flush(); err != nil {
   164  		t.Fatalf("failed to flush db: %v", err)
   165  	}
   166  	db, err = Open(fn, false)
   167  	if err != nil {
   168  		t.Fatalf("failed to open db: %v", err)
   169  	}
   170  	want = map[string]Record{
   171  		"3": {Val: []byte("333"), Seq: 33},
   172  		"4": {Val: []byte("44"), Seq: 4},
   173  		"5": {Val: []byte("55"), Seq: 5},
   174  	}
   175  	if !reflect.DeepEqual(db.Records, want) {
   176  		t.Fatalf("bad db after save: %v, want: %v", db.Records, want)
   177  	}
   178  }
   179  
   180  func TestOpenInvalid(t *testing.T) {
   181  	f, err := os.CreateTemp("", "syz-db-test")
   182  	if err != nil {
   183  		t.Error(err)
   184  	}
   185  
   186  	defer f.Close()
   187  	defer os.Remove(f.Name())
   188  	if _, err := f.Write([]byte(`some invalid data`)); err != nil {
   189  		t.Error(err)
   190  	}
   191  	if db, err := Open(f.Name(), true); err == nil {
   192  		t.Fatal("opened invalid db")
   193  	} else if db == nil {
   194  		t.Fatal("db is nil")
   195  	}
   196  }
   197  
   198  func TestOpenInaccessible(t *testing.T) {
   199  	if os.Getuid() == 0 {
   200  		t.Skip("opening inaccessible file won't fail under root")
   201  	}
   202  	f, err := os.CreateTemp("", "syz-db-test")
   203  	if err != nil {
   204  		t.Error(err)
   205  	}
   206  	f.Close()
   207  	os.Chmod(f.Name(), 0)
   208  	defer os.Chmod(f.Name(), 0777)
   209  	defer os.Remove(f.Name())
   210  	if db, err := Open(f.Name(), false); err == nil {
   211  		t.Fatal("opened inaccessible db")
   212  	} else if db != nil {
   213  		t.Fatal("db is not nil")
   214  	}
   215  }
   216  
   217  func TestOpenCorrupted(t *testing.T) {
   218  	fn := tempFile(t)
   219  	defer os.Remove(fn)
   220  	db, err := Open(fn, false)
   221  	if err != nil {
   222  		t.Fatalf("failed to open db: %v", err)
   223  	}
   224  	// Write 1000 records, then wipe half of the file and test that we
   225  	// (1) get an error, (2) still get 450-550 records.
   226  	for i := 0; i < 1000; i++ {
   227  		db.Save(fmt.Sprintf("%v", i), []byte{byte(i)}, 0)
   228  	}
   229  	if err := db.Flush(); err != nil {
   230  		t.Fatalf("failed to flush db: %v", err)
   231  	}
   232  	data, err := os.ReadFile(fn)
   233  	if err != nil {
   234  		t.Fatalf("failed to read db: %v", err)
   235  	}
   236  	for i := len(data) / 2; i < len(data); i++ {
   237  		data[i] = 0
   238  	}
   239  	if err := osutil.WriteFile(fn, data); err != nil {
   240  		t.Fatalf("failed to write db: %v", err)
   241  	}
   242  	db, err = Open(fn, true)
   243  	if err == nil {
   244  		t.Fatalf("no error for corrutped db")
   245  	}
   246  	t.Logf("records %v, error: %v", len(db.Records), err)
   247  	if len(db.Records) < 450 || len(db.Records) > 550 {
   248  		t.Fatalf("wrong record count: %v", len(db.Records))
   249  	}
   250  }
   251  
   252  func tempFile(t *testing.T) string {
   253  	fn, err := osutil.TempFile("syzkaller.test.db")
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  	return fn
   258  }