2016-05-25 06:42:19 +03:00
package abool
import (
2021-04-28 17:48:48 +03:00
"encoding/json"
2016-05-25 06:42:19 +03:00
"sync"
"sync/atomic"
"testing"
)
2020-07-16 10:01:49 +03:00
func TestDefaultValue ( t * testing . T ) {
2020-06-30 05:10:38 +03:00
t . Parallel ( )
2020-07-16 10:01:49 +03:00
v := New ( )
if v . IsSet ( ) {
t . Fatal ( "Empty value of AtomicBool should be false" )
}
2020-06-30 05:10:38 +03:00
2020-07-16 10:01:49 +03:00
v = NewBool ( true )
2016-05-25 10:06:49 +03:00
if ! v . IsSet ( ) {
2020-07-16 10:01:49 +03:00
t . Fatal ( "NewValue(true) should be true" )
2016-05-25 10:06:49 +03:00
}
v = NewBool ( false )
if v . IsSet ( ) {
2020-07-16 10:01:49 +03:00
t . Fatal ( "NewValue(false) should be false" )
2016-05-25 10:06:49 +03:00
}
2020-07-16 10:01:49 +03:00
}
2016-05-25 10:06:49 +03:00
2020-07-16 10:01:49 +03:00
func TestIsNotSet ( t * testing . T ) {
t . Parallel ( )
v := New ( )
2016-05-25 06:42:19 +03:00
2020-07-16 09:46:58 +03:00
if v . IsSet ( ) == v . IsNotSet ( ) {
t . Fatal ( "AtomicBool.IsNotSet() should be the opposite of IsSet()" )
}
2020-07-16 10:01:49 +03:00
}
func TestSetUnSet ( t * testing . T ) {
t . Parallel ( )
v := New ( )
2020-07-16 09:46:58 +03:00
2016-05-25 06:42:19 +03:00
v . Set ( )
if ! v . IsSet ( ) {
t . Fatal ( "AtomicBool.Set() failed" )
}
v . UnSet ( )
if v . IsSet ( ) {
t . Fatal ( "AtomicBool.UnSet() failed" )
}
2020-07-16 10:01:49 +03:00
}
func TestSetTo ( t * testing . T ) {
t . Parallel ( )
v := New ( )
2016-05-25 06:42:19 +03:00
v . SetTo ( true )
if ! v . IsSet ( ) {
t . Fatal ( "AtomicBool.SetTo(true) failed" )
}
v . SetTo ( false )
if v . IsSet ( ) {
t . Fatal ( "AtomicBool.SetTo(false) failed" )
}
2016-06-02 06:47:41 +03:00
if set := v . SetToIf ( true , false ) ; set || v . IsSet ( ) {
t . Fatal ( "AtomicBool.SetTo(true, false) failed" )
}
if set := v . SetToIf ( false , true ) ; ! set || ! v . IsSet ( ) {
t . Fatal ( "AtomicBool.SetTo(false, true) failed" )
}
2020-07-16 10:01:49 +03:00
}
2018-09-07 17:17:26 +03:00
2021-09-02 01:50:36 +03:00
func TestToggle ( t * testing . T ) {
t . Parallel ( )
v := New ( )
_ = v . Toggle ( )
if ! v . IsSet ( ) {
t . Fatal ( "AtomicBool.Toggle() to true failed" )
}
prev := v . Toggle ( )
if v . IsSet ( ) == prev {
t . Fatal ( "AtomicBool.Toggle() to false failed" )
}
}
func TestToogleMultipleTimes ( t * testing . T ) {
t . Parallel ( )
v := New ( )
pre := ! v . IsSet ( )
for i := 0 ; i < 100 ; i ++ {
v . SetTo ( false )
for j := 0 ; j < i ; j ++ {
pre = v . Toggle ( )
}
expected := i % 2 != 0
if v . IsSet ( ) != expected {
t . Fatalf ( "AtomicBool.Toogle() doesn't work after %d calls, expected: %v, got %v" , i , expected , v . IsSet ( ) )
}
if pre == v . IsSet ( ) {
t . Fatalf ( "AtomicBool.Toogle() returned wrong value at the %dth calls, expected: %v, got %v" , i , ! v . IsSet ( ) , pre )
}
}
}
2016-05-25 09:57:10 +03:00
func TestRace ( t * testing . T ) {
repeat := 10000
var wg sync . WaitGroup
2021-09-05 07:25:08 +03:00
wg . Add ( repeat * 3 )
2016-05-25 09:57:10 +03:00
v := New ( )
2016-05-25 10:06:49 +03:00
// Writer
2016-05-25 09:57:10 +03:00
go func ( ) {
for i := 0 ; i < repeat ; i ++ {
v . Set ( )
wg . Done ( )
}
} ( )
2016-05-25 10:06:49 +03:00
// Reader
2016-05-25 09:57:10 +03:00
go func ( ) {
for i := 0 ; i < repeat ; i ++ {
v . IsSet ( )
wg . Done ( )
}
} ( )
2016-05-25 10:06:49 +03:00
// Writer
2016-05-25 09:57:10 +03:00
go func ( ) {
for i := 0 ; i < repeat ; i ++ {
v . UnSet ( )
wg . Done ( )
}
} ( )
wg . Wait ( )
}
2021-09-05 18:29:37 +03:00
func TestSetToIfAfterMultipleToggles ( t * testing . T ) {
v := New ( ) // false
v . Toggle ( ) // true
v . Toggle ( ) // false
v . Toggle ( ) // true
// As v is true, it should now be flipped to false
v . SetToIf ( true , false )
expected := false
if v . IsSet ( ) != expected {
t . Fatalf ( "Toggling the value atleast 3 times, until it's true, `SetToIf(true, false)` should flip v to false, expected: %v, got %v" , expected , v . IsSet ( ) )
}
}
2021-05-15 14:33:05 +03:00
func TestJSONCompatibleWithBuiltinBool ( t * testing . T ) {
for _ , value := range [ ] bool { true , false } {
// Test bool -> bytes -> AtomicBool
2021-04-28 17:48:48 +03:00
2021-05-15 14:33:05 +03:00
// act 1. bool -> bytes
buf , err := json . Marshal ( value )
2021-05-08 18:49:35 +03:00
if err != nil {
2021-05-15 14:33:05 +03:00
t . Fatalf ( "json.Marshal(%t) failed: %s" , value , err )
2021-05-08 18:49:35 +03:00
}
2021-04-28 17:48:48 +03:00
2021-05-15 14:33:05 +03:00
// act 2. bytes -> AtomicBool
//
2021-05-08 18:49:35 +03:00
// Try to unmarshall the JSON byte slice
// of a normal boolean into an AtomicBool
2021-05-15 14:33:05 +03:00
//
// Create an AtomicBool with the oppsite default to ensure the unmarshal process did the work
ab := NewBool ( ! value )
err = json . Unmarshal ( buf , ab )
if err != nil {
t . Fatalf ( ` json.Unmarshal("%s", %T) failed: %s ` , buf , ab , err )
}
// assert
if ab . IsSet ( ) != value {
t . Fatalf ( "Expected AtomicBool to represent %t but actual value was %t" , value , ab . IsSet ( ) )
}
// Test AtomicBool -> bytes -> bool
// act 3. AtomicBool -> bytes
buf , err = json . Marshal ( ab )
2021-05-08 18:49:35 +03:00
if err != nil {
2021-05-15 14:33:05 +03:00
t . Fatalf ( "json.Marshal(%T) failed: %s" , ab , err )
2021-05-08 18:49:35 +03:00
}
2021-04-28 17:48:48 +03:00
2021-05-15 14:33:05 +03:00
// using the opposite value for the same reason as the former case
b := ab . IsNotSet ( )
// act 4. bytes -> bool
err = json . Unmarshal ( buf , & b )
if err != nil {
t . Fatalf ( ` json.Unmarshal("%s", %T) failed: %s ` , buf , & b , err )
}
// assert
if b != ab . IsSet ( ) {
t . Fatalf ( ` json.Unmarshal("%s", %T) didn't work, expected %t, got %t ` , buf , ab , ab . IsSet ( ) , b )
}
}
}
func TestUnmarshalJSONErrorNoWrite ( t * testing . T ) {
for _ , val := range [ ] bool { true , false } {
ab := NewBool ( val )
oldVal := ab . IsSet ( )
buf := [ ] byte ( "invalid-json" )
err := json . Unmarshal ( buf , ab )
if err == nil {
t . Fatalf ( ` Error expected from json.Unmarshal("%s", %T) ` , buf , ab )
}
if oldVal != ab . IsSet ( ) {
t . Fatal ( "Failed json.Unmarshal modified the value of AtomicBool which is not expected" )
2021-05-08 18:49:35 +03:00
}
2021-04-28 17:48:48 +03:00
}
}
2016-05-25 09:16:55 +03:00
func ExampleAtomicBool ( ) {
2020-07-16 09:46:58 +03:00
cond := New ( ) // default to false
any := true
old := any
new := ! any
cond . Set ( ) // Sets to true
cond . IsSet ( ) // Returns true
cond . UnSet ( ) // Sets to false
cond . IsNotSet ( ) // Returns true
cond . SetTo ( any ) // Sets to whatever you want
cond . SetToIf ( new , old ) // Sets to `new` only if the Boolean matches the `old`, returns whether succeeded
2016-05-25 09:16:55 +03:00
}
2021-09-05 18:29:37 +03:00
func BenchmarkAtomicBoolToggle ( b * testing . B ) {
v := New ( )
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
_ = v . Toggle ( )
}
}
2016-05-25 06:42:19 +03:00
// Benchmark Read
func BenchmarkMutexRead ( b * testing . B ) {
var m sync . RWMutex
var v bool
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
m . RLock ( )
_ = v
m . RUnlock ( )
}
}
func BenchmarkAtomicValueRead ( b * testing . B ) {
var v atomic . Value
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
_ = v . Load ( ) != nil
}
}
func BenchmarkAtomicBoolRead ( b * testing . B ) {
v := New ( )
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
_ = v . IsSet ( )
}
}
// Benchmark Write
func BenchmarkMutexWrite ( b * testing . B ) {
var m sync . RWMutex
var v bool
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
m . RLock ( )
v = true
m . RUnlock ( )
}
b . StopTimer ( )
_ = v
}
func BenchmarkAtomicValueWrite ( b * testing . B ) {
var v atomic . Value
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
v . Store ( true )
}
}
func BenchmarkAtomicBoolWrite ( b * testing . B ) {
v := New ( )
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
v . Set ( )
}
}
2016-06-02 06:47:41 +03:00
// Benchmark CAS
func BenchmarkMutexCAS ( b * testing . B ) {
var m sync . RWMutex
var v bool
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
m . Lock ( )
if ! v {
v = true
}
m . Unlock ( )
}
}
func BenchmarkAtomicBoolCAS ( b * testing . B ) {
v := New ( )
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
v . SetToIf ( false , true )
}
}