Skip to content

Commit

Permalink
faker - updated lock usage, updated seed usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
brianvoe committed Feb 14, 2024
1 parent 6f6e042 commit f2185ac
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 46 deletions.
75 changes: 52 additions & 23 deletions faker.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package gofakeit

import (
"fmt"
"errors"
"math/rand/v2"
"reflect"

"github.com/brianvoe/gofakeit/v6/source"
"sync"
)

// Create global variable to deal with global function call
Expand All @@ -14,43 +13,73 @@ var GlobalFaker *Faker = New(0)
// Faker struct is the primary struct for using localized
type Faker struct {
Rand rand.Source

// Lock to make thread safe
Locked bool
mu sync.Mutex
}

// New creates and returns a new Faker struct seeded with a given seed
// using the PCG algorithm in lock mode for thread safety
func New(seed uint64) *Faker {
pcg := source.NewPCG(seed, seed, true)

return &Faker{
Rand: rand.New(pcg),
Rand: rand.NewPCG(seed, seed),
Locked: true,
}
}

// NewFaker takes in a rand.Source and returns a new Faker struct
func NewFaker(src rand.Source) *Faker {
// NewFaker takes in a rand.Source and thread lock state and returns a new Faker struct
func NewFaker(src rand.Source, lock bool) *Faker {
return &Faker{Rand: src}
}

func Seed(args ...any) {
// Seed attempts to seed the Faker with the given seed
func (f *Faker) Seed(args ...any) error {
// Lock if locked
if f.Locked {
f.mu.Lock()
defer f.mu.Unlock()
}

// Ensure GlobalFaker is not nil
if GlobalFaker == nil {
return
if GlobalFaker.Rand == nil {
return errors.New("GlobalFaker.Rand is nil")
}

// The standard Seed method on rand.Source expects a single int64 argument.
// This code checks for custom Seed implementations with different parameters.
method := reflect.ValueOf(GlobalFaker).MethodByName("Seed")
method := reflect.ValueOf(GlobalFaker.Rand).MethodByName("Seed")

fmt.Printf("method: %v\n", method)
// Check if a Seed method is found
if !method.IsValid() {
return errors.New("Seed method not found")
}

// Check if a Seed method is found that is not the standard one (assuming custom implementations)
if method.IsValid() && len(args) > 0 {
// Prepare arguments for the reflection call
callArgs := make([]reflect.Value, len(args))
for i, arg := range args {
callArgs[i] = reflect.ValueOf(arg)
}
// Adjust args if method requires exactly 2 args but only 1 was provided
if method.Type().NumIn() == 2 && len(args) == 1 {
args = append(args, args[0]) // Double the first value if only one is provided
}

// Dynamically call the Seed method
method.Call(callArgs)
return
// Convert args to the expected type by the Seed method
convertedArgs := make([]reflect.Value, len(args))
for i, arg := range args {
// Assume the method expects uint64, convert the argument
if argInt, ok := arg.(int); ok {
convertedArgs[i] = reflect.ValueOf(uint64(argInt))
} else {
// For simplicity, if it's not an int, just reflect the value directly
// In a real-world scenario, you might need to handle other types and conversions
convertedArgs[i] = reflect.ValueOf(arg)
}
}

// Dynamically call the Seed method with converted arguments
method.Call(convertedArgs)

return nil
}

// Seed attempts to seed the GlobalFaker with the given seed
func Seed(args ...any) error {
return GlobalFaker.Seed(args...)
}
66 changes: 43 additions & 23 deletions faker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,11 @@ package gofakeit

import (
"fmt"
"math/rand/v2"
"sync"
"testing"

"github.com/brianvoe/gofakeit/v6/source"
)

func TestSeed(t *testing.T) {
// Test crypto that has no parameters in Seed
GlobalFaker = New(0)
Seed(11)

// Will panic if Seed couldn't be called
t.Fatal("Seed failed")
}

func Example() {
Seed(11)

Expand Down Expand Up @@ -50,29 +40,59 @@ func Example() {

func ExampleNew() {
// Get new faker with default settings
fake := New(0)

// or

// Create new faker with ChaCha8
chacha := source.NewChaCha8([32]byte{5, 4, 3, 2, 1, 0}, true)
fake = NewFaker(chacha)
fake := New(11)

// All global functions are also available in the structs methods
fmt.Println("Name:", fake.Name())
fmt.Println("Email:", fake.Email())
fmt.Println("Phone:", fake.Phone())

// Output:
// Name: Markus Moen
// Email: [email protected]
// Phone: 9948995369
// Name: Sonny Stiedemann
// Email: [email protected]
// Phone: 7598907999
}

func ExampleNewFaker() {
// Create new faker with ChaCha8, cryptographically secure
chacha := rand.NewChaCha8([32]byte{5, 4, 3, 2, 1, 0})
fake := NewFaker(chacha, true)

// or

// Create new faker with PCG, pseudo-random
pcg := rand.NewPCG(0, 0)
fake = NewFaker(pcg, false)

fmt.Println("Name:", fake.Name())

// Output:
// Name: Damian Pagac
}

func TestSeed(t *testing.T) {
// Test crypto that has no parameters in Seed
GlobalFaker = New(11)

// Test a simple function
name := Name()

// Seed
err := Seed(11)
if err != nil {
t.Error(err)
}

// Make sure name is the same
if name != Name() {
t.Error("Name was different after seed")
}
}

func TestSetGlobalFaker(t *testing.T) {
// Set global to crypto
cryptoFaker := source.NewCrypto(true)
GlobalFaker = NewFaker(cryptoFaker)
crypto := rand.NewPCG(11, 11)
GlobalFaker = NewFaker(crypto, true)

// Test a simple function
name := Name()
Expand Down

0 comments on commit f2185ac

Please sign in to comment.