|
| 1 | +using Microsoft.Diagnostics.Tracing.Stacks; |
| 2 | +using Xunit; |
| 3 | + |
| 4 | +namespace TraceEventTests.Stacks |
| 5 | +{ |
| 6 | + public class HistogramTests |
| 7 | + { |
| 8 | + /// <summary> |
| 9 | + /// Test for the bug where AddMetric doesn't properly transition from single-bucket to array mode. |
| 10 | + /// After creating the array, subsequent calls to AddMetric with the original bucket should update |
| 11 | + /// the array, not the m_singleBucketValue. |
| 12 | + /// </summary> |
| 13 | + [Fact] |
| 14 | + public void AddMetric_TransitionToArrayMode_OriginalBucketUpdatesArray() |
| 15 | + { |
| 16 | + // Create a simple histogram controller for testing |
| 17 | + var stackSource = new CopyStackSource(); |
| 18 | + var callTree = new CallTree(ScalingPolicyKind.TimeMetric) |
| 19 | + { |
| 20 | + StackSource = stackSource |
| 21 | + }; |
| 22 | + var controller = new TimeHistogramController(callTree, 0, 100); |
| 23 | + var histogram = new Histogram(controller); |
| 24 | + |
| 25 | + // Step 1: Add metric to bucket 0 (single bucket mode) |
| 26 | + histogram.AddMetric(1.0f, 0); |
| 27 | + Assert.Equal(1.0f, histogram[0]); |
| 28 | + |
| 29 | + // Step 2: Add metric to bucket 1 (should trigger array creation) |
| 30 | + histogram.AddMetric(2.0f, 1); |
| 31 | + Assert.Equal(1.0f, histogram[0]); // Bucket 0 should still have 1.0 |
| 32 | + Assert.Equal(2.0f, histogram[1]); // Bucket 1 should have 2.0 |
| 33 | + |
| 34 | + // Step 3: Add metric to bucket 0 again (this is where the bug manifests) |
| 35 | + // The value should be added to the array, not to m_singleBucketValue |
| 36 | + histogram.AddMetric(3.0f, 0); |
| 37 | + Assert.Equal(4.0f, histogram[0]); // Bucket 0 should now have 1.0 + 3.0 = 4.0 |
| 38 | + Assert.Equal(2.0f, histogram[1]); // Bucket 1 should still have 2.0 |
| 39 | + } |
| 40 | + |
| 41 | + /// <summary> |
| 42 | + /// Test that multiple buckets work correctly after transitioning to array mode. |
| 43 | + /// </summary> |
| 44 | + [Fact] |
| 45 | + public void AddMetric_MultipleBuckets_AllValuesCorrect() |
| 46 | + { |
| 47 | + var stackSource = new CopyStackSource(); |
| 48 | + var callTree = new CallTree(ScalingPolicyKind.TimeMetric) |
| 49 | + { |
| 50 | + StackSource = stackSource |
| 51 | + }; |
| 52 | + var controller = new TimeHistogramController(callTree, 0, 100); |
| 53 | + var histogram = new Histogram(controller); |
| 54 | + |
| 55 | + // Add to multiple buckets in various order |
| 56 | + histogram.AddMetric(10.0f, 5); |
| 57 | + histogram.AddMetric(20.0f, 10); |
| 58 | + histogram.AddMetric(30.0f, 5); // Add more to bucket 5 |
| 59 | + histogram.AddMetric(40.0f, 15); |
| 60 | + histogram.AddMetric(50.0f, 10); // Add more to bucket 10 |
| 61 | + |
| 62 | + // Verify all values are correct |
| 63 | + Assert.Equal(0.0f, histogram[0]); |
| 64 | + Assert.Equal(40.0f, histogram[5]); // 10 + 30 |
| 65 | + Assert.Equal(70.0f, histogram[10]); // 20 + 50 |
| 66 | + Assert.Equal(40.0f, histogram[15]); |
| 67 | + } |
| 68 | + |
| 69 | + /// <summary> |
| 70 | + /// Test single bucket mode (when only one bucket is ever used). |
| 71 | + /// </summary> |
| 72 | + [Fact] |
| 73 | + public void AddMetric_SingleBucketOnly_ValuesCorrect() |
| 74 | + { |
| 75 | + var stackSource = new CopyStackSource(); |
| 76 | + var callTree = new CallTree(ScalingPolicyKind.TimeMetric) |
| 77 | + { |
| 78 | + StackSource = stackSource |
| 79 | + }; |
| 80 | + var controller = new TimeHistogramController(callTree, 0, 100); |
| 81 | + var histogram = new Histogram(controller); |
| 82 | + |
| 83 | + // Add multiple values to the same bucket |
| 84 | + histogram.AddMetric(1.0f, 7); |
| 85 | + histogram.AddMetric(2.0f, 7); |
| 86 | + histogram.AddMetric(3.0f, 7); |
| 87 | + |
| 88 | + // Verify the value accumulated correctly |
| 89 | + Assert.Equal(6.0f, histogram[7]); |
| 90 | + Assert.Equal(0.0f, histogram[0]); |
| 91 | + Assert.Equal(0.0f, histogram[10]); |
| 92 | + } |
| 93 | + |
| 94 | + /// <summary> |
| 95 | + /// Test AddScaled works correctly after histogram transition to array mode. |
| 96 | + /// </summary> |
| 97 | + [Fact] |
| 98 | + public void AddScaled_AfterArrayTransition_ValuesCorrect() |
| 99 | + { |
| 100 | + var stackSource = new CopyStackSource(); |
| 101 | + var callTree = new CallTree(ScalingPolicyKind.TimeMetric) |
| 102 | + { |
| 103 | + StackSource = stackSource |
| 104 | + }; |
| 105 | + var controller = new TimeHistogramController(callTree, 0, 100); |
| 106 | + |
| 107 | + // Create source histogram with multiple buckets |
| 108 | + var sourceHistogram = new Histogram(controller); |
| 109 | + sourceHistogram.AddMetric(10.0f, 5); |
| 110 | + sourceHistogram.AddMetric(20.0f, 10); |
| 111 | + |
| 112 | + // Create target histogram that starts in single-bucket mode |
| 113 | + var targetHistogram = new Histogram(controller); |
| 114 | + targetHistogram.AddMetric(5.0f, 5); |
| 115 | + |
| 116 | + // Add scaled source to target - this should transition target to array mode |
| 117 | + targetHistogram.AddScaled(sourceHistogram); |
| 118 | + |
| 119 | + // Verify values |
| 120 | + Assert.Equal(15.0f, targetHistogram[5]); // 5 + 10 |
| 121 | + Assert.Equal(20.0f, targetHistogram[10]); // 0 + 20 |
| 122 | + } |
| 123 | + } |
| 124 | +} |
0 commit comments