private static void ValidateNumericAggregateValues(MetricAggregate aggregate, string name, int count, double sum, double max, double min, double stdDev, DateTimeOffset timestamp, long periodMs)
 {
     TestUtil.Util.ValidateNumericAggregateValues(aggregate, name, count, sum, max, min, stdDev, timestamp, periodMs, "Microsoft.Azure.SimpleStatistics");
 }
        public void TrackValueObject()
        {
            var  endTS        = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));
            long periodMillis = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;

            var aggregator = new MeasurementAggregator(
                new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                dataSeries: null,
                aggregationCycleKind: CycleKind.Custom);

            aggregator.TrackValue(null);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Boolean)true));

            MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);

            ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0.0, max: 0.0, min: 0.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(SByte)(0 - 1));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: -1, max: -1, min: -1, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(Byte)2);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: 1, max: 2, min: -1, stdDev: 1.5, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(Int16)(0 - 3));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: -2, max: 2, min: -3, stdDev: 2.05480466765633, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(UInt16)4);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 4, sum: 2, max: 4, min: -3, stdDev: 2.69258240356725, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(Int32)(0 - 5));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 5, sum: -3, max: 4, min: -5, stdDev: 3.26190128606002, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(UInt32)6);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 6, sum: 3, max: 6, min: -5, stdDev: 3.86221007541882, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(Int64)(0 - 7));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 7, sum: -4, max: 6, min: -7, stdDev: 4.43547848464572, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(UInt64)8);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 8, sum: 4, max: 8, min: -7, stdDev: 5.02493781056044, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(IntPtr)0xFF));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(UIntPtr)0xFF));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Char)'x'));

            aggregator.TrackValue((object)(Single)(0f - 9.0f));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 9, sum: -5, max: 8, min: -9, stdDev: 5.59982363037962000, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue((object)(Double)10.0);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 10, sum: 5, max: 10, min: -9, stdDev: 6.18465843842649000, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue("-11");

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 11, sum: -6, max: 10, min: -11, stdDev: 6.76036088821026, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue("12.00");

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 12, sum: 6, max: 12, min: -11, stdDev: 7.34279692397023, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue("-1.300E+01");

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 13, sum: -7, max: 12, min: -13, stdDev: 7.91896831484996, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            aggregator.TrackValue("  +14. ");

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 14, sum: 7, max: 14, min: -13, stdDev: 8.5, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("fifteen"));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(""));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("foo-bar"));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 14, sum: 7, max: 14, min: -13, stdDev: 8.5, timestamp: default(DateTimeOffset), periodMs: periodMillis);
        }
        public void TrackValueDouble()
        {
            var  endTS        = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));
            long periodMillis = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;

            {
                // Empty aggregator:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0.0, max: 0.0, min: 0.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Zero value:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                aggregator.TrackValue(0);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 0.0, max: 0.0, min: 0.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Non zero value:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                aggregator.TrackValue(-42);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: -42.0, max: -42.0, min: -42.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Two values:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                aggregator.TrackValue(-42);
                aggregator.TrackValue(18);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: -24.0, max: 18.0, min: -42.0, stdDev: 30.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // 3 values:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);
                aggregator.TrackValue(1800000);
                aggregator.TrackValue(0);
                aggregator.TrackValue(-4200000);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: -2400000.0, max: 1800000.0, min: -4200000.0, stdDev: 2513961.018, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // NaNs:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);
                aggregator.TrackValue(Double.NaN);
                aggregator.TrackValue(1);
                aggregator.TrackValue(Double.NaN);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 1, max: 1, min: 1, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Infinity:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0, max: 0, min: 0, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(1);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 1, max: 1, min: 1, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(0.5);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: 1.5, max: 1, min: 0.5, stdDev: 0.25, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Double.PositiveInfinity);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: Double.MaxValue, max: Double.MaxValue, min: 0.5, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Int32.MinValue);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 4, sum: Double.MaxValue, max: Double.MaxValue, min: Int32.MinValue, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Double.PositiveInfinity);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 5, sum: Double.MaxValue, max: Double.MaxValue, min: Int32.MinValue, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Double.NegativeInfinity);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 6, sum: 0.0, max: Double.MaxValue, min: -Double.MaxValue, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(11);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 7, sum: 0.0, max: Double.MaxValue, min: -Double.MaxValue, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Very large numbers:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0, max: 0, min: 0, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Math.Exp(300));

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: Math.Exp(300), max: Math.Exp(300), min: Math.Exp(300), stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(-2 * Math.Exp(300));
                double minus2exp200 = -2 * Math.Exp(300);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: -Math.Exp(300), max: Math.Exp(300), min: minus2exp200, stdDev: 2.91363959286188000E+130, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Math.Exp(300));

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: 0, max: Math.Exp(300), min: minus2exp200, stdDev: 2.74700575206167000E+130, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Math.Exp(700));

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 4, sum: Math.Exp(700), max: Math.Exp(700), min: minus2exp200, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Double.MaxValue);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 5, sum: Double.MaxValue, max: Double.MaxValue, min: minus2exp200, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(Double.MaxValue);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 6, sum: Double.MaxValue, max: Double.MaxValue, min: minus2exp200, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(11);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 7, sum: Double.MaxValue, max: Double.MaxValue, min: minus2exp200, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(-Double.MaxValue);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 8, sum: Double.MaxValue, max: Double.MaxValue, min: -Double.MaxValue, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(-Double.PositiveInfinity);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 9, sum: 0, max: Double.MaxValue, min: -Double.MaxValue, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Large number of small values:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                for (int i = 0; i < 100000; i++)
                {
                    for (double v = 0; v <= 1.0 || Math.Abs(1.0 - v) < TestUtil.Util.MaxAllowedPrecisionError; v += 0.01)
                    {
                        aggregator.TrackValue(v);
                    }
                }

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 10100000, sum: 5050000, max: 1, min: 0, stdDev: 0.29154759474226500, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Large number of large values:
                var aggregator = new MeasurementAggregator(
                    new SimpleMetricSeriesConfiguration(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                for (int i = 0; i < 100000; i++)
                {
                    for (double v = 0; v <= 300000.0 || Math.Abs(300000.0 - v) < TestUtil.Util.MaxAllowedPrecisionError; v += 3000)
                    {
                        aggregator.TrackValue(v);
                    }
                }

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 10100000, sum: 1515000000000, max: 300000, min: 0, stdDev: 87464.2784226795, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
        }
        public void TrackValueObject()
        {
            var  endTS        = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));
            long periodMillis = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;

            var aggregator = new MeasurementAggregator(
                new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                dataSeries: null,
                aggregationCycleKind: CycleKind.Custom);

            aggregator.TrackValue(null);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Boolean)true));

            MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);

            ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0.0, max: 0.0, min: 0.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(SByte)(0 - 1)));

            aggregator.TrackValue((object)(Byte)2);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 2, max: 2, min: 2, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Int16)(0 - 3)));

            aggregator.TrackValue((object)(UInt16)4);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: 6, max: 4, min: 2, stdDev: 1, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Int32)(0 - 5)));

            aggregator.TrackValue((object)(UInt32)6);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: 12, max: 6, min: 2, stdDev: 1.63299316185545, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Int64)(0 - 7)));

            aggregator.TrackValue((object)(UInt64)8);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 4, sum: 20, max: 8, min: 2, stdDev: 2.23606797749979, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(IntPtr)0xFF));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(UIntPtr)0xFF));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Char)'x'));

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((object)(Single)(0f - 9.0f)));

            aggregator.TrackValue((object)(Double)10.0);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 5, sum: 30, max: 10, min: 2, stdDev: 2.82842712474619, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("-11"));

            aggregator.TrackValue("12");

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 6, sum: 42, max: 12, min: 2, stdDev: 3.41565025531987, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("-1.300E+01"));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("13.5"));

            aggregator.TrackValue("  +14 ");

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 7, sum: 56, max: 14, min: 2, stdDev: 4, timestamp: default(DateTimeOffset), periodMs: periodMillis);

            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("fifteen"));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(""));
            Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue("foo-bar"));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            ValidateNumericAggregateValues(aggregate, name: "null", count: 7, sum: 56, max: 14, min: 2, stdDev: 4, timestamp: default(DateTimeOffset), periodMs: periodMillis);
        }
        public void TrackValueDouble()
        {
            var  endTS        = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));
            long periodMillis = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;

            {
                // Empty aggregator:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0.0, max: 0.0, min: 0.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Zero value:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                aggregator.TrackValue(0);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 0.0, max: 0.0, min: 0.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Values out of range:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(-1));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(Int32.MinValue));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(Int64.MinValue));

                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(0.1));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(0.9));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue((float)50.01));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(50.99));

                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(((long)UInt32.MaxValue) + (long)1));

                aggregator.TrackValue(Double.NaN);
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(Double.PositiveInfinity));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(Double.NegativeInfinity));
                Assert.ThrowsException <ArgumentException>(() => aggregator.TrackValue(Double.MaxValue));

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0, max: 0, min: 0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // A single value:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: false),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                aggregator.TrackValue(42);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 42.0, max: 42.0, min: 42.0, stdDev: 0.0, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Two values:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                aggregator.TrackValue(42);
                aggregator.TrackValue(19);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: 61.0, max: 42.0, min: 19.0, stdDev: 11.5, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // 3 values:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);
                aggregator.TrackValue(1800000);
                aggregator.TrackValue(0);
                aggregator.TrackValue(4200000);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: 6000000, max: 4200000.0, min: 0, stdDev: 1720465.05340853, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Rounded values:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);
                aggregator.TrackValue(1);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 1, sum: 1, max: 1, min: 1, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(-0.0000001);
                aggregator.TrackValue(0.00000001);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 3, sum: 1, max: 1, min: 0, stdDev: 0.471404520791032, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(100.0000001);
                aggregator.TrackValue(99.9999999);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 5, sum: 201, max: 100, min: 0, stdDev: 48.8278608992858, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(((double)Int32.MaxValue) - 0.0000001);
                aggregator.TrackValue(((double)Int32.MaxValue) + 0.0000001);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 7, sum: 4294967495, max: 2147483647, min: 0, stdDev: 970134205.051638, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(((double)UInt32.MaxValue) - 0.0000001);
                aggregator.TrackValue(((double)UInt32.MaxValue) + 0.0000001);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 9, sum: 12884902085, max: 4294967295, min: 0, stdDev: 1753413037.5015, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Very large numbers:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 0, max: 0, min: 0, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodMillis);

                aggregator.TrackValue(UInt32.MaxValue - 10000);
                aggregator.TrackValue(UInt32.MaxValue - 1000);
                aggregator.TrackValue(UInt32.MaxValue - 100);
                aggregator.TrackValue(UInt32.MaxValue);

                aggregate = aggregator.CreateAggregateUnsafe(endTS);

                // ToDo!!
                // We need a more numerically stable value for the calculation of StdDev / variance.
                // For example, in this case, the expected value is 4189.49579305195, but we get 4189.4343293576, which is close, but still quite a bit off.
                // Since StdDev is utilized rarely, we leave this for later and put the actual outcome into the test expectation to catch breaks in the future.
                ValidateNumericAggregateValues(aggregate, name: "null", count: 4, sum: 17179858080, max: 4294967295, min: 4294957295, stdDev: 4189.4343293576, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Large number of small values:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                for (int i = 0; i < 100000; i++)
                {
                    for (int v = 0; v <= 100; v++)
                    {
                        aggregator.TrackValue(v);
                    }
                }

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 10100000, sum: 505000000, max: 100, min: 0, stdDev: 29.1547594742265, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
            {
                // Large number of large values:
                var aggregator = new MeasurementAggregator(
                    new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: true),
                    dataSeries: null,
                    aggregationCycleKind: CycleKind.Custom);

                for (int i = 0; i < 100000; i++)
                {
                    for (int v = 0; v <= 300000; v += 3000)
                    {
                        aggregator.TrackValue(v);
                    }
                }

                MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);
                ValidateNumericAggregateValues(aggregate, name: "null", count: 10100000, sum: 1515000000000, max: 300000, min: 0, stdDev: 87464.2784226795, timestamp: default(DateTimeOffset), periodMs: periodMillis);
            }
        }
 private static void ValidateNumericAggregateValues(MetricAggregate aggregate, int count, double sum, double max, double min, double stdDev, DateTimeOffset timestamp, long periodMs)
 {
     TestUtil.ValidateNumericAggregateValues(aggregate, "", "null", count, sum, max, min, stdDev, timestamp, periodMs, "Microsoft.Azure.Measurement");
 }
        public void GetCurrentAggregateUnsafe_MetricAggregationCycleKind_DateTimeOffset_Accumulator()
        {
            IMetricSeriesConfiguration seriesConfig = new MetricSeriesConfigurationForAccumulator(restrictToUInt32Values: false);
            const string aggregationKindMoniker     = "Microsoft.Azure.Accumulator";

            // Do not start this test in the last 10 secs or first 2 secs of a minute, to make sure the timings below are likely to work out.

            while (DateTimeOffset.Now.Second >= 49 || DateTimeOffset.Now.Second < 3)
            {
                Thread.Sleep(TimeSpan.FromMilliseconds(500));
            }

            DateTimeOffset startTS = DateTimeOffset.Now;

            var          aggregateCollector = new MemoryMetricTelemetryPipeline();
            var          manager            = new MetricManager(aggregateCollector);
            MetricSeries series             = manager.CreateNewSeries("Foo Bar", seriesConfig);

            DateTimeOffset stepTS        = startTS.AddMinutes(2);
            DateTimeOffset stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);
                Assert.IsNull(aggregate);

                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);
                Assert.IsNull(aggregate);

                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);
                Assert.IsNull(aggregate);
            }

            series.TrackValue(0.4);
            series.TrackValue(2);
            series.TrackValue(-2);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);


            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 0.4, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);

                Assert.AreEqual(
                    (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                    aggregate.AggregationPeriodDuration.TotalMilliseconds);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);
                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 0.4, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);


                    aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);
                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 0.4, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }
            }

            DateTimeOffset customCycleStartTS = stepTS.AddMinutes(1);

            manager.StartOrCycleAggregators(MetricAggregationCycleKind.Custom, customCycleStartTS, futureFilter: null);

            series.TrackValue(0.17);
            series.TrackValue(0.32);
            series.TrackValue(-0.15);
            series.TrackValue(1.07);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);

                Assert.AreEqual(
                    (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                    aggregate.AggregationPeriodDuration.TotalMilliseconds);


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }

                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }
            }

            Thread.Sleep(1500);
            DateTimeOffset flushTS = DateTimeOffset.Now;

            manager.Flush();


            DateTimeOffset quickPulseCycleStartTS = stepTS.AddMinutes(1);

            manager.StartOrCycleAggregators(MetricAggregationCycleKind.QuickPulse, quickPulseCycleStartTS, futureFilter: null);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }
            }

            series.TrackValue(0);
            series.TrackValue(-10);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            manager.StopAggregators(MetricAggregationCycleKind.Custom, stepTS);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: -8.19, max: 2.4, min: -8.19, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);

                if (seriesConfig.RequiresPersistentAggregation)
                {
                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: -8.19, max: 2.4, min: -8.19, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }
                else
                {
                    Assert.IsNull(aggregate);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);

                {
                    Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: -8.19, max: 2.4, min: -8.19, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }
            }

            Util.CompleteDefaultAggregationCycle(manager);
        }
        public void GetCurrentAggregateUnsafe_MetricAggregationCycleKind_DateTimeOffset_Measurement()
        {
            IMetricSeriesConfiguration seriesConfig = new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: false);
            const string aggregationKindMoniker     = "Microsoft.Azure.Measurement";

            // Do not start this test in the last 10 secs or first 2 secs of a minute, to make sure the timings below are likely to work out.

            while (DateTimeOffset.Now.Second >= 49 || DateTimeOffset.Now.Second < 3)
            {
                Thread.Sleep(TimeSpan.FromMilliseconds(500));
            }

            DateTimeOffset startTS = DateTimeOffset.Now;

            var          aggregateCollector = new MemoryMetricTelemetryPipeline();
            var          manager            = new MetricManager(aggregateCollector);
            MetricSeries series             = manager.CreateNewSeries("Foo Bar", seriesConfig);

            DateTimeOffset stepTS        = startTS.AddMinutes(2);
            DateTimeOffset stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);
                Assert.IsNull(aggregate);

                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);
                Assert.IsNull(aggregate);

                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);
                Assert.IsNull(aggregate);
            }

            series.TrackValue(0.4);
            series.TrackValue(2);
            series.TrackValue(-2);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);


            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 3, sum: 0.4, max: 2, min: -2, stdDev: 1.64384373412506, aggKindMoniker: aggregationKindMoniker);

                // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);

                Assert.AreEqual(
                    (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                    aggregate.AggregationPeriodDuration.TotalMilliseconds);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);
                    Assert.IsNull(aggregate);

                    aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);
                    Assert.IsNull(aggregate);
                }
            }

            DateTimeOffset customCycleStartTS = stepTS.AddMinutes(1);

            manager.StartOrCycleAggregators(MetricAggregationCycleKind.Custom, customCycleStartTS, futureFilter: null);

            series.TrackValue(0.17);
            series.TrackValue(0.32);
            series.TrackValue(-0.15);
            series.TrackValue(1.07);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 7, sum: 1.81, max: 2, min: -2, stdDev: 1.13330652229191, aggKindMoniker: aggregationKindMoniker);

                // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);

                Assert.AreEqual(
                    (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                    aggregate.AggregationPeriodDuration.TotalMilliseconds);


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 4, sum: 1.41, max: 1.07, min: -0.15, stdDev: 0.447681527427702, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.

                    Assert.AreEqual(
                        new DateTimeOffset(customCycleStartTS.Year, customCycleStartTS.Month, customCycleStartTS.Day, customCycleStartTS.Hour, customCycleStartTS.Minute, customCycleStartTS.Second, 0, customCycleStartTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }

                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);
                    Assert.IsNull(aggregate);
                }
            }

            Thread.Sleep(1500);
            DateTimeOffset flushTS = DateTimeOffset.Now;

            manager.Flush();


            DateTimeOffset quickPulseCycleStartTS = stepTS.AddMinutes(1);

            manager.StartOrCycleAggregators(MetricAggregationCycleKind.QuickPulse, quickPulseCycleStartTS, futureFilter: null);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);
                    Assert.IsNull(aggregate);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    // Custom was not cycled by Flush.
                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 4, sum: 1.41, max: 1.07, min: -0.15, stdDev: 0.447681527427702, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(customCycleStartTS.Year, customCycleStartTS.Month, customCycleStartTS.Day, customCycleStartTS.Hour, customCycleStartTS.Minute, customCycleStartTS.Second, 0, customCycleStartTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);

                    manager.StartOrCycleAggregators(MetricAggregationCycleKind.Custom, flushTS, null);

                    aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);
                    Assert.IsNull(aggregate);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    // We started QP cycle now, but we did not write values since then.
                    Assert.IsNull(aggregate);
                }
            }

            series.TrackValue(0);

            stepTS        = stepTS.AddMinutes(2);
            stepTSRounded = new DateTimeOffset(stepTS.Year, stepTS.Month, stepTS.Day, stepTS.Hour, stepTS.Minute, stepTS.Second, 0, stepTS.Offset);

            manager.StopAggregators(MetricAggregationCycleKind.Custom, stepTS);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Default, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 1, sum: 0, max: 0, min: 0, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(flushTS.Year, flushTS.Month, flushTS.Day, flushTS.Hour, flushTS.Minute, flushTS.Second, 0, flushTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.Custom, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    Assert.IsNull(aggregate);
                }


                aggregate = series.GetCurrentAggregateUnsafe(MetricAggregationCycleKind.QuickPulse, stepTS);

                {
                    Assert.IsFalse(seriesConfig.RequiresPersistentAggregation);

                    Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 1, sum: 0, max: 0, min: 0, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                    // This might break: Second boundary might be crossed between snapping test and the aggregation timestamps. Try re-running.
                    Assert.AreEqual(
                        new DateTimeOffset(quickPulseCycleStartTS.Year, quickPulseCycleStartTS.Month, quickPulseCycleStartTS.Day, quickPulseCycleStartTS.Hour, quickPulseCycleStartTS.Minute, quickPulseCycleStartTS.Second, 0, quickPulseCycleStartTS.Offset),
                        aggregate.AggregationPeriodStart);

                    Assert.AreEqual(
                        (stepTSRounded - aggregate.AggregationPeriodStart).TotalMilliseconds,
                        aggregate.AggregationPeriodDuration.TotalMilliseconds);
                }
            }

            Util.CompleteDefaultAggregationCycle(manager);
        }
        public void GetCurrentAggregateUnsafe_Accumulator()
        {
            IMetricSeriesConfiguration seriesConfig = new MetricSeriesConfigurationForAccumulator(restrictToUInt32Values: false);
            const string aggregationKindMoniker     = "Microsoft.Azure.Accumulator";

            // Do not start this test in the last 10 secs or first 2 secs of a minute, to make sure the timings below are likely to work out.

            while (DateTimeOffset.Now.Second >= 49 || DateTimeOffset.Now.Second < 3)
            {
                Thread.Sleep(TimeSpan.FromMilliseconds(500));
            }

            DateTimeOffset startTS = DateTimeOffset.Now;

            var          aggregateCollector = new MemoryMetricTelemetryPipeline();
            var          manager            = new MetricManager(aggregateCollector);
            MetricSeries series             = manager.CreateNewSeries("Foo Bar", seriesConfig);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe();
                Assert.IsNull(aggregate);
            }

            series.TrackValue(0.4);
            series.TrackValue(2);
            series.TrackValue(-2);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe();
                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 0.4, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                // The following might break sometimes!
                // There is a little chance that second boundary is crossed between test TS and the aggregation timestamps are snapped.
                // rerun the test if it happens.

                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);
            }

            series.TrackValue(0.17);
            series.TrackValue(0.32);
            series.TrackValue(-0.15);
            series.TrackValue(1.07);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe();

                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                // The following might break sometimes!
                // There is a little chance that second boundary is crossed between test TS and the aggregation timestamps are snapped.
                // rerun the test if it happens.

                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);
            }

            Thread.Sleep(1500);
            DateTimeOffset flushTS = DateTimeOffset.Now;

            manager.Flush();
            {
                Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe();

                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                // The following might break sometimes!
                // There is a little chance that second boundary is crossed between test TS and the aggregation timestamps are snapped.
                // rerun the test if it happens.

                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);
            }

            series.TrackValue(0);

            {
                MetricAggregate aggregate = series.GetCurrentAggregateUnsafe();

                Assert.IsTrue(seriesConfig.RequiresPersistentAggregation);
                Util.ValidateNumericAggregateValues(aggregate, name: "Foo Bar", count: 0, sum: 1.81, max: 2.4, min: 0.4, stdDev: 0, aggKindMoniker: aggregationKindMoniker);

                // The following might break sometimes!
                // There is a little chance that second boundary is crossed between test TS and the aggregation timestamps are snapped.
                // rerun the test if it happens.

                Assert.AreEqual(
                    new DateTimeOffset(startTS.Year, startTS.Month, startTS.Day, startTS.Hour, startTS.Minute, startTS.Second, 0, startTS.Offset),
                    aggregate.AggregationPeriodStart);
            }

            Util.CompleteDefaultAggregationCycle(manager);
        }
Example #10
0
        public static void Reset_PersistentAggregator(IMetricSeriesAggregator aggregator, string aggregateKindMoniker)
        {
            Assert.AreEqual(MetricSeriesConfigurationForTestingAccumulatorBehavior.Constants.AggregateKindMoniker, aggregateKindMoniker);

            var startTS = new DateTimeOffset(2017, 9, 25, 17, 0, 0, TimeSpan.FromHours(-8));
            var endTS   = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));

            long periodStringDef   = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;
            long periodStringStart = (long)(endTS - startTS).TotalMilliseconds;

            int filterInvocationsCount = 0;

            MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);

            Assert.IsNull(aggregate);

            aggregator.Reset(startTS, valueFilter: null);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(10);
            aggregator.TrackValue(20);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            TestUtil.ValidateNumericAggregateValues(aggregate, ns: "", name: "null", count: 0, sum: 30.0, max: 30, min: 10.0, stdDev: 5.0, timestamp: startTS, periodMs: periodStringStart, aggKindMoniker: aggregateKindMoniker);
            Assert.AreEqual(0, filterInvocationsCount);

            aggregator.Reset(default(DateTimeOffset), valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(true); }));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(100);
            aggregator.TrackValue(200);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            TestUtil.ValidateNumericAggregateValues(aggregate, ns: "", name: "null", count: 0, sum: 300, max: 300, min: 100, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodStringDef, aggKindMoniker: aggregateKindMoniker);
            Assert.AreEqual(2, filterInvocationsCount);

            aggregator.Reset(startTS, valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(false); }));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(100);
            aggregator.TrackValue(200);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);
            Assert.AreEqual(4, filterInvocationsCount);

            aggregator.Reset(startTS, valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(true); }));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(100);
            aggregator.TrackValue(-200);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            TestUtil.ValidateNumericAggregateValues(aggregate, ns: "", name: "null", count: 0, sum: -100, max: 100, min: -100, stdDev: 0, timestamp: startTS, periodMs: periodStringStart, aggKindMoniker: aggregateKindMoniker);
            Assert.AreEqual(6, filterInvocationsCount);
        }