public WorkoutLinkSelectionDialog(Workout parentWorkout, IStep destinationStep)
        {
            InitializeComponent();

            this.Text = GarminFitnessView.GetLocalizedString("SelectWorkoutText");
            OKButton.Text = CommonResources.Text.ActionOk;
            Cancel_Button.Text = CommonResources.Text.ActionCancel;

            WorkoutsListBox.Format += new ListControlConvertEventHandler(WorkoutsListBox_Format);
            foreach (Workout workout in GarminWorkoutManager.Instance.Workouts)
            {
                // Don't allow recursive workouts or the same workout link twice since
                //  we can't tell the difference between substeps
                if (workout != parentWorkout &&
                    !workout.ContainsWorkoutLink(parentWorkout) &&
                    parentWorkout.CanAcceptNewStep(workout.StepCount, destinationStep))
                {
                    WorkoutsListBox.Items.Add(workout);
                }
            }

            if (WorkoutsListBox.Items.Count > 0)
            {
                WorkoutsListBox.SelectedIndex = 0;
            }
            else
            {
                OKButton.Enabled = false;
            }
        }
        public WorkoutLinkStep(Workout parent, Workout linkedWorkout)
            : base(StepType.Link, parent)
        {
            m_LinkedWorkout = linkedWorkout;

            Initialize();
        }
        public StepWrapper(StepWrapper parent, IStep element, Workout baseWorkout, bool isWorkoutLinkChild)
            : base(parent, element)
        {
            Debug.Assert(baseWorkout != null);

            m_BaseWorkout = baseWorkout;
            m_IsWorkoutLinkChild = isWorkoutLinkChild;
        }
        public RepeatStep(Byte numRepeats, Workout parent)
            : base(StepType.Repeat, parent)
        {
            Debug.Assert(numRepeats <= Constants.MaxRepeats);

            RepeatCountDuration repeatCountDuration = new RepeatCountDuration(this);
            repeatCountDuration.RepetitionCount = numRepeats;
            Duration = repeatCountDuration;

            m_StepsToRepeat.Add(new RegularStep(parent));
        }
        public WorkoutLinkStep(Stream stream, DataVersion version, Workout parent)
            : base(StepType.Link, parent)
        {
            Deserialize(stream, version);

            // Make sure we found the linked workout, it is possible we are copy-pasting
            //  an unavailable one, in which case this step is temporary and will be disposed
            if (LinkedWorkout != null)
            {
                Initialize();
            }
        }
        public void TestTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            int targetPosition1;
            int targetPosition2;

            // This is required to determine the step's id in the workout during serialization
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // No target
            NullTarget noTarget = new NullTarget(placeholderStep);
            noTarget.Serialize(database, "NoTarget", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(noTargetResult);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid no target serialization");

            // Cadence targets
            BaseCadenceTarget cadenceTarget = new BaseCadenceTarget(placeholderStep);

            // Cadence range
            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(80, 90, cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(cadenceRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid CadenceRange target serialization");

            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(60, 120, cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(cadenceRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid CadenceRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "CadenceRange target serializations don't differ");

            // Cadence ST zone
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(cadenceSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid CadenceST target serialization");

            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[4], cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(cadenceSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid CadenceST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "CadenceST target serializations don't differ");

            // Speed targets
            BaseSpeedTarget speedTarget = new BaseSpeedTarget(placeholderStep);

            // Speed range
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRange target serialization");

            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Mile, Speed.Units.Speed, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedRange target serializations don't differ");

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedRangeTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRange pace target serialization");

            speedTarget.ConcreteTarget = new SpeedRangeTarget(7.5, 15, Length.Units.Mile, Speed.Units.Pace, speedTarget);
            speedTarget.Serialize(database, "SpeedRangeTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedRangeTargetResult4);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedRRange pace target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedRange pace target serializations don't differ");

            // Speed Garmin zone
            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(1, speedTarget);
            speedTarget.Serialize(database, "SpeedGTCTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedGTCTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedGTC target serialization");

            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(3, speedTarget);
            speedTarget.Serialize(database, "SpeedGTCTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedGTCTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedGTC target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedGTC target serializations don't differ");

            // Speed ST zone
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRST target serialization");

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid SpeedRST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "SpeedRST target serializations don't differ");

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(speedSTTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRST %Max target serialization");

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(speedSTTargetResult4);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid SpeedRST %Max target serialization");
            Assert.GreaterOrEqual(targetPosition2, targetPosition1, "SpeedST %Max target serializations don't differ");

            // Heart rate targets
            BaseHeartRateTarget hrTarget = new BaseHeartRateTarget(placeholderStep);

            // HR range
            hrTarget.ConcreteTarget = new HeartRateRangeTarget(130, 170, false, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRange target serialization");

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(100, 190, false, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRRange target serializations don't differ");

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(50, 70, true, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrRangeTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRange %Max target serialization");

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(75, 95, true, hrTarget);
            hrTarget.Serialize(database, "HRRangeTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrRangeTargetResult4);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRRange %Max target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRRange %Max target serializations don't differ");

            // HR Garmin zone
            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(1, hrTarget);
            hrTarget.Serialize(database, "HRGTCTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrGTCTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRGTC target serialization");

            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(3, hrTarget);
            hrTarget.Serialize(database, "HRGTCTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrGTCTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRGTC target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRGTC target serializations don't differ");

            // HR ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRST target serialization");

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[4], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid HRRST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "HRRST target serializations don't differ");

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = true;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(hrSTTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRST %Max target serialization");

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[4], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(hrSTTargetResult4);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid HRRST %Max target serialization");
            Assert.GreaterOrEqual(targetPosition2, targetPosition1, "HRST %Max target serializations don't differ");

            // Power targets
            BasePowerTarget powerTarget = new BasePowerTarget(placeholderStep);

            // Power range
            powerTarget.ConcreteTarget = new PowerRangeTarget(150, 200, false, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerRangeTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerRange target serialization");

            powerTarget.ConcreteTarget = new PowerRangeTarget(300, 400, false, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerRangeTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerRange target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerRange target serializations don't differ");

            powerTarget.ConcreteTarget = new PowerRangeTarget(67, 80, true, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerRangeTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerRange %FTP target serialization");

            powerTarget.ConcreteTarget = new PowerRangeTarget(120, 160, true, powerTarget);
            powerTarget.Serialize(database, "PowerRangeTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerRangeTargetResult4);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerRRange %FTP target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerRange %FTP target serializations don't differ");

            // Power Garmin zone
            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(1, powerTarget);
            powerTarget.Serialize(database, "PowerGTCTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerGTCTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerGTC target serialization");

            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(3, powerTarget);
            powerTarget.Serialize(database, "PowerGTCTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerGTCTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerGTC target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerGTC target serializations don't differ");

            // Power ST zone
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[4];
            Options.Instance.ExportSportTracksPowerAsPercentFTP = false;
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[1], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget1", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerSTTargetResult1);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerST target serialization");

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget2", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerSTTargetResult2);
            Assert.GreaterOrEqual(targetPosition2, 0, "Invalid PowerST target serialization");
            Assert.Greater(targetPosition2, targetPosition1, "PowerST target serializations don't differ");

            Options.Instance.ExportSportTracksPowerAsPercentFTP = true;
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[1], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget3", testDocument);
            targetPosition1 = testDocument.InnerXml.IndexOf(powerSTTargetResult3);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerST %FTP target serialization");

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget4", testDocument);
            targetPosition2 = testDocument.InnerXml.IndexOf(powerSTTargetResult4);
            Assert.GreaterOrEqual(targetPosition1, 0, "Invalid PowerST %FTP target serialization");
            Assert.GreaterOrEqual(targetPosition2, targetPosition1, "PowerST %FTP target serializations don't differ");

            // Make sure to reset options to previous values
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = exportHRAsMax;
            Options.Instance.ExportSportTracksPowerAsPercentFTP = exportPowerAsFTP;
        }
        public void TestRepeatStepFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep regularStep;
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField durationValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField durationTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField targetValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);

            regularStep = placeholderWorkout.Steps[0] as RegularStep;

            // Setup message
            serializedMessage.AddField(durationValueField);
            serializedMessage.AddField(durationTypeField);
            serializedMessage.AddField(targetValueField);
            durationValueField.SetUInt32(0); // Step id to start repeat at
            durationTypeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatCount);
            targetValueField.SetUInt32(2);

            // This test is a little hard to follow because repeat steps in FIT format regroup previously deserialized steps.
            //  We will illustrate the expected output throughout the test to ease validation.

            // At this point we have the following
            // - Root
            //  - Regular step (id = 0)
            // By deserializing the message we should get the following
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(1, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with single child");
            Assert.AreEqual(2, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with single child");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with single child");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[0], "Invalid workout structure after FIT deserialization of repeat step with single child");
            Assert.AreEqual(1, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with single child");
            Assert.AreEqual(regularStep, repeatStep.StepsToRepeat[0], "Invalid child in repeat after FIT deserialization of repeat step with single child");

            placeholderWorkout.Steps.AddStepToRoot(new RegularStep(placeholderWorkout));
            placeholderWorkout.Steps.AddStepToRoot(new RegularStep(placeholderWorkout));
            repeatStep = new RepeatStep(placeholderWorkout);
            durationValueField.SetUInt32(2); // Step id to start repeat at

            // At this point we have the following
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            //  - Regular step (id = 2)
            //  - Regular step (id = 3)
            // By deserializing the message we should get the following
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            //  - Repeat step (id = 4)
            //   - Regular step (id = 2)
            //   - Regular step (id = 3)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(2, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple children");
            Assert.AreEqual(5, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with multiple children");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[1], "Invalid workout structure after FIT deserialization of repeat step with multiple children");
            Assert.AreEqual(2, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with multiple children");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple children");

            regularStep = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.InsertStepBeforeStep(regularStep, placeholderWorkout.Steps[0]);
            repeatStep = new RepeatStep(placeholderWorkout);
            durationValueField.SetUInt32(1); // Step id to start repeat at

            // At this point we have the following
            // - Root
            //  - Regular step (id = 0)
            //  - Repeat step (id = 2)
            //   - Regular step (id = 1)
            //  - Repeat step (id = 5)
            //   - Regular step (id = 4)
            //   - Regular step (id = 5)
            //  - Repeat step (not deserialized yet)
            // By deserializing the message we should get the following
            // - Root
            //  - Regular step (id = 0)
            //  - Repeat step (id = 6)
            //   - Repeat step (id = 2)
            //    - Regular step (id = 1)
            //   - Repeat step (id = 5)
            //    - Regular step (id = 3)
            //    - Regular step (id = 4)
            repeatStep.DeserializeFromFIT(serializedMessage);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            Assert.AreEqual(2, placeholderWorkout.Steps.Count, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(7, placeholderWorkout.StepCount, "Incorrect number of steps in workout after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(placeholderWorkout.Steps[0] is RegularStep, "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(placeholderWorkout.Steps[1] is RepeatStep, "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(regularStep, placeholderWorkout.Steps[0], "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(repeatStep, placeholderWorkout.Steps[1], "Invalid workout structure after FIT deserialization of repeat step with multiple nested children");
            Assert.AreEqual(2, repeatStep.StepsToRepeat.Count, "Invalid number of children in repeat after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(repeatStep.StepsToRepeat[0] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple nested children");
            Assert.IsTrue(repeatStep.StepsToRepeat[1] is RepeatStep, "Invalid repeat step structure after FIT deserialization of repeat step with multiple nested children");
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField messageField;

            // Name
            placeholderWorkout.Name = "WorkoutTest1";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);
            Assert.IsNotNull(messageField, "Workout name field not serialized for step");
            Assert.AreEqual("WorkoutTest1", messageField.GetString(), "Invalid workout name FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest2";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.WorkoutName);
            Assert.IsNotNull(messageField, "Workout name field not serialized for step");
            Assert.AreEqual("WorkoutTest2", messageField.GetString(), "Invalid workout name FIT serialization");
            serializedMessage.Clear();

            // Category/Sport
            placeholderWorkout.Name = "WorkoutTest3";
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[5];
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.SportType);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(FITSports.Cycling, (FITSports)messageField.GetEnum(), "Invalid workout sport FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest4";
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[6];
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.SportType);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(FITSports.Running, (FITSports)messageField.GetEnum(), "Invalid workout sport FIT serialization");
            serializedMessage.Clear();

            // Number of steps
            placeholderWorkout.Name = "WorkoutTest5";
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid workout step count FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest6";
            placeholderWorkout.Steps.AddStepToRoot(new RepeatStep(placeholderWorkout));
            placeholderWorkout.FillFITMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutFieldIds.NumSteps);
            Assert.IsNotNull(messageField, "Workout sport field not serialized for step");
            Assert.AreEqual(3, messageField.GetUInt16(), "Invalid workout step count FIT serialization");
            serializedMessage.Clear();

            // Do not test steps serialization here, it has it's own test
        }
        public void TestTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            Workout workout = new Workout("Test", logbook.ActivityCategories[0]);
            RegularStep step = workout.Steps[0] as RegularStep;
            int resultPosition;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Name
            workout.Name = "WorkoutTest1";
            workout.Serialize(database, "WorkoutTest1", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult1);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid workout name TCX serialization");

            workout.Name = "WorkoutTest2";
            workout.Serialize(database, "WorkoutTest2", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult2);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid workout name TCX serialization");

            // Category/Sport
            workout.Name = "WorkoutTest3";
            workout.Category = logbook.ActivityCategories[0].SubCategories[5];
            workout.Serialize(database, "WorkoutTest3", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult3);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid sport TCX serialization");

            workout.Name = "WorkoutTest4";
            workout.Category = logbook.ActivityCategories[0].SubCategories[6];
            workout.Serialize(database, "WorkoutTest4", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult4);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid sport TCX serialization");

            // Scheduled dates
            workout.Name = "WorkoutTest5";
            workout.ScheduleWorkout(DateTime.Now.ToLocalTime());
            workout.Serialize(database, "WorkoutTest5", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult5);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid scheduled dates TCX serialization");

            workout.Name = "WorkoutTest6";
            workout.ScheduleWorkout(DateTime.Now.ToLocalTime().AddDays(1));
            workout.Serialize(database, "WorkoutTest6", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult6);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid multiple scheduled dates TCX serialization");

            // Test past dates should not schedule anything
            workout.Name = "WorkoutTest6b";
            workout.ScheduleWorkout(new DateTime(1999, 12, 31, 1, 0, 0));
            workout.Serialize(database, "WorkoutTest6b", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult6b);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid past scheduled dates TCX serialization");

            // New Zealand time (UST+12H) is a case where we had problems because the offset changes the day
            String currentZoneName = Time.CurrentTimeZone.standardName;
            workout.Name = "WorkoutTest7";
            workout.ScheduledDates.Clear();
            bool res = Time.SetTimeZone("New Zealand Standard Time");
            workout.ScheduleWorkout(DateTime.Now.ToLocalTime());
            workout.Serialize(database, "WorkoutTest7", testDocument);
            res = Time.SetTimeZone(currentZoneName);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult7);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid UST+12 scheduled dates TCX serialization");

            // Notes
            workout.Name = "WorkoutTest8";
            workout.ScheduledDates.Clear();
            workout.Notes = "Notes test 1";
            workout.Serialize(database, "WorkoutTest8", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult8);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid workout notes TCX serialization");

            workout.Name = "WorkoutTest9";
            workout.ScheduledDates.Clear();
            workout.Notes = "Notes test 2";
            workout.Serialize(database, "WorkoutTest9", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(workoutTestResult9);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid workout notes TCX serialization");
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // This is required to determine the step's id in the workout during serialization
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);

            // No target
            NullTarget noTarget = new NullTarget(placeholderStep);
            noTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid no target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.NoTarget, messageField.GetEnum(), "Invalid target type in field for target");
            serializedMessage.Clear();

            // Cadence targets
            BaseCadenceTarget cadenceTarget = new BaseCadenceTarget(placeholderStep);

            // Cadence range
            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(80, 90, cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(90, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            cadenceTarget.ConcreteTarget = new CadenceRangeTarget(60, 120, cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(60, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            // Cadence ST zone
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(85, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[4], cadenceTarget);
            cadenceTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid CadenceST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Cadence, messageField.GetEnum(), "Invalid target type in field for CadenceST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(100, messageField.GetUInt32(), "Invalid lower value in field for CadenceRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid CadenceRange target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid upper value in field for CadenceRange target");
            serializedMessage.Clear();

            // Speed targets
            BaseSpeedTarget speedTarget = new BaseSpeedTarget(placeholderStep);

            // Speed range
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Kilometer, Speed.Units.Speed, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedRangeTarget(20, 30, Length.Units.Mile, Speed.Units.Speed, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8940, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(13411, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            // Pace shouldn't change the values saved
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(8940, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(13411, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange pace target");
            serializedMessage.Clear();

            // Speed Garmin zone
            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(1, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for SpeedGTC target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedZoneGTCTarget(3, speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for SpeedGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for SpeedGTC target");
            serializedMessage.Clear();

            // Speed ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[3];
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(2777, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[2], speedTarget);
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange target");
            serializedMessage.Clear();

            // Pace shouldn't change the values saved
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[2];
            speedTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid SpeedST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Speed, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(5555, messageField.GetUInt32(), "Invalid lower value in field for SpeedRange pace target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid SpeedRange pace target FIT serialization");
            Assert.AreEqual(8333, messageField.GetUInt32(), "Invalid upper value in field for SpeedRange pace target");
            serializedMessage.Clear();

            // Heart rate targets
            BaseHeartRateTarget hrTarget = new BaseHeartRateTarget(placeholderStep);

            // HR range
            hrTarget.ConcreteTarget = new HeartRateRangeTarget(130, 170, false, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(230, messageField.GetUInt32(), "Invalid lower value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(270, messageField.GetUInt32(), "Invalid upper value in field for HRRange target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(100, 190, false, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(200, messageField.GetUInt32(), "Invalid lower value in field for HRRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange target FIT serialization");
            Assert.AreEqual(290, messageField.GetUInt32(), "Invalid upper value in field for HRRange target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateRangeTarget(50, 70, true, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(50, messageField.GetUInt32(), "Invalid lower value in field for HRRange %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRRange %Max target FIT serialization");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid upper value in field for HRRange %Max target");
            serializedMessage.Clear();

            // HR Garmin zone
            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(1, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for HRGTC target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateZoneGTCTarget(3, hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for HRGTC target");
            serializedMessage.Clear();

            // HR ST zone
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = false;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(240, messageField.GetUInt32(), "Invalid lower value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(260, messageField.GetUInt32(), "Invalid upper value in field for HRST target");
            serializedMessage.Clear();

            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[4], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(280, messageField.GetUInt32(), "Invalid lower value in field for HRST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST target FIT serialization");
            Assert.AreEqual(340, messageField.GetUInt32(), "Invalid upper value in field for HRST target");
            serializedMessage.Clear();

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = true;
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.HeartRate, messageField.GetEnum(), "Invalid target type in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(68, messageField.GetUInt32(), "Invalid lower value in field for HRST %Max target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid HRST %Max target FIT serialization");
            Assert.AreEqual(82, messageField.GetUInt32(), "Invalid upper value in field for HRST %Max target");
            serializedMessage.Clear();

            // Power targets
            BasePowerTarget powerTarget = new BasePowerTarget(placeholderStep);

            // Power range
            powerTarget.ConcreteTarget = new PowerRangeTarget(150, 200, false, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid lower value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1200, messageField.GetUInt32(), "Invalid upper value in field for PowerRange target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerRangeTarget(300, 400, false, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1300, messageField.GetUInt32(), "Invalid lower value in field for PowerRange target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange target FIT serialization");
            Assert.AreEqual(1400, messageField.GetUInt32(), "Invalid upper value in field for PowerRange target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerRangeTarget(67, 80, true, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(67, messageField.GetUInt32(), "Invalid lower value in field for PowerRange %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerRange %FTP target FIT serialization");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid upper value in field for PowerRange %FTP target");
            serializedMessage.Clear();

            // Power Garmin zone
            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(1, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerGTC target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual(1, messageField.GetUInt32(), "Invalid zone value in field for PowerGTC target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerZoneGTCTarget(3, powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerGTC target FIT serialization");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid zone value in field for PowerGTC target");
            serializedMessage.Clear();

            // Power ST zone
            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[4];
            Options.Instance.ExportSportTracksPowerAsPercentFTP = false;
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[1], powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid lower value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerSTFTP target FIT serialization");
            Assert.AreEqual(1200, messageField.GetUInt32(), "Invalid upper value in field for PowerST target");
            serializedMessage.Clear();

            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST target FIT serialization");
            Assert.AreEqual(1300, messageField.GetUInt32(), "Invalid lower value in field for PowerST target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerSTFTP target FIT serialization");
            Assert.AreEqual(1400, messageField.GetUInt32(), "Invalid upper value in field for PowerST target");
            serializedMessage.Clear();

            Options.Instance.ExportSportTracksPowerAsPercentFTP = true;
            powerTarget.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetType);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual((Byte)FITWorkoutStepTargetTypes.Power, messageField.GetEnum(), "Invalid target type in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid zone value in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(120, messageField.GetUInt32(), "Invalid lower value in field for PowerST %FTP target");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            Assert.IsNotNull(messageField, "Invalid PowerST %FTP target FIT serialization");
            Assert.AreEqual(160, messageField.GetUInt32(), "Invalid upper value in field for PowerST %FTP target");
            serializedMessage.Clear();

            // Make sure to reset options to previous values
            Options.Instance.ExportSportTracksHeartRateAsPercentMax = exportHRAsMax;
            Options.Instance.ExportSportTracksPowerAsPercentFTP = exportPowerAsFTP;
        }
        public void TestTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ITarget loadedTarget = null;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            XmlAttribute attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // No target
            readNode.InnerXml = noTargetResult;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "No target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is NullTarget, "No target wasn't deserialized as proper type");

            // Cadence targets
            BaseCadenceTarget cadenceTarget;

            // Cadence range
            CadenceRangeTarget cadenceRangeTarget;

            readNode.InnerXml = cadenceRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(80, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(90, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

            readNode.InnerXml = cadenceRangeTargetResult2;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(60, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(120, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

            // Speed targets
            BaseSpeedTarget speedTarget;

            // Speed range
            SpeedRangeTarget speedRangeTarget;
            double speed;

            readNode.InnerXml = speedRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(20, speed, STCommon.Data.Constants.Delta, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(30, speed, STCommon.Data.Constants.Delta, "SpeedRange max value wasn't properly deserialized");

            readNode.InnerXml = speedRangeTargetResult4;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(4, speed, STCommon.Data.Constants.Delta, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(8, speed, STCommon.Data.Constants.Delta, "SpeedRange max value wasn't properly deserialized");

            // Speed Garmin zone
            SpeedZoneGTCTarget speedGTCTarget;

            readNode.InnerXml = speedGTCTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedGTC target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneGTCTarget, "SpeedGTC target wasn't deserialized as proper concrete type");
            speedGTCTarget = speedTarget.ConcreteTarget as SpeedZoneGTCTarget;
            Assert.AreEqual(1, speedGTCTarget.Zone, "SpeedGTC zone value wasn't properly deserialized");

            readNode.InnerXml = speedGTCTargetResult2;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedGTC target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneGTCTarget, "SpeedGTC target wasn't deserialized as proper concrete type");
            speedGTCTarget = speedTarget.ConcreteTarget as SpeedZoneGTCTarget;
            Assert.AreEqual(3, speedGTCTarget.Zone, "SpeedGTC zone value wasn't properly deserialized");

            // Heart rate targets
            BaseHeartRateTarget hrTarget;

            // Heart rate range
            HeartRateRangeTarget hrRangeTarget;

            readNode.InnerXml = hrRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(false, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(130, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(170, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

            readNode.InnerXml = hrRangeTargetResult3;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(true, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(50, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(70, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

            // Heart rate Garmin zone
            HeartRateZoneGTCTarget hrGTCTarget;

            readNode.InnerXml = hrGTCTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(1, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            readNode.InnerXml = hrGTCTargetResult2;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(3, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            // Power targets
            BasePowerTarget powerTarget;

            // Power range
            PowerRangeTarget powerRangeTarget;

            readNode.InnerXml = powerRangeTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(false, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(150, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(200, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

            readNode.InnerXml = powerRangeTargetResult3;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(true, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(67, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(80, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

            // Power Garmin zone
            PowerZoneGTCTarget powerGTCTarget;

            readNode.InnerXml = powerGTCTargetResult1;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(1, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");

            readNode.InnerXml = powerGTCTargetResult2;
            loadedTarget = TargetFactory.Create(readNode.FirstChild, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(3, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");
        }
        public void TestRegularStepTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep step = placeholderWorkout.Steps[0] as RegularStep;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // Active step
            readNode.InnerXml = regularStepTestResult1;
            step.Deserialize(readNode.FirstChild);
            Assert.AreEqual("StepTest1", step.Name, "Invalid step name in TCX deserialization");
            Assert.AreEqual(RegularStep.StepIntensity.Active, step.Intensity, "Invalid step intensity in active step TCX deserialization");

            // Rest step
            readNode.InnerXml = regularStepTestResult2;
            step.Deserialize(readNode.FirstChild);
            Assert.AreEqual("StepTest2", step.Name, "Invalid step name in TCX deserialization");
            Assert.AreEqual(RegularStep.StepIntensity.Rest, step.Intensity, "Invalid step intensity in rest step TCX deserialization");

            // No name node in XML should revert to empty name
            readNode.InnerXml = noNameStepTestResult;
            step.Deserialize(readNode.FirstChild);
            Assert.IsTrue(String.IsNullOrEmpty(step.Name), "Invalid empty step name in TCX deserialization");

            // Invalid steps
            try
            {
                readNode.InnerXml = stepInvalidResult1;
                step.Deserialize(readNode.FirstChild);
                Assert.Fail("Missing step duration deserialization should trigger exception");
            }
            catch (GarminFitnessXmlDeserializationException)
            {
            }

            try
            {
                readNode.InnerXml = stepInvalidResult2;
                step.Deserialize(readNode.FirstChild);
                Assert.Fail("Missing step intensity TCX deserialization should trigger exception");
            }
            catch (GarminFitnessXmlDeserializationException)
            {
            }

            try
            {
                readNode.InnerXml = stepInvalidResult3;
                step.Deserialize(readNode.FirstChild);
                Assert.Fail("Missing step target TCX deserialization should trigger exception");
            }
            catch (GarminFitnessXmlDeserializationException)
            {
            }

            // Duration & target test
            readNode.InnerXml = regularStepComponentsTestResult;
            step.Deserialize(readNode.FirstChild);
            Assert.IsTrue(step.Duration is DistanceDuration, "Invalid step duration TCX deserialization");
            Assert.IsTrue(step.Target is BaseHeartRateTarget, "Invalid step target TCX deserialization");
        }
        public void TestFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Repeat count
            RepeatCountDuration repeatDuration = new RepeatCountDuration(placeholderStep);
            repeatDuration.RepetitionCount = 3;
            repeatDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat count duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatCount, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat count duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat count duration");
            Assert.AreEqual(3, messageField.GetUInt32(), "Invalid target value in field for repeat count duration");
            serializedMessage.Clear();

            repeatDuration.RepetitionCount = 7;
            repeatDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat count duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatCount, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat count duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat count duration");
            Assert.AreEqual(7, messageField.GetUInt32(), "Invalid target value in field for repeat count duration");
            serializedMessage.Clear();

            // Repeat until calories
            RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
            caloriesDuration.CaloriesToSpend = 550;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until calories duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilCalories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until calories duration");
            Assert.AreEqual(550, messageField.GetUInt32(), "Invalid target value in field for repeat until calories duration");
            serializedMessage.Clear();

            caloriesDuration.CaloriesToSpend = 750;
            caloriesDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until calories duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilCalories, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until calories duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until calories duration");
            Assert.AreEqual(750, messageField.GetUInt32(), "Invalid target value in field for repeat until calories duration");
            serializedMessage.Clear();

            // Repeat until distance
            RepeatUntilDistanceDuration distanceDuration = new RepeatUntilDistanceDuration(1, Length.Units.Kilometer, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until distance duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilDistance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until distance duration");
            Assert.AreEqual(100000, messageField.GetUInt32(), "Invalid target value in field for repeat until distance duration");
            serializedMessage.Clear();

            distanceDuration = new RepeatUntilDistanceDuration(1, Length.Units.Mile, placeholderStep);
            distanceDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until distance duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilDistance, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until distance duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until distance duration");
            Assert.AreEqual(160934, messageField.GetUInt32(), "Invalid target value in field for repeat until distance duration");
            serializedMessage.Clear();

            // Repeat until HRAbove
            RepeatUntilHeartRateAboveDuration hrAboveDuration = new RepeatUntilHeartRateAboveDuration(placeholderStep);
            hrAboveDuration.IsPercentageMaxHeartRate = false;
            hrAboveDuration.MaxHeartRate = 160;
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(260, messageField.GetUInt32(), "Invalid target value in field for repeat until HRAbove duration");
            serializedMessage.Clear();

            hrAboveDuration.IsPercentageMaxHeartRate = true;
            hrAboveDuration.MaxHeartRate = 80;
            hrAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRAbove duration");
            Assert.AreEqual(80, messageField.GetUInt32(), "Invalid target value in field for repeat until HRAbove duration");
            serializedMessage.Clear();

            // Repeat until HRBelow
            RepeatUntilHeartRateBelowDuration hrBelowDuration = new RepeatUntilHeartRateBelowDuration(placeholderStep);
            hrBelowDuration.IsPercentageMaxHeartRate = false;
            hrBelowDuration.MinHeartRate = 150;
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(250, messageField.GetUInt32(), "Invalid target value in field for repeat until HRBelow duration");
            serializedMessage.Clear();

            hrBelowDuration.IsPercentageMaxHeartRate = true;
            hrBelowDuration.MinHeartRate = 70;
            hrBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until HRBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until HRBelow duration");
            Assert.AreEqual(70, messageField.GetUInt32(), "Invalid target value in field for repeat until HRBelow duration");
            serializedMessage.Clear();

            // Repeat until PowerAbove
            RepeatUntilPowerAboveDuration powerAboveDuration = new RepeatUntilPowerAboveDuration(placeholderStep);
            powerAboveDuration.IsPercentFTP = false;
            powerAboveDuration.MaxPower = 250;
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(1250, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerAbove duration");
            serializedMessage.Clear();

            powerAboveDuration.IsPercentFTP = true;
            powerAboveDuration.MaxPower = 100;
            powerAboveDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerAbove duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerAbove duration");
            Assert.AreEqual(100, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerAbove duration");
            serializedMessage.Clear();

            // Repeat until HRBelow
            RepeatUntilPowerBelowDuration powerBelowDuration = new RepeatUntilPowerBelowDuration(placeholderStep);
            powerBelowDuration.IsPercentFTP = false;
            powerBelowDuration.MinPower = 150;
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(1150, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerBelow duration");
            serializedMessage.Clear();

            powerBelowDuration.IsPercentFTP = true;
            powerBelowDuration.MinPower = 270;
            powerBelowDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until PowerBelow duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until PowerBelow duration");
            Assert.AreEqual(270, messageField.GetUInt32(), "Invalid target value in field for repeat until PowerBelow duration");
            serializedMessage.Clear();

            // Repeat until time
            RepeatUntilTimeDuration timeDuration = new RepeatUntilTimeDuration(placeholderStep);
            timeDuration.TimeInSeconds = 600;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilTime, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until time duration");
            Assert.AreEqual(600000, messageField.GetUInt32(), "Invalid target value in field for repeat until time duration");
            serializedMessage.Clear();

            timeDuration.TimeInSeconds = 350;
            timeDuration.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationType);
            Assert.IsNotNull(messageField, "Duration type field not serialized for repeat until time duration");
            Assert.AreEqual(FITWorkoutStepDurationTypes.RepeatUntilTime, (FITWorkoutStepDurationTypes)messageField.GetEnum(), "Invalid duration type in field for repeat until time duration");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.TargetValue);
            Assert.IsNotNull(messageField, "Target value field not serialized for repeat until time duration");
            Assert.AreEqual(350000, messageField.GetUInt32(), "Invalid target value in field for repeat until time duration");
            serializedMessage.Clear();
        }
        public void TestStepNotesTCXDeserialization()
        {
            Workout deserializedWorkout = new Workout("TestWorkout", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            XmlDocument testDocument = new XmlDocument();
            RegularStep regularStep;
            RepeatStep repeatStep;

            testDocument.LoadXml(workoutStepNotesExtensionsResult);
            Assert.AreEqual("TrainingCenterDatabase", testDocument.LastChild.Name, "Cannot find database node");
            Assert.AreEqual("Workouts", testDocument.LastChild.FirstChild.Name, "Cannot find workouts node");
            Assert.AreEqual("Workout", testDocument.LastChild.FirstChild.FirstChild.Name, "Cannot find workout node");
            deserializedWorkout.Deserialize(testDocument.LastChild.FirstChild.FirstChild);

            Assert.AreEqual("TestStepNoteExt", deserializedWorkout.Name, "Invalid workout name deserialized");
            Assert.AreEqual(4, deserializedWorkout.StepCount, "Invalid step count deserialized");

            // Regular step
            regularStep = deserializedWorkout.Steps[0] as RegularStep;
            Assert.AreEqual("Test Note1", regularStep.Notes, "Invalid deserialized step note for regular step");

            regularStep = deserializedWorkout.Steps[1] as RegularStep;
            Assert.AreEqual("Test Note2", regularStep.Notes, "Invalid deserialized step note for regular step");

            // Repeat step
            repeatStep = deserializedWorkout.Steps[2] as RepeatStep;
            Assert.AreEqual("Test Repeat Note", repeatStep.Notes, "Invalid deserialized step note for repeat step");

            regularStep = repeatStep.StepsToRepeat[0] as RegularStep;
            Assert.AreEqual("", regularStep.Notes, "Invalid deserialized step note for nested regular step");
        }
        public void TestStepNotesTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep regularStep = placeholderWorkout.Steps[0] as RegularStep;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Regular step
            regularStep.Notes = "This is a note";
            regularStep.Serialize(database, "stepNotesTest", testDocument);
            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for regular step note");
            Assert.AreEqual(stepNotesExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid step notes serialization");

            regularStep.Notes = "This is a new note";
            regularStep.Serialize(database, "stepNotesTest", testDocument);
            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for regular step note");
            Assert.AreEqual(stepNotesExtensionResult2,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid step notes serialization");

            // Repeat step
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            repeatStep.Notes = "This is a repeat note";
            repeatStep.Serialize(database, "stepNotesTest", testDocument);
            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for repeat step note");
            Assert.AreEqual(stepNotesExtensionResult3,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 2].OuterXml,
                            "Invalid step notes serialization");
        }
        public void TestRepeatStepTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep step = new RepeatStep(placeholderWorkout);
            int resultPosition;

            placeholderWorkout.Steps.AddStepToRoot(step);
            placeholderWorkout.Steps.RemoveStep(placeholderWorkout.Steps[0]);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Single child
            step.Serialize(database, "RepeatStepTest1", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(repeatStepTestResult1);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for repeat step with single child");

            // Multiple children
            step.StepsToRepeat.Add(new RegularStep(placeholderWorkout));
            step.Serialize(database, "RepeatStepTest2", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(repeatStepTestResult2);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for repeat step with multiple children");

            // Nested repeat steps
            step.StepsToRepeat.Add(new RepeatStep(placeholderWorkout));
            step.Serialize(database, "RepeatStepTest3", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(repeatStepTestResult3);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for repeat step with nested repeat child");
        }
        public void TestRepeatStepTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            XmlAttribute attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // Single child
            readNode.InnerXml = repeatStepTestResult1;
            repeatStep.Deserialize(readNode.FirstChild);
            Assert.AreEqual(1, repeatStep.StepsToRepeat.Count, "Invalid step count deserialized for repeat step with single child");
            Assert.IsTrue(repeatStep.StepsToRepeat[0] is RegularStep, "Invalid child step deserialized for repeat step with single child");

            // Multiple children
            readNode.InnerXml = repeatStepTestResult2;
            repeatStep.Deserialize(readNode.FirstChild);
            Assert.AreEqual(2, repeatStep.StepsToRepeat.Count, "Invalid step count deserialized for repeat step with multiple children");
            Assert.IsTrue(repeatStep.StepsToRepeat[0] is RegularStep, "Invalid child step deserialized for repeat step with multiple children");
            Assert.IsTrue(repeatStep.StepsToRepeat[1] is RegularStep, "Invalid child step deserialized for repeat step with multiple children");

            // Nested repeat step
            readNode.InnerXml = repeatStepTestResult3;
            repeatStep.Deserialize(readNode.FirstChild);
            Assert.AreEqual(3, repeatStep.StepsToRepeat.Count, "Invalid step count deserialized for repeat step with nested repeat child");
            Assert.IsTrue(repeatStep.StepsToRepeat[0] is RegularStep, "Invalid child step deserialized for repeat step with nested repeat child");
            Assert.IsTrue(repeatStep.StepsToRepeat[1] is RegularStep, "Invalid child step deserialized for repeat step with nested repeat child");
            Assert.IsTrue(repeatStep.StepsToRepeat[2] is RepeatStep, "Invalid child step deserialized for repeat step with nested repeat child");
            RepeatStep tempRepeat = repeatStep.StepsToRepeat[2] as RepeatStep;
            Assert.AreEqual(1, tempRepeat.StepsToRepeat.Count, "Invalid child step deserialized for repeat step with nested repeat child");
            Assert.IsTrue(tempRepeat.StepsToRepeat[0] is RegularStep, "Invalid child step deserialized for repeat step with nested repeat child");

            // Regular step after repeat
            readNode.InnerXml = repeatStepTestResult4;
            repeatStep.Deserialize(readNode.FirstChild);
            Assert.AreEqual(4, repeatStep.StepsToRepeat.Count, "Invalid step count deserialized for repeat step with step after nested repeat child");
            Assert.IsTrue(repeatStep.StepsToRepeat[3] is RegularStep, "Invalid child step deserialized for repeat step with step after nested repeat child");
        }
        public void TestRepeatStepFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep repeatStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            placeholderWorkout.Steps.RemoveStep(placeholderWorkout.Steps[0]);

            // Single child
            // - Root
            //  - Repeat step (id = 1)
            //   - Regular step (id = 0)
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Multiple children
            // - Root
            //  - Repeat step (id = 2)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            repeatStep.StepsToRepeat.Add(new RegularStep(placeholderWorkout));
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(0, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Nested repeat steps
            //  - Repeat step (id = 4)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            //   - Repeat step (id = 3)
            //    - Regular step (id = 2)
            repeatStep.StepsToRepeat.Add(new RepeatStep(placeholderWorkout));
            repeatStep.StepsToRepeat[2].FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(2, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();

            // Nested repeats with 2nd repeat as first nested step
            // - Root
            //  - Repeat step (id = 4)
            //   - Regular step (id = 0)
            //   - Regular step (id = 1)
            //   - Repeat step (id = 3)
            //    - Regular step (id = 2)
            //  - Repeat step (id = 8)
            //   - Repeat step (id = 6)
            //    - Regular step (id = 5)
            //   - Regular step (id = 7)
            repeatStep = new RepeatStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(repeatStep);
            repeatStep.StepsToRepeat.Insert(0, new RepeatStep(placeholderWorkout));
            repeatStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.DurationValue);
            Assert.IsNotNull(messageField, "Duration value field not serialized for repeat step");
            Assert.AreEqual(5, messageField.GetUInt32(), "Invalid duration value FIT serialization for repeat step with single child");
            serializedMessage.Clear();
        }
        public void TestDeserializeSTTargetExtensions()
        {
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            Workout deserializedWorkout = new Workout("TestWorkout", logbook.ActivityCategories[0]);
            XmlDocument testDocument = new XmlDocument();
            RegularStep step;

            testDocument.LoadXml(workoutSTExtensionsResult);
            Assert.AreEqual("TrainingCenterDatabase", testDocument.LastChild.Name, "Cannot find database node");
            Assert.AreEqual("Workouts", testDocument.LastChild.FirstChild.Name, "Cannot find workouts node");
            Assert.AreEqual("Workout", testDocument.LastChild.FirstChild.FirstChild.Name, "Cannot find workout node");
            deserializedWorkout.Deserialize(testDocument.LastChild.FirstChild.FirstChild);

            Assert.AreEqual("TestSTExtension", deserializedWorkout.Name, "Invalid workout name deserialized");
            Assert.AreEqual(4, deserializedWorkout.StepCount, "Invalid step count deserialized");

            // Speed ST zone
            BaseSpeedTarget speedTarget;
            SpeedZoneSTTarget speedSTTarget;

            Assert.IsTrue(deserializedWorkout.Steps[0] is RegularStep, "First step is of invalid type");
            step = deserializedWorkout.Steps[0] as RegularStep;
            Assert.IsTrue(step.Target is BaseSpeedTarget, "First step target is of invalid type");
            speedTarget = step.Target as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneSTTarget, "Speed concrete target is of invalid type");
            speedSTTarget = speedTarget.ConcreteTarget as SpeedZoneSTTarget;
            Assert.AreEqual(logbook.SpeedZones[0].Zones[3], speedSTTarget.Zone, "Invalid speed ST zone deserialized");

            // Cadence ST zone
            BaseCadenceTarget cadenceTarget;
            CadenceZoneSTTarget cadenceSTTarget;

            Assert.IsTrue(deserializedWorkout.Steps[1] is RegularStep, "Second step is of invalid type");
            step = deserializedWorkout.Steps[1] as RegularStep;
            Assert.IsTrue(step.Target is BaseCadenceTarget, "Second step target is of invalid type");
            cadenceTarget = step.Target as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceZoneSTTarget, "Cadence concrete target is of invalid type");
            cadenceSTTarget = cadenceTarget.ConcreteTarget as CadenceZoneSTTarget;
            Assert.AreEqual(logbook.CadenceZones[0].Zones[4], cadenceSTTarget.Zone, "Invalid cadence ST zone deserialized");

            // HR ST zone
            BaseHeartRateTarget hrTarget;
            HeartRateZoneSTTarget hrSTTarget;

            Assert.IsTrue(deserializedWorkout.Steps[2] is RegularStep, "Third step is of invalid type");
            step = deserializedWorkout.Steps[2] as RegularStep;
            Assert.IsTrue(step.Target is BaseHeartRateTarget, "Third step target is of invalid type");
            hrTarget = step.Target as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneSTTarget, "HR concrete target is of invalid type");
            hrSTTarget = hrTarget.ConcreteTarget as HeartRateZoneSTTarget;
            Assert.AreEqual(logbook.HeartRateZones[0].Zones[3], hrSTTarget.Zone, "Invalid HR ST zone deserialized");

            // Power ST zone
            BasePowerTarget powerTarget;
            PowerZoneSTTarget powerSTTarget;

            Assert.IsTrue(deserializedWorkout.Steps[3] is RegularStep, "Fourth step is of invalid type");
            step = deserializedWorkout.Steps[3] as RegularStep;
            Assert.IsTrue(step.Target is BasePowerTarget, "Fourth step target is of invalid type");
            powerTarget = step.Target as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneSTTarget, "Power concrete target is of invalid type");
            powerSTTarget = powerTarget.ConcreteTarget as PowerZoneSTTarget;
            Assert.AreEqual(logbook.PowerZones[0].Zones[0], powerSTTarget.Zone, "Invalid power ST zone deserialized");
        }
        public void TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            IRepeatDuration deserializedDuration;

            serializedMessage.AddField(typeField);
            serializedMessage.AddField(valueField);

            // Repeat count
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatCount);
            valueField.SetUInt32(6);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatCountDuration, "Repeat count duration not properly FIT deserialized");
            RepeatCountDuration repeatDuration = deserializedDuration as RepeatCountDuration;
            Assert.AreEqual(6, repeatDuration.RepetitionCount, "Invalid value deserialized for FIT repeat count duration");

            valueField.SetUInt32(2);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatCountDuration, "Repeat count duration not properly FIT deserialized");
            repeatDuration = deserializedDuration as RepeatCountDuration;
            Assert.AreEqual(2, repeatDuration.RepetitionCount, "Invalid value deserialized for FIT repeat count duration");

            // Repeat until calories
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilCalories);
            valueField.SetUInt32(666);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilCaloriesDuration, "Repeat until calories duration not properly FIT deserialized");
            RepeatUntilCaloriesDuration caloriesDuration = deserializedDuration as RepeatUntilCaloriesDuration;
            Assert.AreEqual(666, caloriesDuration.CaloriesToSpend, "Invalid value deserialized for FIT repeat until calories duration");

            valueField.SetUInt32(150);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilCaloriesDuration, "Repeat until calories duration not properly FIT deserialized");
            caloriesDuration = deserializedDuration as RepeatUntilCaloriesDuration;
            Assert.AreEqual(150, caloriesDuration.CaloriesToSpend, "Invalid value deserialized for FIT repeat until calories duration");

            // Repeat until distance
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilDistance);
            valueField.SetUInt32(100000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilDistanceDuration, "Repeat until distance duration not properly FIT deserialized");
            RepeatUntilDistanceDuration distanceDuration = deserializedDuration as RepeatUntilDistanceDuration;
            Assert.AreEqual(1,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Kilometer),
                            STCommon.Data.Constants.Delta,
                            "Invalid value deserialized for FIT repeat until distance duration");

            valueField.SetUInt32(160934);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilDistanceDuration, "Repeat until distance duration not properly FIT deserialized");
            distanceDuration = deserializedDuration as RepeatUntilDistanceDuration;
            Assert.AreEqual(1,
                            Length.Convert(distanceDuration.GetDistanceInBaseUnit(), distanceDuration.BaseUnit, Length.Units.Mile),
                            STCommon.Data.Constants.Delta,
                            "Invalid value deserialized for FIT repeat until distance duration");

            // Repeat until HR Above
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilHeartRateGreaterThan);
            valueField.SetUInt32(220);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateAboveDuration, "Repeat until HRAbove duration not properly FIT deserialized");
            RepeatUntilHeartRateAboveDuration hrAboveDuration = deserializedDuration as RepeatUntilHeartRateAboveDuration;
            Assert.IsFalse(hrAboveDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");
            Assert.AreEqual(120, hrAboveDuration.MaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");

            valueField.SetUInt32(80);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateAboveDuration, "Repeat until HRAbove duration not properly FIT deserialized");
            hrAboveDuration = deserializedDuration as RepeatUntilHeartRateAboveDuration;
            Assert.IsTrue(hrAboveDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");
            Assert.AreEqual(80, hrAboveDuration.MaxHeartRate, "Invalid value deserialized for FIT repeat until HRAbove duration");

            // Repeat until HR Below
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilHeartRateLessThan);
            valueField.SetUInt32(230);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateBelowDuration, "Repeat until HRBelow duration not properly FIT deserialized");
            RepeatUntilHeartRateBelowDuration hrBelowDuration = deserializedDuration as RepeatUntilHeartRateBelowDuration;
            Assert.IsFalse(hrBelowDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");
            Assert.AreEqual(130, hrBelowDuration.MinHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");

            valueField.SetUInt32(65);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilHeartRateBelowDuration, "Repeat until HRBelow duration not properly FIT deserialized");
            hrBelowDuration = deserializedDuration as RepeatUntilHeartRateBelowDuration;
            Assert.IsTrue(hrBelowDuration.IsPercentageMaxHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");
            Assert.AreEqual(65, hrBelowDuration.MinHeartRate, "Invalid value deserialized for FIT repeat until HRBelow duration");

            // Repeat until Power Above
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilPowerGreaterThan);
            valueField.SetUInt32(1220);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerAboveDuration, "Repeat until PowerAbove duration not properly FIT deserialized");
            RepeatUntilPowerAboveDuration powerAboveDuration = deserializedDuration as RepeatUntilPowerAboveDuration;
            Assert.IsFalse(powerAboveDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerAbove duration");
            Assert.AreEqual(220, powerAboveDuration.MaxPower, "Invalid value deserialized for FIT repeat until PowerAbove duration");

            valueField.SetUInt32(80);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerAboveDuration, "Repeat until PowerAbove duration not properly FIT deserialized");
            powerAboveDuration = deserializedDuration as RepeatUntilPowerAboveDuration;
            Assert.IsTrue(powerAboveDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerAbove duration");
            Assert.AreEqual(80, powerAboveDuration.MaxPower, "Invalid value deserialized for FIT repeat until PowerAbove duration");

            // Repeat until Power Below
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilPowerLessThan);
            valueField.SetUInt32(1400);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerBelowDuration, "Repeat until PowerBelow duration not properly FIT deserialized");
            RepeatUntilPowerBelowDuration powerBelowDuration = deserializedDuration as RepeatUntilPowerBelowDuration;
            Assert.IsFalse(powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerBelow duration");
            Assert.AreEqual(400, powerBelowDuration.MinPower, "Invalid value deserialized for FIT repeat until PowerBelow duration");

            valueField.SetUInt32(125);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilPowerBelowDuration, "Repeat until PowerBelow duration not properly FIT deserialized");
            powerBelowDuration = deserializedDuration as RepeatUntilPowerBelowDuration;
            Assert.IsTrue(powerBelowDuration.IsPercentFTP, "Invalid value deserialized for FIT repeat until PowerBelow duration");
            Assert.AreEqual(125, powerBelowDuration.MinPower, "Invalid value deserialized for FIT repeat until PowerBelow duration");

            // Repeat until time
            typeField.SetEnum((Byte)FITWorkoutStepDurationTypes.RepeatUntilTime);
            valueField.SetUInt32(60000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilTimeDuration, "Repeat until time duration not properly FIT deserialized");
            RepeatUntilTimeDuration timeDuration = deserializedDuration as RepeatUntilTimeDuration;
            Assert.AreEqual(60, timeDuration.TimeInSeconds, "Invalid value deserialized for FIT repeat until time duration");

            valueField.SetUInt32(600000);
            deserializedDuration = DurationFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(deserializedDuration is RepeatUntilTimeDuration, "Repeat until time duration not properly FIT deserialized");
            timeDuration = deserializedDuration as RepeatUntilTimeDuration;
            Assert.AreEqual(600, timeDuration.TimeInSeconds, "Invalid value deserialized for FIT repeat until time duration");
        }
        public void TestFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField typeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField valueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField lowRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField highRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);
            ITarget loadedTarget = null;

            serializedMessage.AddField(typeField);

            // No target
            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.NoTarget);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(loadedTarget is NullTarget, "No target not properly FIT deserialized without value fields");

            serializedMessage.AddField(valueField);
            serializedMessage.AddField(lowRangeField);
            serializedMessage.AddField(highRangeField);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(loadedTarget is NullTarget, "No target not properly FIT deserialized with unset value fields");

            valueField.SetUInt32(1);
            lowRangeField.SetUInt32(1);
            highRangeField.SetUInt32(1);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsTrue(loadedTarget is NullTarget, "No target not properly FIT deserialized with value fields");

            // Cadence targets
            BaseCadenceTarget cadenceTarget;

            // Cadence range
            CadenceRangeTarget cadenceRangeTarget;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Cadence);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(80);
            highRangeField.SetUInt32(90);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(80, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(90, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(60);
            highRangeField.SetUInt32(120);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "CadenceRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseCadenceTarget, "CadenceRange target wasn't deserialized as proper type");
            cadenceTarget = loadedTarget as BaseCadenceTarget;
            Assert.IsTrue(cadenceTarget.ConcreteTarget is CadenceRangeTarget, "CadenceRange target wasn't deserialized as proper concrete type");
            cadenceRangeTarget = cadenceTarget.ConcreteTarget as CadenceRangeTarget;
            Assert.AreEqual(60, cadenceRangeTarget.MinCadence, "CadenceRange min value wasn't properly deserialized");
            Assert.AreEqual(120, cadenceRangeTarget.MaxCadence, "CadenceRange max value wasn't properly deserialized");

            // Speed targets
            BaseSpeedTarget speedTarget;

            // Speed range
            SpeedRangeTarget speedRangeTarget;
            double speed;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Speed);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(5555);
            highRangeField.SetUInt32(8333);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(20, speed, 0.01, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Kilometer);
            Assert.AreEqual(30, speed, 0.01, "SpeedRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(1788);
            highRangeField.SetUInt32(3576);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedRange target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedRangeTarget, "SpeedRange target wasn't deserialized as proper concrete type");
            speedRangeTarget = speedTarget.ConcreteTarget as SpeedRangeTarget;
            speed = Length.Convert(speedRangeTarget.GetMinSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(4, speed, 0.01, "SpeedRange min value wasn't properly deserialized");
            speed = Length.Convert(speedRangeTarget.GetMaxSpeedInBaseUnitsPerHour(), speedRangeTarget.BaseUnit, Length.Units.Mile);
            Assert.AreEqual(8, speed, 0.01, "SpeedRange max value wasn't properly deserialized");

            // Speed Garmin zone
            SpeedZoneGTCTarget speedGTCTarget;

            valueField.SetUInt32(1);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedGTC target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneGTCTarget, "SpeedGTC target wasn't deserialized as proper concrete type");
            speedGTCTarget = speedTarget.ConcreteTarget as SpeedZoneGTCTarget;
            Assert.AreEqual(1, speedGTCTarget.Zone, "SpeedGTC zone value wasn't properly deserialized");

            valueField.SetUInt32(3);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "SpeedGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseSpeedTarget, "SpeedGTC target wasn't deserialized as proper type");
            speedTarget = loadedTarget as BaseSpeedTarget;
            Assert.IsTrue(speedTarget.ConcreteTarget is SpeedZoneGTCTarget, "SpeedGTC target wasn't deserialized as proper concrete type");
            speedGTCTarget = speedTarget.ConcreteTarget as SpeedZoneGTCTarget;
            Assert.AreEqual(3, speedGTCTarget.Zone, "SpeedGTC zone value wasn't properly deserialized");

            // Heart rate targets
            BaseHeartRateTarget hrTarget;

            // Heart rate range
            HeartRateRangeTarget hrRangeTarget;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.HeartRate);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(230);
            highRangeField.SetUInt32(270);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(false, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(130, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(170, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(50);
            highRangeField.SetUInt32(70);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRRange target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateRangeTarget, "HRRange target wasn't deserialized as proper concrete type");
            hrRangeTarget = hrTarget.ConcreteTarget as HeartRateRangeTarget;
            Assert.AreEqual(true, hrRangeTarget.IsPercentMaxHeartRate, "HRRange %Max wasn't properly deserialized");
            Assert.AreEqual(50, hrRangeTarget.MinHeartRate, "HRRange min value wasn't properly deserialized");
            Assert.AreEqual(70, hrRangeTarget.MaxHeartRate, "HRRange max value wasn't properly deserialized");

            // Heart rate Garmin zone
            HeartRateZoneGTCTarget hrGTCTarget;

            valueField.SetUInt32(2);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(2, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            valueField.SetUInt32(4);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "HRGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BaseHeartRateTarget, "HRGTC target wasn't deserialized as proper type");
            hrTarget = loadedTarget as BaseHeartRateTarget;
            Assert.IsTrue(hrTarget.ConcreteTarget is HeartRateZoneGTCTarget, "HRGTC target wasn't deserialized as proper concrete type");
            hrGTCTarget = hrTarget.ConcreteTarget as HeartRateZoneGTCTarget;
            Assert.AreEqual(4, hrGTCTarget.Zone, "HRGTC zone value wasn't properly deserialized");

            // Power targets
            BasePowerTarget powerTarget;

            // Power range
            PowerRangeTarget powerRangeTarget;

            typeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Power);
            valueField.SetUInt32(0);
            lowRangeField.SetUInt32(1150);
            highRangeField.SetUInt32(1200);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(false, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(150, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(200, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

            lowRangeField.SetUInt32(67);
            highRangeField.SetUInt32(200);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerRange target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerRange target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerRangeTarget, "PowerRange target wasn't deserialized as proper concrete type");
            powerRangeTarget = powerTarget.ConcreteTarget as PowerRangeTarget;
            Assert.AreEqual(true, powerRangeTarget.IsPercentFTP, "PowerRange %FTP wasn't properly deserialized");
            Assert.AreEqual(67, powerRangeTarget.MinPower, "PowerRange min value wasn't properly deserialized");
            Assert.AreEqual(200, powerRangeTarget.MaxPower, "PowerRange max value wasn't properly deserialized");

            // Power Garmin zone
            PowerZoneGTCTarget powerGTCTarget;

            valueField.SetUInt32(1);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(1, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");

            valueField.SetUInt32(7);
            loadedTarget = TargetFactory.Create(serializedMessage, placeholderStep);
            Assert.IsNotNull(loadedTarget, "PowerGTC target wasn't properly deserialized");
            Assert.IsTrue(loadedTarget is BasePowerTarget, "PowerGTC target wasn't deserialized as proper type");
            powerTarget = loadedTarget as BasePowerTarget;
            Assert.IsTrue(powerTarget.ConcreteTarget is PowerZoneGTCTarget, "PowerGTC target wasn't deserialized as proper concrete type");
            powerGTCTarget = powerTarget.ConcreteTarget as PowerZoneGTCTarget;
            Assert.AreEqual(7, powerGTCTarget.Zone, "PowerGTC zone value wasn't properly deserialized");
        }
        public void TestTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            XmlAttribute attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // Repeat count
            readNode.InnerXml = repeatCountDurationResult1;
            RepeatCountDuration countDuration = new RepeatCountDuration(placeholderStep);
            countDuration.Deserialize(readNode.FirstChild);
            Assert.AreEqual(4, countDuration.RepetitionCount, "Invalid number of repetitions deserialized");

            readNode.InnerXml = repeatCountDurationResult2;
            countDuration = new RepeatCountDuration(placeholderStep);
            countDuration.Deserialize(readNode.FirstChild);
            Assert.AreEqual(10, countDuration.RepetitionCount, "Invalid number of repetitions deserialized");

            // All other repeat durtions should not deserialize from TCX
            try
            {
                RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
                caloriesDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until calories should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilDistanceDuration distanceDuration = new RepeatUntilDistanceDuration(placeholderStep);
                distanceDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until distance should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilHeartRateAboveDuration hrAboveDuration = new RepeatUntilHeartRateAboveDuration(placeholderStep);
                hrAboveDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until HRAbove should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilHeartRateBelowDuration hrBelowDuration = new RepeatUntilHeartRateBelowDuration(placeholderStep);
                hrBelowDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until HRBelow should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilPowerAboveDuration powerAboveDuration = new RepeatUntilPowerAboveDuration(placeholderStep);
                powerAboveDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until PowerAbove should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilPowerBelowDuration powerBelowDuration = new RepeatUntilPowerBelowDuration(placeholderStep);
                powerBelowDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until PowerBelow should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilTimeDuration timeDuration = new RepeatUntilTimeDuration(placeholderStep);
                timeDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until time should not deserialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
                caloriesDuration.Deserialize(readNode.FirstChild);
                Assert.Fail("Repeat until calories should not deserialize");
            }
            catch (NotSupportedException)
            {
            }
        }
        public void TestSerializeSTTargetExtensions()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;

            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Cadence ST zone
            BaseCadenceTarget cadenceTarget = new BaseCadenceTarget(placeholderStep);
            cadenceTarget.ConcreteTarget = new CadenceZoneSTTarget(logbook.CadenceZones[0].Zones[2], cadenceTarget);
            cadenceTarget.Serialize(database, "CadenceSTTarget1", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for CadenceST target");
            Assert.AreEqual(cadenceSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid CadenceST target extension serialization");

            // Speed ST zone
            BaseSpeedTarget speedTarget = new BaseSpeedTarget(placeholderStep);
            speedTarget.ConcreteTarget = new SpeedZoneSTTarget(logbook.SpeedZones[0].Zones[1], speedTarget);
            speedTarget.Serialize(database, "SpeedSTTarget1", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for SpeedST target");
            Assert.AreEqual(speedSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid SpeedST target extension serialization");

            // HR ST zone
            BaseHeartRateTarget hrTarget = new BaseHeartRateTarget(placeholderStep);
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[0].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget1", testDocument);

            placeholderWorkout.Category = logbook.ActivityCategories[0].SubCategories[1];
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[2], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget3", testDocument);
            hrTarget.ConcreteTarget = new HeartRateZoneSTTarget(logbook.HeartRateZones[1].Zones[4], hrTarget);
            hrTarget.Serialize(database, "HRSTTarget4", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 3, "Missing step extension node for HRST target");
            Assert.AreEqual(hrSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 3].OuterXml,
                            "Invalid HRST target extension serialization");
            Assert.AreEqual(hrSTTargetExtensionResult2,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid HRST target extension serialization");
            Assert.AreEqual(placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].LastChild.FirstChild.InnerText,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 2].LastChild.FirstChild.InnerText,
                            "Matching HRST target extension are not identical");
            Assert.AreNotEqual(placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].LastChild.FirstChild.InnerText,
                               placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 3].LastChild.FirstChild.InnerText,
                               "Mismatching HRST target extension are identical");

            // Power ST zone
            BasePowerTarget powerTarget = new BasePowerTarget(placeholderStep);
            powerTarget.ConcreteTarget = new PowerZoneSTTarget(logbook.PowerZones[0].Zones[3], powerTarget);
            powerTarget.Serialize(database, "PowerSTTarget1", testDocument);

            Assert.GreaterOrEqual(placeholderWorkout.STExtensions.Count, 1, "Missing step extension node for PowerST target");
            Assert.AreEqual(powerSTTargetExtensionResult1,
                            placeholderWorkout.STExtensions[placeholderWorkout.STExtensions.Count - 1].OuterXml,
                            "Invalid PowerST target extension serialization");
        }
        public void TestRegularStepFITDeserialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField nameField = new FITMessageField((Byte)FITWorkoutStepFieldIds.StepName);
            FITMessageField intensityField = new FITMessageField((Byte)FITWorkoutStepFieldIds.Intensity);
            FITMessageField durationTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationType);
            FITMessageField durationValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.DurationValue);
            FITMessageField targetTypeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetType);
            FITMessageField targetValueField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetValue);
            FITMessageField targetLowRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueLow);
            FITMessageField targetHighRangeField = new FITMessageField((Byte)FITWorkoutStepFieldIds.TargetCustomValueHigh);

            // Setup message
            serializedMessage.AddField(nameField);
            serializedMessage.AddField(intensityField);
            serializedMessage.AddField(durationTypeField);
            serializedMessage.AddField(durationValueField);
            serializedMessage.AddField(targetTypeField);
            serializedMessage.AddField(targetValueField);
            serializedMessage.AddField(targetLowRangeField);
            serializedMessage.AddField(targetHighRangeField);
            durationTypeField.SetEnum((Byte)FITWorkoutStepDurationTypes.Calories);
            durationValueField.SetUInt32(456);
            targetTypeField.SetEnum((Byte)FITWorkoutStepTargetTypes.Cadence);
            targetValueField.SetUInt32(0);
            targetLowRangeField.SetUInt32(80);
            targetHighRangeField.SetUInt32(90);

            // Active step
            nameField.SetString("TestStep1");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Active);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep1", placeholderStep.Name, "Name not properly FIT deserialized for active step");
            Assert.AreEqual(RegularStep.StepIntensity.Active, placeholderStep.Intensity, "Intensity not properly FIT deserialized for active step");

            // Rest step
            nameField.SetString("TestStep2");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Rest);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep2", placeholderStep.Name, "Name not properly FIT deserialized for rest step");
            Assert.AreEqual(RegularStep.StepIntensity.Rest, placeholderStep.Intensity, "Intensity not properly FIT deserialized for rest step");

            // Warmup step
            nameField.SetString("TestStep3");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Warmup);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep3", placeholderStep.Name, "Name not properly FIT deserialized for warmup step");
            Assert.AreEqual(RegularStep.StepIntensity.Warmup, placeholderStep.Intensity, "Intensity not properly FIT deserialized for warmup step");

            // Cooldown step
            nameField.SetString("TestStep4");
            intensityField.SetEnum((Byte)FITWorkoutStepIntensity.Cooldown);
            placeholderStep.DeserializeFromFIT(serializedMessage);
            Assert.AreEqual("TestStep4", placeholderStep.Name, "Name not properly FIT deserialized for cooldown step");
            Assert.AreEqual(RegularStep.StepIntensity.Cooldown, placeholderStep.Intensity, "Intensity not properly FIT deserialized for cooldown step");

            // Duration & target test
            Assert.IsTrue(placeholderStep.Duration is CaloriesDuration, "Duration not properly FIT deserialized in regular step");
            Assert.IsTrue(placeholderStep.Target is BaseCadenceTarget, "Target not properly FIT deserialized in regular step");
        }
        public void TestFITScheduleSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = new RegularStep(placeholderWorkout);
            ILogbook logbook = PluginMain.GetApplication().Logbook;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.Workout);
            FITMessageField messageField;
            DateTime serializedDate;
            DateTime referenceDate = new DateTime(1989, 12, 31);

            // Scheduled dates
            placeholderWorkout.Name = "WorkoutTest5";
            serializedDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 12, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual((UInt32)(serializedDate - referenceDate).TotalSeconds,
                            messageField.GetUInt32(),
                            "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            placeholderWorkout.Name = "WorkoutTest6";
            serializedDate.AddDays(1);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual((UInt32)(serializedDate - referenceDate).TotalSeconds,
                            messageField.GetUInt32(),
                            "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            // Test specific date with hardcoded result
            placeholderWorkout.Name = "WorkoutTest6b";
            serializedDate = new DateTime(1999, 12, 31, 12, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual(315576000, messageField.GetUInt32(), "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();

            // New Zealand time (UST+12H) is a case where we had problems because the offset changes the day
            String currentZoneName = Time.CurrentTimeZone.standardName;
            placeholderWorkout.Name = "WorkoutTest7";
            bool res = Time.SetTimeZone("New Zealand Standard Time");
            serializedDate = new DateTime(1999, 12, 31, 0, 0, 0);
            placeholderWorkout.FillFITMessageForScheduledDate(serializedMessage, serializedDate);
            res = Time.SetTimeZone(currentZoneName);
            messageField = serializedMessage.GetField((Byte)FITScheduleFieldIds.ScheduledDate);
            Assert.IsNotNull(messageField, "Schedule date field not serialized for step");
            Assert.AreEqual(315576000, messageField.GetUInt32(), "Invalid Schedule date FIT serialization");
            serializedMessage.Clear();
        }
        public void Deserialize_V14(Stream stream, DataVersion version)
        {
            base.Deserialize(typeof(IStep), stream, version);

            GarminFitnessGuid linkedWorkoutId = new GarminFitnessGuid();

            linkedWorkoutId.Deserialize(stream, version);
            m_LinkedWorkout = GarminWorkoutManager.Instance.GetWorkout(linkedWorkoutId);
        }
        public void TestTCXDeserialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode readNode;
            XmlNode database;
            XmlAttribute attribute;
            Workout workout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            ILogbook logbook = PluginMain.GetApplication().Logbook;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);
            readNode = testDocument.CreateElement("TestNode");
            database.AppendChild(readNode);

            // Workout name
            readNode.InnerXml = workoutTestResult1;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest1", workout.Name, "Invalid workout name in TCX deserialization");

            readNode.InnerXml = workoutTestResult2;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest2", workout.Name, "Invalid workout name in TCX deserialization");

            // Category/Sport
            readNode.InnerXml = workoutTestResult3;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest3", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual(logbook.ActivityCategories[0].SubCategories[5], workout.Category, "Invalid SportTracks category in TCX deserialization");

            readNode.InnerXml = workoutTestResult4;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest4", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual(logbook.ActivityCategories[0].SubCategories[6], workout.Category, "Invalid SportTracks category in TCX deserialization");

            // Scheduled dates
            readNode.InnerXml = workoutTestResult5;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest5", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual(1, workout.ScheduledDates.Count, "Invalid number of scheduled dates in TCX deserialization");
            Assert.LessOrEqual(0, (DateTime.Now - workout.ScheduledDates[0]).TotalDays, "Invalid scheduled date in TCX deserialization");

            readNode.InnerXml = workoutTestResult6;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest6", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual(2, workout.ScheduledDates.Count, "Invalid number of scheduled dates in TCX deserialization");
            Assert.LessOrEqual(0, (DateTime.Now - workout.ScheduledDates[0]).TotalDays, "Invalid scheduled date in TCX deserialization");
            Assert.LessOrEqual(0, (DateTime.Now.AddDays(1) - workout.ScheduledDates[0]).TotalDays, "Invalid future scheduled date in TCX deserialization");

            // New Zealand time (UST+12H) is a case where we had problems because the offset changes the day
            String currentZoneName = Time.CurrentTimeZone.standardName;
            Time.SetTimeZone("New Zealand Standard Time");
            readNode.InnerXml = workoutTestResult7;
            workout.Deserialize(readNode.FirstChild);
            Time.SetTimeZone(currentZoneName);
            Assert.AreEqual("WorkoutTest7", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual(1, workout.ScheduledDates.Count, "Invalid number of scheduled dates in TCX deserialization");
            Assert.LessOrEqual(0, (DateTime.Now - workout.ScheduledDates[0]).TotalDays, "Invalid scheduled date in TCX deserialization");

            readNode.InnerXml = workoutTestResult8;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest8", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual("Notes test 1", workout.Notes, "Invalid workout notes in TCX deserialization");

            readNode.InnerXml = workoutTestResult9;
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest9", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual("Notes test 2", workout.Notes, "Invalid workout notes in TCX deserialization");

            // Erase old value for successive deserializations
            Assert.AreNotEqual(logbook.ActivityCategories[1], workout.Category, "No SportTracks category TCX deserialization setup failure");
            readNode.InnerXml = workoutTestResult10;
            workout.Category = logbook.ActivityCategories[1];
            workout.Deserialize(readNode.FirstChild);
            Assert.AreEqual("WorkoutTest10", workout.Name, "Invalid workout name in TCX deserialization");
            Assert.AreEqual(logbook.ActivityCategories[1], workout.Category, "No SportTracks category TCX deserialization won't request category");
            Assert.IsTrue(String.IsNullOrEmpty(workout.Notes), "TCXDeserialization without notes didn't erase old notes");
        }
        public void TestRegularStepFITSerialization()
        {
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep placeholderStep = placeholderWorkout.Steps[0] as RegularStep;
            bool exportHRAsMax = Options.Instance.ExportSportTracksHeartRateAsPercentMax;
            bool exportPowerAsFTP = Options.Instance.ExportSportTracksPowerAsPercentFTP;
            FITMessage serializedMessage = new FITMessage(FITGlobalMessageIds.WorkoutStep);
            FITMessageField messageField;

            // Active step
            placeholderStep.Name = "TestStep1";
            placeholderStep.Intensity = RegularStep.StepIntensity.Active;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(0, messageField.GetUInt16(), "Invalid message index FIT serialization for regular step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep1", messageField.GetString(), "Invalid name in field for active step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Active, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for active step");
            serializedMessage.Clear();

            // Rest step
            placeholderStep.Name = "TestStep2";
            placeholderStep.Intensity = RegularStep.StepIntensity.Rest;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(0, messageField.GetUInt16(), "Invalid message index FIT serialization for rest step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep2", messageField.GetString(), "Invalid name in field for rest step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Rest, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for rest step");
            serializedMessage.Clear();

            // Warmup step
            placeholderStep = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(placeholderStep);
            placeholderStep.Name = "TestStep3";
            placeholderStep.Intensity = RegularStep.StepIntensity.Warmup;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid message index FIT serialization for warmup step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep3", messageField.GetString(), "Invalid name in field for warmup step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Warmup, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for warmup step");
            serializedMessage.Clear();

            // Cooldown step
            placeholderStep.Name = "TestStep4";
            placeholderStep.Intensity = RegularStep.StepIntensity.Cooldown;
            placeholderStep.FillFITStepMessage(serializedMessage);
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.MessageIndex);
            Assert.IsNotNull(messageField, "Message index field not serialized for step");
            Assert.AreEqual(1, messageField.GetUInt16(), "Invalid message index FIT serialization for cooldwon step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.StepName);
            Assert.IsNotNull(messageField, "Invalid active step name FIT serialization");
            Assert.AreEqual("TestStep4", messageField.GetString(), "Invalid name in field for cooldown step");
            messageField = serializedMessage.GetField((Byte)FITWorkoutStepFieldIds.Intensity);
            Assert.IsNotNull(messageField, "Invalid active step intensity FIT serialization");
            Assert.AreEqual(FITWorkoutStepIntensity.Cooldown, (FITWorkoutStepIntensity)messageField.GetEnum(), "Invalid intensity in field for cooldown step");
            serializedMessage.Clear();
        }
        public void TestTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RepeatStep placeholderStep = new RepeatStep(placeholderWorkout);

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Repeat count
            RepeatCountDuration countDuration = new RepeatCountDuration(placeholderStep);
            placeholderStep.Duration = countDuration;
            countDuration.RepetitionCount = 4;
            countDuration.Serialize(database, "RepeatCountDuration1", testDocument);
            int durationPosition1 = testDocument.InnerXml.IndexOf(repeatCountDurationResult1);
            Assert.GreaterOrEqual(durationPosition1, 0, "Invalid repeat count duration serialization");
            countDuration.RepetitionCount = 10;
            countDuration.Serialize(database, "RepeatCountDuration2", testDocument);
            int durationPosition2 = testDocument.InnerXml.IndexOf(repeatCountDurationResult2);
            Assert.GreaterOrEqual(durationPosition2, 0, "Invalid repeat count duration serialization");
            Assert.Greater(durationPosition2, durationPosition1, "Repeat count durations serialization don't differ");

            // All other repeat durtions should not serialize in TCX
            try
            {
                RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
                caloriesDuration.Serialize(database, "RepeatUntilCaloriesDuration", testDocument);
                Assert.Fail("Repeat until calories should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilDistanceDuration distanceDuration = new RepeatUntilDistanceDuration(placeholderStep);
                distanceDuration.Serialize(database, "RepeatUntilDistanceDuration", testDocument);
                Assert.Fail("Repeat until distance should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilHeartRateAboveDuration hrAboveDuration = new RepeatUntilHeartRateAboveDuration(placeholderStep);
                hrAboveDuration.Serialize(database, "RepeatUntilHRAboveDuration", testDocument);
                Assert.Fail("Repeat until HRAbove should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilHeartRateBelowDuration hrBelowDuration = new RepeatUntilHeartRateBelowDuration(placeholderStep);
                hrBelowDuration.Serialize(database, "RepeatUntilHRBelowDuration", testDocument);
                Assert.Fail("Repeat until HRBelow should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilPowerAboveDuration powerAboveDuration = new RepeatUntilPowerAboveDuration(placeholderStep);
                powerAboveDuration.Serialize(database, "RepeatUntilPowerAboveDuration", testDocument);
                Assert.Fail("Repeat until PowerAbove should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilPowerBelowDuration powerBelowDuration = new RepeatUntilPowerBelowDuration(placeholderStep);
                powerBelowDuration.Serialize(database, "RepeatUntilPowerBelowDuration", testDocument);
                Assert.Fail("Repeat until PowerBelow should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilTimeDuration timeDuration = new RepeatUntilTimeDuration(placeholderStep);
                timeDuration.Serialize(database, "RepeatUntilTimeDuration", testDocument);
                Assert.Fail("Repeat until time should not serialize");
            }
            catch (NotSupportedException)
            {
            }

            try
            {
                RepeatUntilCaloriesDuration caloriesDuration = new RepeatUntilCaloriesDuration(placeholderStep);
                caloriesDuration.Serialize(database, "RepeatUntilCaloriesDuration", testDocument);
                Assert.Fail("Repeat until calories should not serialize");
            }
            catch (NotSupportedException)
            {
            }
        }
        public void TestRegularStepTCXSerialization()
        {
            XmlDocument testDocument = new XmlDocument();
            XmlNode database;
            XmlAttribute attribute;
            Workout placeholderWorkout = new Workout("Test", PluginMain.GetApplication().Logbook.ActivityCategories[0]);
            RegularStep step = placeholderWorkout.Steps[0] as RegularStep;
            int resultPosition;

            // Setup document
            testDocument.AppendChild(testDocument.CreateXmlDeclaration("1.0", "UTF-8", "no"));
            database = testDocument.CreateNode(XmlNodeType.Element, "TrainingCenterDatabase", null);
            testDocument.AppendChild(database);
            attribute = testDocument.CreateAttribute("xmlns", "xsi", GarminFitnessPlugin.Constants.xmlns);
            attribute.Value = "http://www.w3.org/2001/XMLSchema-instance";
            database.Attributes.Append(attribute);

            // Active step
            step.Name = "StepTest1";
            step.Intensity = RegularStep.StepIntensity.Active;
            step.Serialize(database, "StepTest1", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult1);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for active step");

            // Rest step
            step.Name = "StepTest2";
            step.Intensity = RegularStep.StepIntensity.Rest;
            step.Serialize(database, "StepTest2", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult2);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for rest step");

            // Warmup step
            step = new RegularStep(placeholderWorkout);
            placeholderWorkout.Steps.AddStepToRoot(step);
            Options.Instance.TCXExportWarmupAs = RegularStep.StepIntensity.Active;
            step.Name = "StepTest3";
            step.Intensity = RegularStep.StepIntensity.Warmup;
            step.Serialize(database, "StepTest3", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult3);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for warmup step");

            step.Name = "StepTest4";
            Options.Instance.TCXExportWarmupAs = RegularStep.StepIntensity.Rest;
            step.Serialize(database, "StepTest4", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult4);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for warmup step");

            // Cooldown step
            Options.Instance.TCXExportCooldownAs = RegularStep.StepIntensity.Active;
            step.Name = "StepTest5";
            step.Intensity = RegularStep.StepIntensity.Cooldown;
            step.Serialize(database, "StepTest5", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult5);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for cooldown step");

            step.Name = "StepTest6";
            Options.Instance.TCXExportCooldownAs = RegularStep.StepIntensity.Rest;
            step.Serialize(database, "StepTest6", testDocument);
            resultPosition = testDocument.InnerXml.IndexOf(regularStepTestResult6);
            Assert.GreaterOrEqual(resultPosition, 0, "Invalid step TCX serialization for cooldown step");
        }