internal void Transition(UserflowState newState)
        {
            // Transition a userflow from current state to newState .
            if (newState == UserflowState.CANCELLED)
            {
                SetState(newState, DateTime.UtcNow.Ticks);
            }
            else
            {
                switch (state)
                {
                case UserflowState.CREATED:
                    if (newState == UserflowState.BEGUN)
                    {
                        SetState(newState, DateTime.UtcNow.Ticks);
                    }
                    else if (newState == UserflowState.CRASHED)
                    {
                        // NOP. Leave userflow in CREATED state.
                    }
                    else
                    {
                        // Userflow being begun for the first time after create.
                        // Crittercism spec says newState has to be
                        // UserflowState.BEGUN in this case unless there is change
                        // of opinion about immediately failing a userflow possibility.
                        DebugUtils.LOG_ERROR("Ending userflow that hasn't begun is forbidden.");
                    }
                    break;

                case UserflowState.BEGUN:
                    if (newState != UserflowState.BEGUN)
                    {
                        SetState(newState, DateTime.UtcNow.Ticks);
                    }
                    else
                    {
                        // Complain. Crittercism spec says you shouldn't begin userflow
                        // more than once.
                        DebugUtils.LOG_ERROR("Beginning userflow more than once is forbidden.");
                    }
                    break;

                default:
                    if (newState != UserflowState.TIMEOUT)
                    {
                        // Already in final state.  We are only checking for TIMEOUT to prevent
                        // printing this message (the Userflow must have entered some final
                        // state in the nick of time).
                        DebugUtils.LOG_ERROR("Ending userflow more than once is forbidden.");
                    }
                    break;
                }
            }
        }
Beispiel #2
0
 public void TestTransition()
 {
     {
         // Resulting state are correct.
         UserflowState state1  = UserflowState.BEGUN;
         Userflow      example = ExampleUserflow();
         example.Transition(state1);
         Assert.IsTrue(example.State() == state1,
                       "Confirm Transition changes state to given state");
     }
 }
Beispiel #3
0
        public void TestSaveLoad()
        {
            // Load saved userflow.  Does it look the same?
            // Extract fields from userflow before saving.
            Userflow      example1       = ExampleUserflow();
            string        firstName      = example1.Name();
            UserflowState firstState     = example1.State();
            long          firstTimeout   = example1.Timeout();
            int           firstValue     = example1.Value();
            long          firstEyeTime   = example1.EyeTime();
            string        firstBeginTime = example1.BeginTimeString();
            string        firstEndTime   = example1.EndTimeString();

            // Save followed by load.
            UserflowReporter.Background();
            UserflowReporter.Foreground();
            example1 = Userflow.UserflowForName(firstName);
            Assert.IsNotNull(example1,
                             "Expecting to find example1 again");
            // Extract fields from loaded userflow.
            string        secondName      = example1.Name();
            UserflowState secondState     = example1.State();
            long          secondTimeout   = example1.Timeout();
            int           secondValue     = example1.Value();
            long          secondEyeTime   = example1.EyeTime();
            string        secondBeginTime = example1.BeginTimeString();
            string        secondEndTime   = example1.EndTimeString();

            // Everything is supposed to match now (within limits of persisting doubles).
            Assert.IsTrue(firstState == secondState,
                          "Expecting firstState==secondState");
            Assert.IsTrue(firstName == secondName,
                          "Expecting firstName==secondName");
            Assert.IsTrue(firstTimeout == secondTimeout,
                          "Expecting firstTimeout==secondTimeout");
            Assert.IsTrue(firstValue == secondValue,
                          "Expecting firstValue==secondValue");
            Assert.IsTrue(firstEyeTime == secondEyeTime,
                          "Expecting firstEyeTime==secondEyeTime");
            Assert.IsTrue(firstBeginTime == secondBeginTime,
                          "Expecting firstBeginTime==secondBeginTime");
            Assert.IsTrue(firstEndTime == secondEndTime,
                          "Expecting firstEndTime==secondEndTime");
            //UserflowReporter.Background();
            Trace.WriteLine("testSaveLoad EXITING");
            Trace.WriteLine("");
        }
 internal Userflow(string name, long beginTime, long endTime) : this(name, 0)
 {
     ////////////////////////////////////////////////////////////////
     // Input:
     //    name = userflow name
     //    beginTime = userflow begin time in ticks
     //    endTime = userflow end time in ticks
     // NOTE: Automatic userflows ("App Load", "App Foreground", "App Background")
     ////////////////////////////////////////////////////////////////
     state = UserflowState.ENDED;
     SetBeginTime(beginTime);
     SetEndTime(endTime);
     eyeTime = endTime - beginTime;
     SetForegroundTime(beginTime);
     // This "Save" needs to occur after the "state" assigned above is known.
     Debug.WriteLine("Reporting '" + name + "' == " + (eyeTime / (double)TimeUtils.TICKS_PER_SEC) + " seconds");
     UserflowReporter.Save(this);
 }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            Userflow userflow = null;
            // Load JArray from stream .  For better or worse, probably a bit of the latter,
            // Newtonsoft.Json deserializes a persisted timestamp string as a JTokenType.Date .
            JArray a = JArray.Load(reader);

            if (IsUserflowJson(a))
            {
                // Extract values from "JArray a" .  This is all according to the
                // Crittercism "Userflows Wire Protocol - v1" in Confluence.
                string        name           = (string)((JValue)(a[(int)UserflowIndex.Name])).Value;
                UserflowState state          = (UserflowState)(long)((JValue)(a[(int)UserflowIndex.State])).Value;
                double        timeoutSeconds = Convert.ToDouble(((JValue)(a[(int)UserflowIndex.Timeout])).Value); // seconds (!!!)
                int           timeout        = (int)Convert.ToDouble(timeoutSeconds * TimeUtils.MSEC_PER_SEC);    // milliseconds
                int           value          = Convert.ToInt32(((JValue)(a[(int)UserflowIndex.Value])).Value);
#if DEBUG
                {
                    // NOTE: Userflow metadata skeleton in the closet.  Tossed around
                    // in early design and even implemented in iOS SDK client side.  It
                    // was never supported on platform nor publicly exposed to users.
                    JObject o = a[(int)UserflowIndex.Metadata] as JObject;
                    Debug.Assert(o.Count == 0);
                }
#endif
                Dictionary <string, string> metadata = new Dictionary <string, string>();
                long   beginTime      = JsonUtils.JsonDateToTicks(a[(int)UserflowIndex.BeginTime]);        // ticks
                long   endTime        = JsonUtils.JsonDateToTicks(a[(int)UserflowIndex.EndTime]);          // ticks
                double eyeTimeSeconds = Convert.ToDouble(((JValue)(a[(int)UserflowIndex.EyeTime])).Value); // seconds (!!!)
                long   eyeTime        = (long)(eyeTimeSeconds * TimeUtils.TICKS_PER_SEC);                  // ticks
                // Call Userflow constructor.
                userflow = new Userflow(
                    name,
                    state,
                    timeout,
                    value,
                    metadata,
                    beginTime,
                    endTime,
                    eyeTime
                    );
            }
            return(userflow);
        }
        private Userflow()
        {
            name     = "";
            state    = UserflowState.CREATED;
            value    = NULL_VALUE;
            metadata = new Dictionary <string, string>();
            // 0 ticks corresponds to reference date of
            // 12:00:00 midnight, January 1, 0001
            // (0:00:00 UTC on January 1, 0001, in the Gregorian calendar)
            // https://msdn.microsoft.com/en-us/library/system.datetime.ticks(v=vs.110).aspx
            // And we are calling SetBeginTime and SetEndTime here so the strings
            // beginTimeString and endTimeString will get computed.
            const long referenceTime = 0;

            SetBeginTime(referenceTime);
            SetEndTime(referenceTime);
            eyeTime = 0;
            SetForegroundTime(referenceTime);
        }
        private void SetState(UserflowState newState, long nowTime)
        {
            // Establishes newState for userflow at nowTime .
            state          = newState;
            isForegrounded = UserflowReporter.isForegrounded;
            switch (state)
            {
            case UserflowState.CANCELLED:
                SetEndTime(nowTime);
                RemoveTimer();
                break;

            case UserflowState.BEGUN:
                SetBeginTime(nowTime);
                if (isForegrounded)
                {
                    SetForegroundTime(nowTime);
                    CreateTimer();
                }
                break;

            default:
                // Final state
                SetEndTime(nowTime);
                RemoveTimer();
                if (isForegrounded)
                {
                    // Entering final state is effectively closing early ahead of
                    // the time when app may be backgrounded later.  The persisted
                    // record gets the correct additional "eye time".
                    eyeTime       += nowTime - foregroundTime;
                    isForegrounded = false;
                }
                if (newState == UserflowState.TIMEOUT)
                {
                    Crittercism.OnUserflowTimeOut(new CRUserflowEventArgs(name));
                }
                break;
            }
            UserflowReporter.Save(this);
        }
 internal Userflow(
     string name,
     UserflowState state,
     int timeout,
     int value,
     Dictionary <string, string> metadata,
     long beginTime,
     long endTime,
     long eyeTime)
 {
     // This constructor only used by UserflowConvert ReadJson for finished
     // Userflow's appearing in either a UserflowReport or a Crash report.
     this.name     = name;
     this.state    = state;
     this.timeout  = timeout; // milliseconds
     this.value    = value;
     this.metadata = metadata;
     SetBeginTime(beginTime); // ticks
     SetEndTime(endTime);     // ticks
     this.eyeTime = eyeTime;  // ticks
 }