View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.beans.stats.collectors;
12  
13  import static org.junit.jupiter.api.Assertions.assertEquals;
14  import static org.junit.jupiter.api.Assertions.assertNotNull;
15  import static org.junit.jupiter.api.Assertions.assertTrue;
16  
17  import java.util.ArrayList;
18  import java.util.List;
19  
20  import org.jfree.data.xy.XYDataItem;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  import org.springframework.test.util.ReflectionTestUtils;
24  
25  import psiprobe.beans.stats.listeners.StatsCollectionEvent;
26  import psiprobe.beans.stats.listeners.StatsCollectionListener;
27  import psiprobe.model.stats.StatsCollection;
28  
29  /**
30   * Tests for {@link AbstractStatsCollectorBean}.
31   */
32  class AbstractStatsCollectorBeanTest {
33  
34    /** A minimal concrete implementation for testing. */
35    static class TestCollector extends AbstractStatsCollectorBean {
36      @Override
37      public void collect() throws Exception {
38        // no-op for testing
39      }
40  
41      // Expose protected methods for testing
42      long testBuildDeltaStats(String name, long value) throws InterruptedException {
43        return buildDeltaStats(name, value);
44      }
45  
46      long testBuildDeltaStats(String name, long value, long time) throws InterruptedException {
47        return buildDeltaStats(name, value, time);
48      }
49  
50      void testBuildAbsoluteStats(String name, long value) throws InterruptedException {
51        buildAbsoluteStats(name, value);
52      }
53  
54      void testBuildAbsoluteStats(String name, long value, long time) throws InterruptedException {
55        buildAbsoluteStats(name, value, time);
56      }
57  
58      void testBuildTimePercentageStats(String name, long value, long time)
59          throws InterruptedException {
60        buildTimePercentageStats(name, value, time);
61      }
62    }
63  
64    /** A test listener to capture events. */
65    static class TestListener implements StatsCollectionListener {
66      List<StatsCollectionEvent> events = new ArrayList<>();
67      boolean enabled = true;
68  
69      @Override
70      public boolean isEnabled() {
71        return enabled;
72      }
73  
74      @Override
75      public void statsCollected(StatsCollectionEvent event) {
76        events.add(event);
77      }
78  
79      void reset() {
80        events.clear();
81      }
82    }
83  
84    private TestCollector collector;
85    private StatsCollection statsCollection;
86  
87    @BeforeEach
88    void setUp() {
89      collector = new TestCollector();
90      statsCollection = new StatsCollection();
91      ReflectionTestUtils.setField(collector, "statsCollection", statsCollection);
92      collector.setMaxSeries(10);
93    }
94  
95    @Test
96    void testSettersAndGetters() {
97      assertEquals(statsCollection, ReflectionTestUtils.getField(collector, "statsCollection"));
98      assertEquals(10, collector.getMaxSeries());
99    }
100 
101   @Test
102   void testBuildAbsoluteStatsFirstTime() throws InterruptedException {
103     // First call creates new stats (empty list is returned, not null)
104     collector.testBuildAbsoluteStats("test.stat", 100L);
105     assertNotNull(statsCollection.getStats("test.stat")); // first call creates an empty list
106   }
107 
108   @Test
109   void testBuildAbsoluteStatsSubsequentCalls() throws InterruptedException {
110     // First call creates the stats list
111     collector.testBuildAbsoluteStats("abs.stat", 100L);
112     // Second call adds data
113     collector.testBuildAbsoluteStats("abs.stat", 200L, System.currentTimeMillis());
114 
115     List<XYDataItem> stats = statsCollection.getStats("abs.stat");
116     assertNotNull(stats);
117     assertEquals(1, stats.size());
118     assertEquals(200L, stats.get(0).getY().longValue());
119   }
120 
121   @Test
122   void testBuildDeltaStats() throws InterruptedException {
123     statsCollection.newStats("delta.stat", 10);
124     // First call - delta is value - 0 = value
125     long delta1 = collector.testBuildDeltaStats("delta.stat", 100L);
126     assertEquals(100L, delta1);
127 
128     // Second call - delta is new - previous
129     long delta2 = collector.testBuildDeltaStats("delta.stat", 150L);
130     assertEquals(50L, delta2);
131   }
132 
133   @Test
134   void testBuildDeltaStatsNegativeDeltaClampsToZero() throws InterruptedException {
135     statsCollection.newStats("neg.stat", 10);
136     collector.testBuildDeltaStats("neg.stat", 100L);
137     // decrease should give 0
138     long delta = collector.testBuildDeltaStats("neg.stat", 50L);
139     assertEquals(0L, delta);
140   }
141 
142   @Test
143   void testBuildAbsoluteStatsWithListener() throws InterruptedException {
144     TestListener listener = new TestListener();
145     List<StatsCollectionListener> listeners = new ArrayList<>();
146     listeners.add(listener);
147     collector.setListeners(listeners);
148 
149     // First call creates stats
150     collector.testBuildAbsoluteStats("listen.stat", 100L);
151     // Second call triggers listener
152     collector.testBuildAbsoluteStats("listen.stat", 200L, System.currentTimeMillis());
153 
154     assertEquals(1, listener.events.size());
155     assertEquals("listen.stat", listener.events.get(0).getName());
156   }
157 
158   @Test
159   void testBuildAbsoluteStatsWithDisabledListener() throws InterruptedException {
160     TestListener listener = new TestListener();
161     listener.enabled = false;
162     List<StatsCollectionListener> listeners = new ArrayList<>();
163     listeners.add(listener);
164     collector.setListeners(listeners);
165 
166     collector.testBuildAbsoluteStats("dl.stat", 100L);
167     collector.testBuildAbsoluteStats("dl.stat", 200L, System.currentTimeMillis());
168 
169     // disabled listener should not receive events
170     assertEquals(0, listener.events.size());
171   }
172 
173   @Test
174   void testBuildTimePercentageStats() throws InterruptedException {
175     statsCollection.newStats("time.stat", 10);
176     long now = System.currentTimeMillis();
177 
178     // First call - stores baseline
179     collector.testBuildTimePercentageStats("time.stat", 0L, now);
180     // Second call - calculates percentage
181     collector.testBuildTimePercentageStats("time.stat", 500L, now + 1000L);
182 
183     // Should have created stats and added data
184     assertTrue(statsCollection.isCollected("time.stat"));
185   }
186 
187   @Test
188   void testMaxSeriesHousekeeping() throws InterruptedException {
189     collector.setMaxSeries(3);
190     statsCollection.newStats("hs.stat", 3);
191     // Add more than maxSeries entries via second call (first call creates)
192     long time = System.currentTimeMillis();
193     for (int i = 0; i < 6; i++) {
194       collector.testBuildAbsoluteStats("hs.stat", i * 100L, time + i * 1000L);
195     }
196     List<XYDataItem> stats = statsCollection.getStats("hs.stat");
197     // housekeeping should trim to maxSeries
198     assertTrue(stats.size() <= 3);
199   }
200 
201   @Test
202   void testNullStatsCollection() throws InterruptedException {
203     ReflectionTestUtils.setField(collector, "statsCollection", null);
204     // Should not throw
205     long delta = collector.testBuildDeltaStats("x", 100L);
206     assertEquals(0L, delta);
207   }
208 }