package tmobile import ( "context" "math/rand" "strconv" "sync" "time" "fiskerinc.com/modules/grpc/sms" ) // Want to make a client simulator that has a delay for each message, along with a chance of failure // For use with just await sms testing type tmobileSimulator struct { messages map[string]fakeMessage sy sync.Mutex count int FailPercentage int // 10 = 10% chance of failure AVGDeliveryTime time.Duration // lets do two seconds Deviation float64 // The number of seconds we can deviate by Rando *rand.Rand // Using a set seed so we can have consistent results with our random messages filter ICCIDFilter } // ChangeDeviceStatus implements TMobClienter. func (ts *tmobileSimulator) ChangeDeviceStatus(ctx context.Context, cda ChangeDeviceActivation) (err error) { panic("unimplemented") } type fakeMessage struct { finishTime time.Time //status string // Set the time that the message will resolve, and then it will return this resolution. If blank, the message is not going to be delivered within 5 seconds } // avgDeliveryTime is time in seconds func newTMboileSimulator() (tms tmobileSimulator) { tms.messages = make(map[string]fakeMessage) tms.Rando = rand.New(rand.NewSource(99)) tms.FailPercentage = 5 // 5% chance of a message not being delivered, so 10,000 will have 500 failed tms.Deviation = 2 // How many seconds, maybe don't use this tms.AVGDeliveryTime = time.Second * 2 // On average take 2ish seconds to deliver the message return } // AccessToken implements TMobClienter. func (*tmobileSimulator) AccessToken(ctx context.Context) (out *AccessTokenResponse, err error) { out = &AccessTokenResponse{} out.ExpiresIn = 500000 return } // ChangeRatePlan implements TMobClienter. func (*tmobileSimulator) ChangeRatePlan(context.Context, *ChangeRatePlanRequest) (*ChangeRatePlanResponse, error) { panic("unimplemented") } // CustomAttributes implements TMobClienter. func (*tmobileSimulator) CustomAttributes(context.Context, *CustomAtributesRequest) (*CustomAtributesResponse, error) { panic("unimplemented") } // Details implements TMobClienter. func (ts *tmobileSimulator) Details(ctx context.Context, ID string) (out *SMSDetailsResponse, err error) { out = &SMSDetailsResponse{} msg := ts.messages[ID] if msg.finishTime.IsZero() { out.Status = "Pending" return } //-cpuprofile cpu.out if time.Now().After(msg.finishTime) { out.Status = "Delivered" } else { out.Status = "Pending" } time.Sleep(time.Millisecond * 40) return } // DeviceDetails implements TMobClienter. func (*tmobileSimulator) DeviceDetails(context.Context, *DeviceDetailsRequest) (*DeviceDetailsResponse, error) { panic("unimplemented") } // GetProducts implements TMobClienter. func (*tmobileSimulator) GetProducts(context.Context, *sms.GetAvailableProductsRequest) (*sms.GetAvailableProductsResponse, error) { panic("unimplemented") } // SendSMS implements TMobClienter. // Call this synchronously as we are using a non-locking map and non-locked index count func (ts *tmobileSimulator) SendSMS(ctx context.Context, in *SendSMSRequest) (out *SendSMSResponse, err error) { ts.sy.Lock() defer ts.sy.Unlock() out = &SendSMSResponse{} ts.messages[strconv.Itoa(ts.count)] = ts.randomDelivery() out.SmsMessageID = strconv.Itoa(ts.count) ts.count += 1 return } func (ts *tmobileSimulator) randomDelivery() (msg fakeMessage) { if ts.Rando.Intn(100) <= ts.FailPercentage { return } // 0 -> .99 of deviation // 0:Deviation Value - 1/2 deviation = -.5 deviation -> .5 deviation // I think its okay that the time is only going to take longer, no real need for shorter messages ranomometer := (ts.Rando.Float64() * ts.Deviation) // Actually don't need the status for this test. We have the pending unless its delivered. WE don't really see the failed to delivery message msg.finishTime = time.Now().Add(ts.AVGDeliveryTime + time.Duration(ranomometer)) return } // SetAccessToken implements TMobClienter. func (*tmobileSimulator) SetAccessToken(accessToken string) { } // SetFilter implements TMobClienter. func (ts *tmobileSimulator) SetFilter(filter []string) { ts.filter = ICCIDFilter{ filter: filter, } } var _ TMobClienter = new(tmobileSimulator) // with the channel instead of default //BenchmarkSMSWrapper-16 1 5009644871 ns/op 3096536 B/op 45650 allocs/op // flat flat% sum% cum cum% // 50ms 31.25% 31.25% 50ms 31.25% runtime.kevent // 40ms 25.00% 56.25% 40ms 25.00% runtime.pthread_cond_wait // 20ms 12.50% 68.75% 20ms 12.50% runtime.pthread_cond_signal // 10ms 6.25% 75.00% 10ms 6.25% runtime.(*mspan).init // 10ms 6.25% 81.25% 20ms 12.50% runtime.mallocgc // 10ms 6.25% 87.50% 10ms 6.25% runtime.read // 10ms 6.25% 93.75% 10ms 6.25% runtime.usleep // 10ms 6.25% 100% 10ms 6.25% runtime.write1 // 0 0% 100% 20ms 12.50% fiskerinc.com/modules/tmobile.(*SMSClient).SendSMS // 0 0% 100% 10ms 6.25% fiskerinc.com/modules/tmobile.(*tmobileSimulator).SendSMS // with default //BenchmarkSMSWrapper-16 1 5110808221 ns/op 2923960 B/op 43176 allocs/op no major difference // flat flat% sum% cum cum% // 50ms 45.45% 45.45% 50ms 45.45% runtime.pthread_cond_wait // 20ms 18.18% 63.64% 20ms 18.18% runtime.pthread_cond_signal // 10ms 9.09% 72.73% 10ms 9.09% runtime.arenaIndex (inline) // 10ms 9.09% 81.82% 10ms 9.09% runtime.kevent // 10ms 9.09% 90.91% 10ms 9.09% runtime.stackpoolalloc // 10ms 9.09% 100% 10ms 9.09% runtime.usleep // 0 0% 100% 10ms 9.09% fiskerinc.com/modules/tmobile.BenchmarkSMSWrapper // 0 0% 100% 10ms 9.09% runtime.copystack // 0 0% 100% 10ms 9.09% runtime.findObject // 0 0% 100% 60ms 54.55% runtime.findRunnable