summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/github.com/mattn/go-sqlite3@v1.14.16/backup_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'dependencies/pkg/mod/github.com/mattn/go-sqlite3@v1.14.16/backup_test.go')
-rw-r--r--dependencies/pkg/mod/github.com/mattn/go-sqlite3@v1.14.16/backup_test.go294
1 files changed, 294 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/github.com/mattn/go-sqlite3@v1.14.16/backup_test.go b/dependencies/pkg/mod/github.com/mattn/go-sqlite3@v1.14.16/backup_test.go
new file mode 100644
index 0000000..6d857de
--- /dev/null
+++ b/dependencies/pkg/mod/github.com/mattn/go-sqlite3@v1.14.16/backup_test.go
@@ -0,0 +1,294 @@
+// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+
+package sqlite3
+
+import (
+ "database/sql"
+ "fmt"
+ "os"
+ "testing"
+ "time"
+)
+
+// The number of rows of test data to create in the source database.
+// Can be used to control how many pages are available to be backed up.
+const testRowCount = 100
+
+// The maximum number of seconds after which the page-by-page backup is considered to have taken too long.
+const usePagePerStepsTimeoutSeconds = 30
+
+// Test the backup functionality.
+func testBackup(t *testing.T, testRowCount int, usePerPageSteps bool) {
+ // This function will be called multiple times.
+ // It uses sql.Register(), which requires the name parameter value to be unique.
+ // There does not currently appear to be a way to unregister a registered driver, however.
+ // So generate a database driver name that will likely be unique.
+ var driverName = fmt.Sprintf("sqlite3_testBackup_%v_%v_%v", testRowCount, usePerPageSteps, time.Now().UnixNano())
+
+ // The driver's connection will be needed in order to perform the backup.
+ driverConns := []*SQLiteConn{}
+ sql.Register(driverName, &SQLiteDriver{
+ ConnectHook: func(conn *SQLiteConn) error {
+ driverConns = append(driverConns, conn)
+ return nil
+ },
+ })
+
+ // Connect to the source database.
+ srcTempFilename := TempFilename(t)
+ defer os.Remove(srcTempFilename)
+ srcDb, err := sql.Open(driverName, srcTempFilename)
+ if err != nil {
+ t.Fatal("Failed to open the source database:", err)
+ }
+ defer srcDb.Close()
+ err = srcDb.Ping()
+ if err != nil {
+ t.Fatal("Failed to connect to the source database:", err)
+ }
+
+ // Connect to the destination database.
+ destTempFilename := TempFilename(t)
+ defer os.Remove(destTempFilename)
+ destDb, err := sql.Open(driverName, destTempFilename)
+ if err != nil {
+ t.Fatal("Failed to open the destination database:", err)
+ }
+ defer destDb.Close()
+ err = destDb.Ping()
+ if err != nil {
+ t.Fatal("Failed to connect to the destination database:", err)
+ }
+
+ // Check the driver connections.
+ if len(driverConns) != 2 {
+ t.Fatalf("Expected 2 driver connections, but found %v.", len(driverConns))
+ }
+ srcDbDriverConn := driverConns[0]
+ if srcDbDriverConn == nil {
+ t.Fatal("The source database driver connection is nil.")
+ }
+ destDbDriverConn := driverConns[1]
+ if destDbDriverConn == nil {
+ t.Fatal("The destination database driver connection is nil.")
+ }
+
+ // Generate some test data for the given ID.
+ var generateTestData = func(id int) string {
+ return fmt.Sprintf("test-%v", id)
+ }
+
+ // Populate the source database with a test table containing some test data.
+ tx, err := srcDb.Begin()
+ if err != nil {
+ t.Fatal("Failed to begin a transaction when populating the source database:", err)
+ }
+ _, err = srcDb.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
+ if err != nil {
+ tx.Rollback()
+ t.Fatal("Failed to create the source database \"test\" table:", err)
+ }
+ for id := 0; id < testRowCount; id++ {
+ _, err = srcDb.Exec("INSERT INTO test (id, value) VALUES (?, ?)", id, generateTestData(id))
+ if err != nil {
+ tx.Rollback()
+ t.Fatal("Failed to insert a row into the source database \"test\" table:", err)
+ }
+ }
+ err = tx.Commit()
+ if err != nil {
+ t.Fatal("Failed to populate the source database:", err)
+ }
+
+ // Confirm that the destination database is initially empty.
+ var destTableCount int
+ err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount)
+ if err != nil {
+ t.Fatal("Failed to check the destination table count:", err)
+ }
+ if destTableCount != 0 {
+ t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount)
+ }
+
+ // Prepare to perform the backup.
+ backup, err := destDbDriverConn.Backup("main", srcDbDriverConn, "main")
+ if err != nil {
+ t.Fatal("Failed to initialize the backup:", err)
+ }
+
+ // Allow the initial page count and remaining values to be retrieved.
+ // According to <https://www.sqlite.org/c3ref/backup_finish.html>, the page count and remaining values are "... only updated by sqlite3_backup_step()."
+ isDone, err := backup.Step(0)
+ if err != nil {
+ t.Fatal("Unable to perform an initial 0-page backup step:", err)
+ }
+ if isDone {
+ t.Fatal("Backup is unexpectedly done.")
+ }
+
+ // Check that the page count and remaining values are reasonable.
+ initialPageCount := backup.PageCount()
+ if initialPageCount <= 0 {
+ t.Fatalf("Unexpected initial page count value: %v", initialPageCount)
+ }
+ initialRemaining := backup.Remaining()
+ if initialRemaining <= 0 {
+ t.Fatalf("Unexpected initial remaining value: %v", initialRemaining)
+ }
+ if initialRemaining != initialPageCount {
+ t.Fatalf("Initial remaining value differs from the initial page count value; remaining: %v; page count: %v", initialRemaining, initialPageCount)
+ }
+
+ // Perform the backup.
+ if usePerPageSteps {
+ var startTime = time.Now().Unix()
+
+ // Test backing-up using a page-by-page approach.
+ var latestRemaining = initialRemaining
+ for {
+ // Perform the backup step.
+ isDone, err = backup.Step(1)
+ if err != nil {
+ t.Fatal("Failed to perform a backup step:", err)
+ }
+
+ // The page count should remain unchanged from its initial value.
+ currentPageCount := backup.PageCount()
+ if currentPageCount != initialPageCount {
+ t.Fatalf("Current page count differs from the initial page count; initial page count: %v; current page count: %v", initialPageCount, currentPageCount)
+ }
+
+ // There should now be one less page remaining.
+ currentRemaining := backup.Remaining()
+ expectedRemaining := latestRemaining - 1
+ if currentRemaining != expectedRemaining {
+ t.Fatalf("Unexpected remaining value; expected remaining value: %v; actual remaining value: %v", expectedRemaining, currentRemaining)
+ }
+ latestRemaining = currentRemaining
+
+ if isDone {
+ break
+ }
+
+ // Limit the runtime of the backup attempt.
+ if (time.Now().Unix() - startTime) > usePagePerStepsTimeoutSeconds {
+ t.Fatal("Backup is taking longer than expected.")
+ }
+ }
+ } else {
+ // Test the copying of all remaining pages.
+ isDone, err = backup.Step(-1)
+ if err != nil {
+ t.Fatal("Failed to perform a backup step:", err)
+ }
+ if !isDone {
+ t.Fatal("Backup is unexpectedly not done.")
+ }
+ }
+
+ // Check that the page count and remaining values are reasonable.
+ finalPageCount := backup.PageCount()
+ if finalPageCount != initialPageCount {
+ t.Fatalf("Final page count differs from the initial page count; initial page count: %v; final page count: %v", initialPageCount, finalPageCount)
+ }
+ finalRemaining := backup.Remaining()
+ if finalRemaining != 0 {
+ t.Fatalf("Unexpected remaining value: %v", finalRemaining)
+ }
+
+ // Finish the backup.
+ err = backup.Finish()
+ if err != nil {
+ t.Fatal("Failed to finish backup:", err)
+ }
+
+ // Confirm that the "test" table now exists in the destination database.
+ var doesTestTableExist bool
+ err = destDb.QueryRow("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'test' LIMIT 1) AS test_table_exists").Scan(&doesTestTableExist)
+ if err != nil {
+ t.Fatal("Failed to check if the \"test\" table exists in the destination database:", err)
+ }
+ if !doesTestTableExist {
+ t.Fatal("The \"test\" table could not be found in the destination database.")
+ }
+
+ // Confirm that the number of rows in the destination database's "test" table matches that of the source table.
+ var actualTestTableRowCount int
+ err = destDb.QueryRow("SELECT COUNT(*) FROM test").Scan(&actualTestTableRowCount)
+ if err != nil {
+ t.Fatal("Failed to determine the rowcount of the \"test\" table in the destination database:", err)
+ }
+ if testRowCount != actualTestTableRowCount {
+ t.Fatalf("Unexpected destination \"test\" table row count; expected: %v; found: %v", testRowCount, actualTestTableRowCount)
+ }
+
+ // Check each of the rows in the destination database.
+ for id := 0; id < testRowCount; id++ {
+ var checkedValue string
+ err = destDb.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&checkedValue)
+ if err != nil {
+ t.Fatal("Failed to query the \"test\" table in the destination database:", err)
+ }
+
+ var expectedValue = generateTestData(id)
+ if checkedValue != expectedValue {
+ t.Fatalf("Unexpected value in the \"test\" table in the destination database; expected value: %v; actual value: %v", expectedValue, checkedValue)
+ }
+ }
+}
+
+func TestBackupStepByStep(t *testing.T) {
+ testBackup(t, testRowCount, true)
+}
+
+func TestBackupAllRemainingPages(t *testing.T) {
+ testBackup(t, testRowCount, false)
+}
+
+// Test the error reporting when preparing to perform a backup.
+func TestBackupError(t *testing.T) {
+ const driverName = "sqlite3_TestBackupError"
+
+ // The driver's connection will be needed in order to perform the backup.
+ var dbDriverConn *SQLiteConn
+ sql.Register(driverName, &SQLiteDriver{
+ ConnectHook: func(conn *SQLiteConn) error {
+ dbDriverConn = conn
+ return nil
+ },
+ })
+
+ // Connect to the database.
+ dbTempFilename := TempFilename(t)
+ defer os.Remove(dbTempFilename)
+ db, err := sql.Open(driverName, dbTempFilename)
+ if err != nil {
+ t.Fatal("Failed to open the database:", err)
+ }
+ defer db.Close()
+ db.Ping()
+
+ // Need the driver connection in order to perform the backup.
+ if dbDriverConn == nil {
+ t.Fatal("Failed to get the driver connection.")
+ }
+
+ // Prepare to perform the backup.
+ // Intentionally using the same connection for both the source and destination databases, to trigger an error result.
+ backup, err := dbDriverConn.Backup("main", dbDriverConn, "main")
+ if err == nil {
+ t.Fatal("Failed to get the expected error result.")
+ }
+ const expectedError = "source and destination must be distinct"
+ if err.Error() != expectedError {
+ t.Fatalf("Unexpected error message; expected value: \"%v\"; actual value: \"%v\"", expectedError, err.Error())
+ }
+ if backup != nil {
+ t.Fatal("Failed to get the expected nil backup result.")
+ }
+}