Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

feat: implement v3 style option function #173

Merged
merged 5 commits into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions faker.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,17 @@ func ResetUnique() {
uniqueValues = map[string][]interface{}{}
}

var (
SetGenerateUniqueValues = options.SetGenerateUniqueValues
SetIgnoreInterface = options.SetIgnoreInterface
SetRandomStringLength = options.SetRandomStringLength
SetStringLang = options.SetStringLang
SetRandomMapAndSliceSize = options.SetRandomMapAndSliceSize
SetRandomMapAndSliceMaxSize = options.SetRandomMapAndSliceMaxSize
SetRandomMapAndSliceMinSize = options.SetRandomMapAndSliceMinSize
SetRandomNumberBoundaries = options.SetRandomNumberBoundaries
)

func initMapperTagWithOption(opts ...options.OptionFunc) {
mapperTag[EmailTag] = GetNetworker(opts...).Email
mapperTag[MacAddressTag] = GetNetworker(opts...).MacAddress
Expand Down
108 changes: 107 additions & 1 deletion faker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ var (

sliceLenCorrectTags = [4]string{"slice_len=0", "slice_len=4", "slice_len=5", "slice_len=10"}
sliceLenIncorrectTags = [3]string{"slice_len=b", "slice_len=-1", "slice_len=-10"}

//Sets the random max size for slices and maps.
randomMaxSize = 100
//Sets the random min size for slices and maps.
randomMinSize = 0
)

type Coupon struct {
Expand Down Expand Up @@ -476,6 +481,16 @@ func TestSetRandomStringLength(t *testing.T) {
if utfLen(someStruct.StringValue) > strLen {
t.Error("SetRandomStringLength did not work.")
}
strLen = 1
if err := SetRandomStringLength(strLen); err != nil {
t.Error("Fake data generation has failed")
}
if err := FakeData(&someStruct); err != nil {
t.Error("Fake data generation has failed")
}
if utfLen(someStruct.StringValue) > strLen {
t.Error("SetRandomStringLength did not work.")
}
}

func TestSetStringLang(t *testing.T) {
Expand All @@ -484,6 +499,12 @@ func TestSetStringLang(t *testing.T) {
if err := FakeData(&someStruct, options.WithStringLanguage(interfaces.LangENG)); err != nil {
t.Error("Fake data generation has failed")
}

someStruct = SomeStruct{}
SetStringLang(interfaces.LangENG)
if err := FakeData(&someStruct); err != nil {
t.Error("Fake data generation has failed")
}
}

func TestSetRandomNumberBoundaries(t *testing.T) {
Expand All @@ -496,17 +517,47 @@ func TestSetRandomNumberBoundaries(t *testing.T) {
if someStruct.Inta >= boundary.End || someStruct.Inta < boundary.Start {
t.Errorf("%d must be between [%d,%d)", someStruct.Inta, boundary.Start, boundary.End)
}

someStruct = SomeStruct{}
if err := SetRandomNumberBoundaries(10, 0); err == nil {
t.Error("Start must be smaller than end value")
}
boundary = interfaces.RandomIntegerBoundary{Start: 10, End: 90}
if err := SetRandomNumberBoundaries(boundary.Start, boundary.End); err != nil {
t.Error("SetRandomNumberBoundaries method is corrupted.")
}
if err := FakeData(&someStruct); err != nil {
t.Error("Fake data generation has failed")
}
if someStruct.Inta >= boundary.End || someStruct.Inta < boundary.Start {
t.Errorf("%d must be between [%d,%d)", someStruct.Inta, boundary.Start, boundary.End)
}
}

func TestSetRandomMapAndSliceSize(t *testing.T) {
someStruct := SomeStruct{}
size := 5
size := 2
if err := FakeData(&someStruct, options.WithRandomMapAndSliceMaxSize(uint(size))); err != nil {
t.Error("Fake data generation has failed")
}
if len(someStruct.MapStringStruct) > size || len(someStruct.SBool) > size {
t.Error("SetRandomMapAndSliceSize did not work.")
}

someStruct = SomeStruct{}
if err := SetRandomMapAndSliceSize(-1); err == nil {
t.Error("Random Map and Slice must not accept lower than 0 as a size")
}
size = 5
if err := SetRandomMapAndSliceSize(size); err != nil {
t.Error("SetRandomMapAndSliceSize method is corrupted.")
}
if err := FakeData(&someStruct); err != nil {
t.Error("Fake data generation has failed")
}
if len(someStruct.MapStringStruct) > size || len(someStruct.SBool) > size {
t.Error("SetRandomMapAndSliceSize did not work.")
}
}

func TestSetNilIfLenIsZero(t *testing.T) {
Expand All @@ -532,6 +583,17 @@ func TestSetIgnoreInterface(t *testing.T) {
if err := FakeData(&someInterface, options.WithIgnoreInterface(true)); err != nil {
t.Error("Fake data generation fail on interface{} with SetIgnoreInterface(true)")
}

someInterface = nil
SetIgnoreInterface(false)
if err := FakeData(&someInterface); err == nil {
t.Error("Fake data generation didn't fail on interface{}")
}
SetIgnoreInterface(true)
if err := FakeData(&someInterface); err != nil {
t.Error("Fake data generation fail on interface{} with SetIgnoreInterface(true)")
}
SetIgnoreInterface(false)
}

func TestBoundaryAndLen(t *testing.T) {
Expand Down Expand Up @@ -2187,6 +2249,50 @@ func TestRandomMaxMinMapSliceSize(t *testing.T) {
t.Errorf("slice (len:%d) not expect length with test case %+v\n", len(s.Slice), c)
}
}

orimax, orimin := randomMaxSize, randomMinSize
defer func() {
err := SetRandomMapAndSliceMaxSize(orimax)
if err != nil {
t.Fatal(err)
}
}()
defer func() {
err := SetRandomMapAndSliceMinSize(orimin)
if err != nil {
t.Fatal(err)
}
}()

for _, c := range []struct {
max, min, expect int
}{
{2, 1, 1}, // [1,2) => always 1
{2, 2, 2},
{2, 3, 3}, // if min >= max, result will always be min
} {
err := SetRandomMapAndSliceMaxSize(c.max)
if err != nil {
t.Fatal(err)
}
err = SetRandomMapAndSliceMinSize(c.min)
if err != nil {
t.Fatal(err)
}
s := SliceMap{}
err = FakeData(&s)
if err != nil {
t.Error(err)
}

if len(s.Map) != c.expect {
t.Errorf("map (len:%d) not expect length with test case %+v\n", len(s.Map), c)
}

if len(s.Slice) != c.expect {
t.Errorf("slice (len:%d) not expect length with test case %+v\n", len(s.Slice), c)
}
}
}

func TestRandomMapSliceSize(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions lorem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ func TestUniqueWord(t *testing.T) {
if !slice.Contains(wordList, word) {
t.Error("Expected word from slice wordList")
}
SetGenerateUniqueValues(true)
word = Word()
ResetUnique()
SetGenerateUniqueValues(false)
if !slice.Contains(wordList, word) {
t.Error("Expected word from slice wordList")
}
}

func TestUniqueWordPanic(t *testing.T) {
Expand Down
90 changes: 85 additions & 5 deletions pkg/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,31 @@ import (
"errors"
"fmt"
"reflect"
"sync/atomic"
"unsafe"

fakerErrors "github.com/bxcodec/faker/v4/pkg/errors"
"github.com/bxcodec/faker/v4/pkg/interfaces"
)

var (
// global settings, read/write must be concurrent safe
generateUniqueValues atomic.Value
ignoreInterface atomic.Value
randomStringLen int32 = 25
lang unsafe.Pointer
randomMaxSize int32 = 100
randomMinSize int32 = 0
iBoundary unsafe.Pointer
)

func init() {
generateUniqueValues.Store(false)
ignoreInterface.Store(false)
lang = unsafe.Pointer(&interfaces.LangENG)
iBoundary = unsafe.Pointer(&interfaces.DefaultIntBoundary)
}

// Options represent all available option for faker.
type Options struct {
// IgnoreFields used for ignoring a field when generating the fake data
Expand Down Expand Up @@ -77,12 +97,14 @@ func DefaultOption() *Options {
typeSeen: make(map[reflect.Type]int, 1),
recursionMaxDepth: 1,
}
ops.StringLanguage = &interfaces.LangENG
ops.RandomStringLength = 25 //default
ops.RandomMaxSliceSize = 100 //default
ops.RandomMinSliceSize = 0 // default
ops.GenerateUniqueValues = generateUniqueValues.Load().(bool)
ops.IgnoreInterface = ignoreInterface.Load().(bool)
ops.StringLanguage = (*interfaces.LangRuneBoundary)(atomic.LoadPointer(&lang))
ops.RandomStringLength = int(atomic.LoadInt32(&randomStringLen))
ops.RandomMaxSliceSize = int(atomic.LoadInt32(&randomMaxSize))
ops.RandomMinSliceSize = int(atomic.LoadInt32(&randomMinSize))
ops.MaxGenerateStringRetries = 1000000 //default
ops.RandomIntegerBoundary = &interfaces.DefaultIntBoundary
ops.RandomIntegerBoundary = (*interfaces.RandomIntegerBoundary)(atomic.LoadPointer(&iBoundary))
ops.RandomFloatBoundary = &interfaces.DefaultFloatBoundary
return ops
}
Expand Down Expand Up @@ -213,3 +235,61 @@ func WithRandomFloatBoundaries(boundary interfaces.RandomFloatBoundary) OptionFu
oo.RandomFloatBoundary = &boundary
}
}

// SetGenerateUniqueValues allows to set the single fake data generator functions to generate unique data.
func SetGenerateUniqueValues(unique bool) {
generateUniqueValues.Store(unique)
}

// SetIgnoreInterface allows to set a flag to ignore found interface{}s.
func SetIgnoreInterface(ignore bool) {
ignoreInterface.Store(ignore)
}

// SetRandomStringLength sets a length for random string generation
func SetRandomStringLength(size int) error {
if size < 0 {
return fmt.Errorf(fakerErrors.ErrSmallerThanZero, size)
}
atomic.StoreInt32(&randomStringLen, int32(size))
return nil
}

// SetStringLang sets language of random string generation (LangENG, LangCHI, LangRUS, LangJPN, LangKOR, EmotEMJ)
func SetStringLang(l interfaces.LangRuneBoundary) {
atomic.StorePointer(&lang, unsafe.Pointer(&l))
}

// SetRandomMapAndSliceSize sets the size for maps and slices for random generation.
// deprecates, currently left for old version usage
func SetRandomMapAndSliceSize(size int) error {
return SetRandomMapAndSliceMaxSize(size)
}

// SetRandomMapAndSliceMaxSize sets the max size for maps and slices for random generation.
func SetRandomMapAndSliceMaxSize(size int) error {
if size < 1 {
return fmt.Errorf(fakerErrors.ErrSmallerThanOne, size)
}
atomic.StoreInt32(&randomMaxSize, int32(size))
return nil
}

// SetRandomMapAndSliceMinSize sets the min size for maps and slices for random generation.
func SetRandomMapAndSliceMinSize(size int) error {
if size < 0 {
return fmt.Errorf(fakerErrors.ErrSmallerThanZero, size)
}
atomic.StoreInt32(&randomMinSize, int32(size))
return nil
}

// SetRandomNumberBoundaries sets boundary for random number generation
func SetRandomNumberBoundaries(start, end int) error {
if start > end {
return errors.New(fakerErrors.ErrStartValueBiggerThanEnd)
}
ptr := &interfaces.RandomIntegerBoundary{Start: start, End: end}
atomic.StorePointer(&iBoundary, unsafe.Pointer(ptr))
return nil
}