Skip to content

Commit

Permalink
Rework assertion to follow etcd approach. Enable assertions in tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
ncabatoff committed Oct 25, 2023
1 parent 104aeb4 commit 618b536
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 25 deletions.
16 changes: 8 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ lint:
.PHONY: test
test:
@echo "hashmap freelist test"
TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} -timeout ${TESTFLAGS_TIMEOUT}
TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./internal/...
TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./cmd/bbolt
BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} -timeout ${TESTFLAGS_TIMEOUT}
BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./internal/...
BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./cmd/bbolt

@echo "array freelist test"
TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} -timeout ${TESTFLAGS_TIMEOUT}
TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./internal/...
TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./cmd/bbolt
BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} -timeout ${TESTFLAGS_TIMEOUT}
BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./internal/...
BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./cmd/bbolt

.PHONY: coverage
coverage:
Expand All @@ -76,8 +76,8 @@ install-gofail:
.PHONY: test-failpoint
test-failpoint:
@echo "[failpoint] hashmap freelist test"
TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} -timeout 30m ./tests/failpoint
BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} -timeout 30m ./tests/failpoint

@echo "[failpoint] array freelist test"
TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} -timeout 30m ./tests/failpoint
BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} -timeout 30m ./tests/failpoint

9 changes: 0 additions & 9 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import (
"go.etcd.io/bbolt/internal/common"
)

// When enabled, the database will perform assert function to check the slow-path code
var assertVerify = os.Getenv("BBOLT_VERIFY") == "true"

// The time elapsed between consecutive file locking attempts.
const flockRetryTimeout = 50 * time.Millisecond

Expand Down Expand Up @@ -1312,9 +1309,3 @@ type Info struct {
Data uintptr
PageSize int
}

func _assertVerify(conditionFunc func() bool, msg string, v ...interface{}) {
if assertVerify && !conditionFunc() {
panic(fmt.Sprintf("assertion failed: "+msg, v...))
}
}
6 changes: 5 additions & 1 deletion freelist_hmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import (

// hashmapFreeCount returns count of free pages(hashmap version)
func (f *freelist) hashmapFreeCount() int {
_assertVerify(func() bool { return int(f.freePagesCount) == f.hashmapFreeCountSlow() }, "freePagesCount is out of sync with free pages map")
common.Verify(func() {
slow := f.hashmapFreeCountSlow()
common.Assert(int(f.freePagesCount) == slow,
"freePagesCount (%d) is out of sync with free pages map (%d)", f.freePagesCount, slow)
})
return int(f.freePagesCount)
}

Expand Down
5 changes: 5 additions & 0 deletions freelist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,11 @@ func newTestFreelist() *freelist {
}

func Test_freelist_hashmapGetFreePageIDs(t *testing.T) {
// Because we pre-create the freelist here, we'd fail the assertion that
// the computed count matches the freePagesCount.
reenable := common.DisableVerifications()
defer reenable()

f := newTestFreelist()
if f.freelistType == FreelistArrayType {
t.Skip()
Expand Down
7 changes: 0 additions & 7 deletions internal/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ import (
"unsafe"
)

// Assert will panic with a given formatted message if the given condition is false.
func Assert(condition bool, msg string, v ...interface{}) {
if !condition {
panic(fmt.Sprintf("assertion failed: "+msg, v...))
}
}

func LoadBucket(buf []byte) *InBucket {
return (*InBucket)(unsafe.Pointer(&buf[0]))
}
Expand Down
80 changes: 80 additions & 0 deletions internal/common/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2022 The etcd Authors
//
// 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 common

import (
"fmt"
"os"
"strings"
)

const ENV_VERIFY = "BBOLT_VERIFY"

type VerificationType string

const (
ENV_VERIFY_VALUE_ALL VerificationType = "all"
ENV_VERIFY_VALUE_ASSERT VerificationType = "assert"
)

func getEnvVerify() string {
return strings.ToLower(os.Getenv(ENV_VERIFY))
}

func IsVerificationEnabled(verification VerificationType) bool {
env := getEnvVerify()
return env == string(ENV_VERIFY_VALUE_ALL) || env == strings.ToLower(string(verification))
}

// EnableVerifications sets `ENV_VERIFY` and returns a function that
// can be used to bring the original settings.
func EnableVerifications(verification VerificationType) func() {
previousEnv := getEnvVerify()
os.Setenv(ENV_VERIFY, string(verification))
return func() {
os.Setenv(ENV_VERIFY, previousEnv)
}
}

// EnableAllVerifications enables verification and returns a function
// that can be used to bring the original settings.
func EnableAllVerifications() func() {
return EnableVerifications(ENV_VERIFY_VALUE_ALL)
}

// DisableVerifications unsets `ENV_VERIFY` and returns a function that
// can be used to bring the original settings.
func DisableVerifications() func() {
previousEnv := getEnvVerify()
os.Unsetenv(ENV_VERIFY)
return func() {
os.Setenv(ENV_VERIFY, previousEnv)
}
}

// Verify performs verification if the assertions are enabled.
// In the default setup running in tests and skipped in the production code.
func Verify(f func()) {
if IsVerificationEnabled(ENV_VERIFY_VALUE_ASSERT) {
f()
}
}

// Assert will panic with a given formatted message if the given condition is false.
func Assert(condition bool, msg string, v ...any) {
if !condition {
panic(fmt.Sprintf("assertion failed: "+msg, v...))
}
}

0 comments on commit 618b536

Please sign in to comment.