From 68635520f9f25e56c68ffdc84aa8e2d32aa11c98 Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Sun, 11 Sep 2022 22:46:32 +0700 Subject: [PATCH 1/8] fix: resolve map interface fake data --- faker.go | 22 ++++++++++++---------- faker_test.go | 18 ++++++++++++++++-- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/faker.go b/faker.go index ef28b90..7f4b8f5 100644 --- a/faker.go +++ b/faker.go @@ -440,11 +440,11 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error) res, err := randomString(opts.RandomStringLength, *opts) return reflect.ValueOf(res), err case reflect.Slice: - len := randomSliceAndMapSize(*opts) - if opts.SetSliceMapNilIfLenZero && len == 0 { + length := randomSliceAndMapSize(*opts) + if opts.SetSliceMapNilIfLenZero && length == 0 { return reflect.Zero(t), nil } - v := reflect.MakeSlice(t, len, len) + v := reflect.MakeSlice(t, length, length) for i := 0; i < v.Len(); i++ { val, err := getFakedValue(v.Index(i).Interface(), opts) if err != nil { @@ -499,12 +499,12 @@ func getFakedValue(a interface{}, opts *options.Options) (reflect.Value, error) return reflect.ValueOf(uint64(randomInteger(opts))), nil case reflect.Map: - len := randomSliceAndMapSize(*opts) - if opts.SetSliceMapNilIfLenZero && len == 0 { + length := randomSliceAndMapSize(*opts) + if opts.SetSliceMapNilIfLenZero && length == 0 { return reflect.Zero(t), nil } v := reflect.MakeMap(t) - for i := 0; i < len; i++ { + for i := 0; i < length; i++ { keyInstance := reflect.New(t.Key()).Elem().Interface() key, err := getFakedValue(keyInstance, opts) if err != nil { @@ -660,13 +660,13 @@ func userDefinedMap(v reflect.Value, tag string, opt options.Options) error { return nil } - len := randomSliceAndMapSize(opt) - if opt.SetSliceMapNilIfLenZero && len == 0 { + length := randomSliceAndMapSize(opt) + if opt.SetSliceMapNilIfLenZero && length == 0 { v.Set(reflect.Zero(v.Type())) return nil } definedMap := reflect.MakeMap(v.Type()) - for i := 0; i < len; i++ { + for i := 0; i < length; i++ { key, err := getValueWithTag(v.Type().Key(), tag, opt) if err != nil { return err @@ -1241,7 +1241,9 @@ func RandomInt(parameters ...int) (p []int, err error) { for i := range p { p[i] += minInt } - p = p[0:count] + if len(p) > count { + p = p[0:count] + } default: err = fmt.Errorf(fakerErrors.ErrMoreArguments, len(parameters)) } diff --git a/faker_test.go b/faker_test.go index 5b97ca9..ad52e04 100644 --- a/faker_test.go +++ b/faker_test.go @@ -415,9 +415,12 @@ func TestUnsuportedMapStringInterface(t *testing.T) { Map map[string]interface{} } var sample = new(Sample) - if err := FakeData(sample); err == nil { + if err := FakeData(sample, options.WithRandomMapAndSliceMinSize(1)); err == nil { t.Error("Expected Error. But got nil") } + if err := FakeData(sample, options.WithRandomMapAndSliceMaxSize(1)); err != nil { + t.Errorf("Expected nil. But got error: %+v", err) // empty map + } } func TestSetDataIfArgumentNotPtr(t *testing.T) { @@ -925,8 +928,19 @@ func TestRandomIntOnlySecondParameters(t *testing.T) { } func TestRandomIntThreeParameters(t *testing.T) { + if res, err := RandomInt(1, 2, 3); err != nil { + t.Fatal(err) + } else if len(res) != 2 { + t.Fatalf("expect 2 numbers, got %d", len(res)) + } + if res, err := RandomInt(1, 10, 3); err != nil { + t.Fatal(err) + } else if len(res) != 3 { + t.Fatalf("expect 3 numbers, got %d", len(res)) + } + first := rand.Intn(50) - second := rand.Intn(100) + first + second := rand.Intn(100) + first + 5 // at least 5 numbers third := rand.Intn(5) res, _ := RandomInt(first, second, third) if len(res) != (third) { From 088cc90df6d1d825b006de0231b449954d98083f Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Sun, 11 Sep 2022 23:02:40 +0700 Subject: [PATCH 2/8] feat: add v3 functions --- faker.go | 11 +++++ faker_test.go | 106 +++++++++++++++++++++++++++++++++++++++++ lorem_test.go | 8 ++++ pkg/options/options.go | 90 ++++++++++++++++++++++++++++++++-- 4 files changed, 210 insertions(+), 5 deletions(-) diff --git a/faker.go b/faker.go index 7f4b8f5..7cd6dfc 100644 --- a/faker.go +++ b/faker.go @@ -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 diff --git a/faker_test.go b/faker_test.go index ad52e04..796f33f 100644 --- a/faker_test.go +++ b/faker_test.go @@ -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 { @@ -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) { @@ -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) { @@ -496,6 +517,21 @@ 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) { @@ -507,6 +543,21 @@ func TestSetRandomMapAndSliceSize(t *testing.T) { 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) { @@ -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) { @@ -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) { diff --git a/lorem_test.go b/lorem_test.go index 6c3dc3c..bbfb930 100644 --- a/lorem_test.go +++ b/lorem_test.go @@ -70,6 +70,14 @@ 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) { diff --git a/pkg/options/options.go b/pkg/options/options.go index 9a1b2fc..d4d958b 100644 --- a/pkg/options/options.go +++ b/pkg/options/options.go @@ -4,11 +4,31 @@ import ( "errors" "fmt" "reflect" + "sync/atomic" + "unsafe" fakerErrors "github.com/go-faker/faker/v4/pkg/errors" "github.com/go-faker/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 @@ -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 } @@ -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 +} From bbda1ee141435038e80e7a963c42708aca8200b7 Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Sun, 11 Sep 2022 23:07:48 +0700 Subject: [PATCH 3/8] chore: align wiht bxcodec repo --- faker_test.go | 2 +- lorem_test.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/faker_test.go b/faker_test.go index 796f33f..ab43077 100644 --- a/faker_test.go +++ b/faker_test.go @@ -536,7 +536,7 @@ func TestSetRandomNumberBoundaries(t *testing.T) { 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") } diff --git a/lorem_test.go b/lorem_test.go index bbfb930..b02c337 100644 --- a/lorem_test.go +++ b/lorem_test.go @@ -70,7 +70,6 @@ func TestUniqueWord(t *testing.T) { if !slice.Contains(wordList, word) { t.Error("Expected word from slice wordList") } - SetGenerateUniqueValues(true) word = Word() ResetUnique() From 7aeb21bc419418f04d762dd015c01cb4a20085b9 Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Mon, 12 Sep 2022 23:00:08 +0700 Subject: [PATCH 4/8] fix: concurrent write on map --- faker.go | 263 +++++++++++++++++++++++++++++--------------------- faker_test.go | 15 +++ 2 files changed, 167 insertions(+), 111 deletions(-) diff --git a/faker.go b/faker.go index 7cd6dfc..429c8f7 100644 --- a/faker.go +++ b/faker.go @@ -11,6 +11,7 @@ import ( "regexp" "strconv" "strings" + "sync" "time" fakerErrors "github.com/go-faker/faker/v4/pkg/errors" @@ -100,93 +101,121 @@ var PriorityTags = []string{ID, HyphenatedID, EmailTag, MacAddressTag, DomainNam CurrencyTag, AmountTag, AmountWithCurrencyTag, SKIP, Length, SliceLength, Language, BoundaryStart, BoundaryEnd, ONEOF, } -var defaultTag = map[string]string{ - EmailTag: EmailTag, - MacAddressTag: MacAddressTag, - DomainNameTag: DomainNameTag, - URLTag: URLTag, - UserNameTag: UserNameTag, - IPV4Tag: IPV4Tag, - IPV6Tag: IPV6Tag, - PASSWORD: PASSWORD, - JWT: JWT, - CreditCardType: CreditCardType, - CreditCardNumber: CreditCardNumber, - LATITUDE: LATITUDE, - LONGITUDE: LONGITUDE, - PhoneNumber: PhoneNumber, - TollFreeNumber: TollFreeNumber, - E164PhoneNumberTag: E164PhoneNumberTag, - TitleMaleTag: TitleMaleTag, - TitleFemaleTag: TitleFemaleTag, - FirstNameTag: FirstNameTag, - FirstNameMaleTag: FirstNameMaleTag, - FirstNameFemaleTag: FirstNameFemaleTag, - LastNameTag: LastNameTag, - NAME: NAME, - ChineseFirstNameTag: ChineseFirstNameTag, - ChineseLastNameTag: ChineseLastNameTag, - ChineseNameTag: ChineseNameTag, - GENDER: GENDER, - UnixTimeTag: UnixTimeTag, - DATE: DATE, - TIME: TimeFormat, - MonthNameTag: MonthNameTag, - YEAR: YearFormat, - DayOfWeekTag: DayOfWeekTag, - DayOfMonthTag: DayOfMonthFormat, - TIMESTAMP: TIMESTAMP, - CENTURY: CENTURY, - TIMEZONE: TIMEZONE, - TimePeriodTag: TimePeriodFormat, - WORD: WORD, - SENTENCE: SENTENCE, - PARAGRAPH: PARAGRAPH, - CurrencyTag: CurrencyTag, - AmountTag: AmountTag, - AmountWithCurrencyTag: AmountWithCurrencyTag, - ID: ID, - HyphenatedID: HyphenatedID, +type mapperTagCustom struct { + sync.Map } -var mapperTag = map[string]interfaces.TaggedFunction{ - CreditCardType: GetPayment().CreditCardType, - CreditCardNumber: GetPayment().CreditCardNumber, - LATITUDE: GetAddress().Latitude, - LONGITUDE: GetAddress().Longitude, - PhoneNumber: GetPhoner().PhoneNumber, - TollFreeNumber: GetPhoner().TollFreePhoneNumber, - E164PhoneNumberTag: GetPhoner().E164PhoneNumber, - TitleMaleTag: GetPerson().TitleMale, - TitleFemaleTag: GetPerson().TitleFeMale, - FirstNameTag: GetPerson().FirstName, - FirstNameMaleTag: GetPerson().FirstNameMale, - FirstNameFemaleTag: GetPerson().FirstNameFemale, - LastNameTag: GetPerson().LastName, - NAME: GetPerson().Name, - ChineseFirstNameTag: GetPerson().ChineseFirstName, - ChineseLastNameTag: GetPerson().ChineseLastName, - ChineseNameTag: GetPerson().ChineseName, - GENDER: GetPerson().Gender, - UnixTimeTag: GetDateTimer().UnixTime, - DATE: GetDateTimer().Date, - TIME: GetDateTimer().Time, - MonthNameTag: GetDateTimer().MonthName, - YEAR: GetDateTimer().Year, - DayOfWeekTag: GetDateTimer().DayOfWeek, - DayOfMonthTag: GetDateTimer().DayOfMonth, - TIMESTAMP: GetDateTimer().Timestamp, - CENTURY: GetDateTimer().Century, - TIMEZONE: GetDateTimer().TimeZone, - TimePeriodTag: GetDateTimer().TimePeriod, - WORD: GetLorem().Word, - SENTENCE: GetLorem().Sentence, - PARAGRAPH: GetLorem().Paragraph, - CurrencyTag: GetPrice().Currency, - AmountTag: GetPrice().Amount, - AmountWithCurrencyTag: GetPrice().AmountWithCurrency, - ID: GetIdentifier().Digit, - HyphenatedID: GetIdentifier().Hyphenated, +func (m mapperTagCustom) Load(key string) (interfaces.TaggedFunction, bool) { + mappedTagFunc, ok := m.Map.Load(key) + if !ok { + return nil, ok + } + tagFunc, ok := mappedTagFunc.(interfaces.TaggedFunction) + if ok { + return tagFunc, ok + } + tagPureFunc, ok := mappedTagFunc.(func(v reflect.Value) (interface{}, error)) + if ok { + return tagPureFunc, ok + } + return nil, false +} + +func (m *mapperTagCustom) Store(key string, taggedFunc interfaces.TaggedFunction) { + m.Map.Store(key, taggedFunc) +} + +var defaultTag = sync.Map{} + +func initDefaultTag() { + defaultTag.Store(EmailTag, EmailTag) + defaultTag.Store(MacAddressTag, MacAddressTag) + defaultTag.Store(DomainNameTag, DomainNameTag) + defaultTag.Store(URLTag, URLTag) + defaultTag.Store(UserNameTag, UserNameTag) + defaultTag.Store(IPV4Tag, IPV4Tag) + defaultTag.Store(IPV6Tag, IPV6Tag) + defaultTag.Store(PASSWORD, PASSWORD) + defaultTag.Store(JWT, JWT) + defaultTag.Store(CreditCardType, CreditCardType) + defaultTag.Store(CreditCardNumber, CreditCardNumber) + defaultTag.Store(LATITUDE, LATITUDE) + defaultTag.Store(LONGITUDE, LONGITUDE) + defaultTag.Store(PhoneNumber, PhoneNumber) + defaultTag.Store(TollFreeNumber, TollFreeNumber) + defaultTag.Store(E164PhoneNumberTag, E164PhoneNumberTag) + defaultTag.Store(TitleMaleTag, TitleMaleTag) + defaultTag.Store(TitleFemaleTag, TitleFemaleTag) + defaultTag.Store(FirstNameTag, FirstNameTag) + defaultTag.Store(FirstNameMaleTag, FirstNameMaleTag) + defaultTag.Store(FirstNameFemaleTag, FirstNameFemaleTag) + defaultTag.Store(LastNameTag, LastNameTag) + defaultTag.Store(NAME, NAME) + defaultTag.Store(ChineseFirstNameTag, ChineseFirstNameTag) + defaultTag.Store(ChineseLastNameTag, ChineseLastNameTag) + defaultTag.Store(ChineseNameTag, ChineseNameTag) + defaultTag.Store(GENDER, GENDER) + defaultTag.Store(UnixTimeTag, UnixTimeTag) + defaultTag.Store(DATE, DATE) + defaultTag.Store(TIME, TimeFormat) + defaultTag.Store(MonthNameTag, MonthNameTag) + defaultTag.Store(YEAR, YearFormat) + defaultTag.Store(DayOfWeekTag, DayOfWeekTag) + defaultTag.Store(DayOfMonthTag, DayOfMonthFormat) + defaultTag.Store(TIMESTAMP, TIMESTAMP) + defaultTag.Store(CENTURY, CENTURY) + defaultTag.Store(TIMEZONE, TIMEZONE) + defaultTag.Store(TimePeriodTag, TimePeriodFormat) + defaultTag.Store(WORD, WORD) + defaultTag.Store(SENTENCE, SENTENCE) + defaultTag.Store(PARAGRAPH, PARAGRAPH) + defaultTag.Store(CurrencyTag, CurrencyTag) + defaultTag.Store(AmountTag, AmountTag) + defaultTag.Store(AmountWithCurrencyTag, AmountWithCurrencyTag) + defaultTag.Store(ID, ID) + defaultTag.Store(HyphenatedID, HyphenatedID) +} + +var mapperTag = mapperTagCustom{} + +func initMappertTagDefault() { + mapperTag.Store(CreditCardType, GetPayment().CreditCardType) + mapperTag.Store(CreditCardNumber, GetPayment().CreditCardNumber) + mapperTag.Store(LATITUDE, GetAddress().Latitude) + mapperTag.Store(LONGITUDE, GetAddress().Longitude) + mapperTag.Store(PhoneNumber, GetPhoner().PhoneNumber) + mapperTag.Store(TollFreeNumber, GetPhoner().TollFreePhoneNumber) + mapperTag.Store(E164PhoneNumberTag, GetPhoner().E164PhoneNumber) + mapperTag.Store(TitleMaleTag, GetPerson().TitleMale) + mapperTag.Store(TitleFemaleTag, GetPerson().TitleFeMale) + mapperTag.Store(FirstNameTag, GetPerson().FirstName) + mapperTag.Store(FirstNameMaleTag, GetPerson().FirstNameMale) + mapperTag.Store(FirstNameFemaleTag, GetPerson().FirstNameFemale) + mapperTag.Store(LastNameTag, GetPerson().LastName) + mapperTag.Store(NAME, GetPerson().Name) + mapperTag.Store(ChineseFirstNameTag, GetPerson().ChineseFirstName) + mapperTag.Store(ChineseLastNameTag, GetPerson().ChineseLastName) + mapperTag.Store(ChineseNameTag, GetPerson().ChineseName) + mapperTag.Store(GENDER, GetPerson().Gender) + mapperTag.Store(UnixTimeTag, GetDateTimer().UnixTime) + mapperTag.Store(DATE, GetDateTimer().Date) + mapperTag.Store(TIME, GetDateTimer().Time) + mapperTag.Store(MonthNameTag, GetDateTimer().MonthName) + mapperTag.Store(YEAR, GetDateTimer().Year) + mapperTag.Store(DayOfWeekTag, GetDateTimer().DayOfWeek) + mapperTag.Store(DayOfMonthTag, GetDateTimer().DayOfMonth) + mapperTag.Store(TIMESTAMP, GetDateTimer().Timestamp) + mapperTag.Store(CENTURY, GetDateTimer().Century) + mapperTag.Store(TIMEZONE, GetDateTimer().TimeZone) + mapperTag.Store(TimePeriodTag, GetDateTimer().TimePeriod) + mapperTag.Store(WORD, GetLorem().Word) + mapperTag.Store(SENTENCE, GetLorem().Sentence) + mapperTag.Store(PARAGRAPH, GetLorem().Paragraph) + mapperTag.Store(CurrencyTag, GetPrice().Currency) + mapperTag.Store(AmountTag, GetPrice().Amount) + mapperTag.Store(AmountWithCurrencyTag, GetPrice().AmountWithCurrency) + mapperTag.Store(ID, GetIdentifier().Digit) + mapperTag.Store(HyphenatedID, GetIdentifier().Hyphenated) } // Compiled regexp @@ -209,6 +238,11 @@ func init() { randNameFlag = rand.Intn(100) // for person } +func init() { + initDefaultTag() + initMappertTagDefault() +} + // ResetUnique is used to forget generated unique values. // Call this when you're done generating a dataset. func ResetUnique() { @@ -227,15 +261,15 @@ var ( ) func initMapperTagWithOption(opts ...options.OptionFunc) { - mapperTag[EmailTag] = GetNetworker(opts...).Email - mapperTag[MacAddressTag] = GetNetworker(opts...).MacAddress - mapperTag[DomainNameTag] = GetNetworker(opts...).DomainName - mapperTag[URLTag] = GetNetworker(opts...).URL - mapperTag[UserNameTag] = GetNetworker(opts...).UserName - mapperTag[IPV4Tag] = GetNetworker(opts...).IPv4 - mapperTag[IPV6Tag] = GetNetworker(opts...).IPv6 - mapperTag[PASSWORD] = GetNetworker(opts...).Password - mapperTag[JWT] = GetNetworker(opts...).Jwt + mapperTag.Store(EmailTag, GetNetworker(opts...).Email) + mapperTag.Store(MacAddressTag, GetNetworker(opts...).MacAddress) + mapperTag.Store(DomainNameTag, GetNetworker(opts...).DomainName) + mapperTag.Store(URLTag, GetNetworker(opts...).URL) + mapperTag.Store(UserNameTag, GetNetworker(opts...).UserName) + mapperTag.Store(IPV4Tag, GetNetworker(opts...).IPv4) + mapperTag.Store(IPV6Tag, GetNetworker(opts...).IPv6) + mapperTag.Store(PASSWORD, GetNetworker(opts...).Password) + mapperTag.Store(JWT, GetNetworker(opts...).Jwt) } func initOption(opt ...options.OptionFunc) *options.Options { @@ -312,23 +346,21 @@ func FakeData(a interface{}, opt ...options.OptionFunc) error { // {ID:43 Gondoruwo:{Name:Power Locatadata:324} Danger:danger-ranger} // Notes: when using a custom provider make sure to return the same type as the field func AddProvider(tag string, provider interfaces.TaggedFunction) error { - if _, ok := mapperTag[tag]; ok { + if _, ok := mapperTag.Load(tag); ok { return errors.New(fakerErrors.ErrTagAlreadyExists) } PriorityTags = append(PriorityTags, tag) - mapperTag[tag] = provider + mapperTag.Store(tag, provider) return nil } // RemoveProvider removes existing customization added with AddProvider func RemoveProvider(tag string) error { - if _, ok := mapperTag[tag]; !ok { + if _, ok := mapperTag.Load(tag); !ok { return errors.New(fakerErrors.ErrTagDoesNotExist) } - - delete(mapperTag, tag) - + mapperTag.Delete(tag) return nil } @@ -611,7 +643,7 @@ func setDataWithTag(v reflect.Value, tag string, opt options.Options) error { v = reflect.Indirect(v) switch v.Kind() { case reflect.Ptr: - if _, exist := mapperTag[tag]; !exist { + if _, exist := mapperTag.Load(tag); !exist { newv := reflect.New(v.Type().Elem()) if err := setDataWithTag(newv, tag, opt); err != nil { return err @@ -619,8 +651,12 @@ func setDataWithTag(v reflect.Value, tag string, opt options.Options) error { v.Set(newv) return nil } - if _, def := defaultTag[tag]; !def { - res, err := mapperTag[tag](v) + if _, def := defaultTag.Load(tag); !def { + tagFunc, ok := mapperTag.Load(tag) + if !ok { + return fmt.Errorf(fakerErrors.ErrTagNotSupported, tag) + } + res, err := tagFunc(v) if err != nil { return err } @@ -630,7 +666,11 @@ func setDataWithTag(v reflect.Value, tag string, opt options.Options) error { t := v.Type() newv := reflect.New(t.Elem()) - res, err := mapperTag[tag](newv.Elem()) + tagFunc, ok := mapperTag.Load(tag) + if !ok { + return fmt.Errorf(fakerErrors.ErrTagNotSupported, tag) + } + res, err := tagFunc(newv.Elem()) if err != nil { return err } @@ -648,10 +688,11 @@ func setDataWithTag(v reflect.Value, tag string, opt options.Options) error { case reflect.Map: return userDefinedMap(v, tag, opt) default: - if _, exist := mapperTag[tag]; !exist { + tagFunc, ok := mapperTag.Load(tag) + if !ok { return fmt.Errorf(fakerErrors.ErrTagNotSupported, tag) } - res, err := mapperTag[tag](v) + res, err := tagFunc(v) if err != nil { return err } @@ -661,7 +702,7 @@ func setDataWithTag(v reflect.Value, tag string, opt options.Options) error { } func userDefinedMap(v reflect.Value, tag string, opt options.Options) error { - if tagFunc, ok := mapperTag[tag]; ok { + if tagFunc, ok := mapperTag.Load(tag); ok { res, err := tagFunc(v) if err != nil { return err @@ -763,9 +804,9 @@ func getValueWithNoTag(t reflect.Type, opt options.Options) (interface{}, error) } func userDefinedArray(v reflect.Value, tag string, opt options.Options) error { - _, tagExists := mapperTag[tag] + tagFunc, tagExists := mapperTag.Load(tag) if tagExists { - res, err := mapperTag[tag](v) + res, err := tagFunc(v) if err != nil { return err } @@ -808,7 +849,7 @@ func userDefinedString(v reflect.Value, tag string, opt options.Options) error { var res interface{} var err error - if tagFunc, ok := mapperTag[tag]; ok { + if tagFunc, ok := mapperTag.Load(tag); ok { res, err = tagFunc(v) if err != nil { return err @@ -831,7 +872,7 @@ func userDefinedNumber(v reflect.Value, tag string) error { var res interface{} var err error - if tagFunc, ok := mapperTag[tag]; ok { + if tagFunc, ok := mapperTag.Load(tag); ok { res, err = tagFunc(v) if err != nil { return err diff --git a/faker_test.go b/faker_test.go index ab43077..b6c2004 100644 --- a/faker_test.go +++ b/faker_test.go @@ -5,6 +5,7 @@ import ( mathrand "math/rand" "reflect" "strings" + "sync" "testing" "time" "unicode/utf8" @@ -2436,3 +2437,17 @@ func TestFakeData_RecursiveType(t *testing.T) { } } } + +func TestFakeDate_ConcurrentSafe(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1000) + fmt.Println("Running for loop…") + for i := 0; i < 1000; i++ { + go func(i int) { + defer wg.Done() + fmt.Println(fmt.Sprintf("%s-%s", FirstName(), LastName())) + }(i) + } + wg.Wait() + fmt.Println("Finished for loop") +} From a1f60ff158bf6e4e13773b6508daed481fea5ce4 Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Mon, 12 Sep 2022 23:05:32 +0700 Subject: [PATCH 5/8] chore: resolve linter issue --- faker.go | 2 +- faker_test.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/faker.go b/faker.go index 429c8f7..9c2ee45 100644 --- a/faker.go +++ b/faker.go @@ -105,7 +105,7 @@ type mapperTagCustom struct { sync.Map } -func (m mapperTagCustom) Load(key string) (interfaces.TaggedFunction, bool) { +func (m *mapperTagCustom) Load(key string) (interfaces.TaggedFunction, bool) { mappedTagFunc, ok := m.Map.Load(key) if !ok { return nil, ok diff --git a/faker_test.go b/faker_test.go index b6c2004..09b8161 100644 --- a/faker_test.go +++ b/faker_test.go @@ -2445,7 +2445,8 @@ func TestFakeDate_ConcurrentSafe(t *testing.T) { for i := 0; i < 1000; i++ { go func(i int) { defer wg.Done() - fmt.Println(fmt.Sprintf("%s-%s", FirstName(), LastName())) + fmt.Printf("Goroutine #%d\n", i) + fmt.Printf("%s-%s\n", FirstName(), LastName()) }(i) } wg.Wait() From 7f3b88f60d24b8f68cd05a001c99d34db85dc198 Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Mon, 19 Sep 2022 23:45:02 +0700 Subject: [PATCH 6/8] chore: fix test --- faker_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faker_test.go b/faker_test.go index 09b8161..df24176 100644 --- a/faker_test.go +++ b/faker_test.go @@ -2286,7 +2286,7 @@ func TestRandomMaxMinMapSliceSize(t *testing.T) { t.Error(err) } - if len(s.Map) != c.expect { + if len(s.Map) > c.expect { t.Errorf("map (len:%d) not expect length with test case %+v\n", len(s.Map), c) } From 0050db61321333eaf3097d4b8faf6ca1e3b24b8e Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Mon, 19 Sep 2022 23:48:46 +0700 Subject: [PATCH 7/8] chore: fix test --- faker_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/faker_test.go b/faker_test.go index df24176..5de62ff 100644 --- a/faker_test.go +++ b/faker_test.go @@ -2242,7 +2242,7 @@ func TestRandomMaxMinMapSliceSize(t *testing.T) { t.Error(err) } - if len(s.Map) != c.expect { + if len(s.Map) > c.expect { t.Errorf("map (len:%d) not expect length with test case %+v\n", len(s.Map), c) } @@ -2445,10 +2445,9 @@ func TestFakeDate_ConcurrentSafe(t *testing.T) { for i := 0; i < 1000; i++ { go func(i int) { defer wg.Done() - fmt.Printf("Goroutine #%d\n", i) - fmt.Printf("%s-%s\n", FirstName(), LastName()) + _ = FirstName() + _ = LastName() }(i) } wg.Wait() - fmt.Println("Finished for loop") } From aa3389f495aecf356d9622bea4bf310a4cc379b3 Mon Sep 17 00:00:00 2001 From: Iman Tumorang Date: Mon, 19 Sep 2022 23:51:04 +0700 Subject: [PATCH 8/8] chore: fix linter --- faker_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/faker_test.go b/faker_test.go index 5de62ff..b0f9aa1 100644 --- a/faker_test.go +++ b/faker_test.go @@ -2445,6 +2445,7 @@ func TestFakeDate_ConcurrentSafe(t *testing.T) { for i := 0; i < 1000; i++ { go func(i int) { defer wg.Done() + _ = i _ = FirstName() _ = LastName() }(i)