1+ package tslib .movingaverage ;
2+
3+ import org .junit .Test ;
4+ import org .junit .Before ;
5+ import static org .junit .Assert .*;
6+
7+ import java .util .Arrays ;
8+ import java .util .List ;
9+ import java .util .ArrayList ;
10+
11+ /**
12+ * Comprehensive unit tests for WeightedMovingAverage.
13+ * Tests edge cases, mathematical correctness, and API behavior.
14+ *
15+ * Author: navdeep
16+ */
17+ public class WeightedMovingAverageTest {
18+
19+ private WeightedMovingAverage wma ;
20+
21+ @ Before
22+ public void setUp () {
23+ wma = new WeightedMovingAverage (5 );
24+ }
25+
26+ @ Test
27+ public void testConstructorWithValidPeriod () {
28+ WeightedMovingAverage wma3 = new WeightedMovingAverage (3 );
29+ WeightedMovingAverage wma10 = new WeightedMovingAverage (10 );
30+ assertNotNull (wma3 );
31+ assertNotNull (wma10 );
32+ }
33+
34+ @ Test (expected = IllegalArgumentException .class )
35+ public void testConstructorWithInvalidPeriodZero () {
36+ new WeightedMovingAverage (0 );
37+ }
38+
39+ @ Test (expected = IllegalArgumentException .class )
40+ public void testConstructorWithInvalidPeriodNegative () {
41+ new WeightedMovingAverage (-1 );
42+ }
43+
44+ @ Test (expected = IllegalArgumentException .class )
45+ public void testConstructorWithInvalidPeriodNegativeFive () {
46+ new WeightedMovingAverage (-5 );
47+ }
48+
49+ @ Test
50+ public void testEmptyInput () {
51+ List <Double > result = wma .compute (new ArrayList <>());
52+ assertTrue (result .isEmpty ());
53+ }
54+
55+ @ Test
56+ public void testNullInput () {
57+ List <Double > result = wma .compute (null );
58+ assertTrue (result .isEmpty ());
59+ }
60+
61+ @ Test
62+ public void testSingleValue () {
63+ List <Double > data = Arrays .asList (10.0 );
64+ List <Double > result = wma .compute (data );
65+
66+ assertEquals (1 , result .size ());
67+ assertNull (result .get (0 )); // Not enough data for period 5
68+ }
69+
70+ @ Test
71+ public void testInsufficientData () {
72+ List <Double > data = Arrays .asList (1.0 , 2.0 , 3.0 , 4.0 );
73+ List <Double > result = wma .compute (data );
74+
75+ assertEquals (4 , result .size ());
76+ assertNull (result .get (0 ));
77+ assertNull (result .get (1 ));
78+ assertNull (result .get (2 ));
79+ assertNull (result .get (3 ));
80+ }
81+
82+ @ Test
83+ public void testExactPeriodData () {
84+ // Test with exactly 5 values: [1, 2, 3, 4, 5]
85+ // Weights: [1, 2, 3, 4, 5]
86+ // Weighted sum: 1*1 + 2*2 + 3*3 + 4*4 + 5*5 = 1 + 4 + 9 + 16 + 25 = 55
87+ // Weight sum: 1 + 2 + 3 + 4 + 5 = 15
88+ // Expected: 55/15 = 3.666...
89+ List <Double > data = Arrays .asList (1.0 , 2.0 , 3.0 , 4.0 , 5.0 );
90+ List <Double > result = wma .compute (data );
91+
92+ assertEquals (5 , result .size ());
93+ assertNull (result .get (0 ));
94+ assertNull (result .get (1 ));
95+ assertNull (result .get (2 ));
96+ assertNull (result .get (3 ));
97+ assertEquals (55.0 /15.0 , result .get (4 ), 1e-10 );
98+ }
99+
100+ @ Test
101+ public void testPeriod3Calculation () {
102+ WeightedMovingAverage wma3 = new WeightedMovingAverage (3 );
103+ // Test with [1, 2, 3]
104+ // Weights: [1, 2, 3]
105+ // Weighted sum: 1*1 + 2*2 + 3*3 = 1 + 4 + 9 = 14
106+ // Weight sum: 1 + 2 + 3 = 6
107+ // Expected: 14/6 = 2.333...
108+ List <Double > data = Arrays .asList (1.0 , 2.0 , 3.0 );
109+ List <Double > result = wma3 .compute (data );
110+
111+ assertEquals (3 , result .size ());
112+ assertNull (result .get (0 ));
113+ assertNull (result .get (1 ));
114+ assertEquals (14.0 /6.0 , result .get (2 ), 1e-10 );
115+ }
116+
117+ @ Test
118+ public void testSlidingWindow () {
119+ // Test sliding window behavior
120+ // Data: [1, 2, 3, 4, 5, 6, 7]
121+ // Window 1: [1, 2, 3, 4, 5] -> (1*1 + 2*2 + 3*3 + 4*4 + 5*5) / 15 = 55/15
122+ // Window 2: [2, 3, 4, 5, 6] -> (2*1 + 3*2 + 4*3 + 5*4 + 6*5) / 15 = 70/15
123+ // Window 3: [3, 4, 5, 6, 7] -> (3*1 + 4*2 + 5*3 + 6*4 + 7*5) / 15 = 85/15
124+ List <Double > data = Arrays .asList (1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0 );
125+ List <Double > result = wma .compute (data );
126+
127+ assertEquals (7 , result .size ());
128+ assertNull (result .get (0 ));
129+ assertNull (result .get (1 ));
130+ assertNull (result .get (2 ));
131+ assertNull (result .get (3 ));
132+ assertEquals (55.0 /15.0 , result .get (4 ), 1e-10 );
133+ assertEquals (70.0 /15.0 , result .get (5 ), 1e-10 );
134+ assertEquals (85.0 /15.0 , result .get (6 ), 1e-10 );
135+ }
136+
137+ @ Test
138+ public void testConstantValues () {
139+ // Test with constant values - WMA should equal the constant
140+ List <Double > data = Arrays .asList (5.0 , 5.0 , 5.0 , 5.0 , 5.0 );
141+ List <Double > result = wma .compute (data );
142+
143+ assertEquals (5 , result .size ());
144+ assertNull (result .get (0 ));
145+ assertNull (result .get (1 ));
146+ assertNull (result .get (2 ));
147+ assertNull (result .get (3 ));
148+ assertEquals (5.0 , result .get (4 ), 1e-10 );
149+ }
150+
151+ @ Test
152+ public void testIncreasingValues () {
153+ // Test with strictly increasing values
154+ // Data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
155+ List <Double > data = Arrays .asList (1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0 , 8.0 , 9.0 , 10.0 );
156+ List <Double > result = wma .compute (data );
157+
158+ assertEquals (10 , result .size ());
159+ // Check that WMA values are increasing (trend following)
160+ assertTrue (result .get (4 ) < result .get (5 ));
161+ assertTrue (result .get (5 ) < result .get (6 ));
162+ assertTrue (result .get (6 ) < result .get (7 ));
163+ assertTrue (result .get (7 ) < result .get (8 ));
164+ assertTrue (result .get (8 ) < result .get (9 ));
165+ }
166+
167+ @ Test
168+ public void testDecreasingValues () {
169+ // Test with strictly decreasing values
170+ // Data: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
171+ List <Double > data = Arrays .asList (10.0 , 9.0 , 8.0 , 7.0 , 6.0 , 5.0 , 4.0 , 3.0 , 2.0 , 1.0 );
172+ List <Double > result = wma .compute (data );
173+
174+ assertEquals (10 , result .size ());
175+ // Check that WMA values are decreasing (trend following)
176+ assertTrue (result .get (4 ) > result .get (5 ));
177+ assertTrue (result .get (5 ) > result .get (6 ));
178+ assertTrue (result .get (6 ) > result .get (7 ));
179+ assertTrue (result .get (7 ) > result .get (8 ));
180+ assertTrue (result .get (8 ) > result .get (9 ));
181+ }
182+
183+ @ Test
184+ public void testResetFunctionality () {
185+ // Test that reset works correctly
186+ List <Double > data1 = Arrays .asList (1.0 , 2.0 , 3.0 , 4.0 , 5.0 );
187+ List <Double > result1 = wma .compute (data1 );
188+
189+ // Reset and compute with different data
190+ wma .reset ();
191+ List <Double > data2 = Arrays .asList (10.0 , 20.0 , 30.0 , 40.0 , 50.0 );
192+ List <Double > result2 = wma .compute (data2 );
193+
194+ // Results should be different
195+ assertNotEquals (result1 .get (4 ), result2 .get (4 ));
196+ }
197+
198+ @ Test
199+ public void testAddMethod () {
200+ // Test the add method directly
201+ wma .add (1.0 );
202+ wma .add (2.0 );
203+ wma .add (3.0 );
204+ wma .add (4.0 );
205+ wma .add (5.0 );
206+
207+ // Should have 5 values now
208+ assertEquals (5 , wma .compute (Arrays .asList (1.0 , 2.0 , 3.0 , 4.0 , 5.0 )).size ());
209+ }
210+
211+ @ Test
212+ public void testLargePeriod () {
213+ // Test with a larger period
214+ WeightedMovingAverage wma10 = new WeightedMovingAverage (10 );
215+ List <Double > data = new ArrayList <>();
216+ for (int i = 1 ; i <= 15 ; i ++) {
217+ data .add ((double ) i );
218+ }
219+
220+ List <Double > result = wma10 .compute (data );
221+ assertEquals (15 , result .size ());
222+
223+ // First 9 should be null
224+ for (int i = 0 ; i < 9 ; i ++) {
225+ assertNull (result .get (i ));
226+ }
227+
228+ // 10th should have a value
229+ assertNotNull (result .get (9 ));
230+ }
231+
232+ @ Test
233+ public void testPrecision () {
234+ // Test precision with decimal values
235+ List <Double > data = Arrays .asList (1.1 , 2.2 , 3.3 , 4.4 , 5.5 );
236+ List <Double > result = wma .compute (data );
237+
238+ // Manual calculation:
239+ // Weights: [1, 2, 3, 4, 5]
240+ // Weighted sum: 1.1*1 + 2.2*2 + 3.3*3 + 4.4*4 + 5.5*5 = 1.1 + 4.4 + 9.9 + 17.6 + 27.5 = 60.5
241+ // Weight sum: 15
242+ // Expected: 60.5/15 = 4.033...
243+ assertEquals (60.5 /15.0 , result .get (4 ), 1e-10 );
244+ }
245+
246+ @ Test
247+ public void testNegativeValues () {
248+ // Test with negative values
249+ List <Double > data = Arrays .asList (-1.0 , -2.0 , -3.0 , -4.0 , -5.0 );
250+ List <Double > result = wma .compute (data );
251+
252+ // Manual calculation:
253+ // Weights: [1, 2, 3, 4, 5]
254+ // Weighted sum: -1*1 + -2*2 + -3*3 + -4*4 + -5*5 = -1 - 4 - 9 - 16 - 25 = -55
255+ // Weight sum: 15
256+ // Expected: -55/15 = -3.666...
257+ assertEquals (-55.0 /15.0 , result .get (4 ), 1e-10 );
258+ }
259+
260+ @ Test
261+ public void testMixedPositiveNegative () {
262+ // Test with mixed positive and negative values
263+ List <Double > data = Arrays .asList (-1.0 , 2.0 , -3.0 , 4.0 , -5.0 );
264+ List <Double > result = wma .compute (data );
265+
266+ // Manual calculation:
267+ // Weights: [1, 2, 3, 4, 5]
268+ // Weighted sum: -1*1 + 2*2 + -3*3 + 4*4 + -5*5 = -1 + 4 - 9 + 16 - 25 = -15
269+ // Weight sum: 15
270+ // Expected: -15/15 = -1.0
271+ assertEquals (-1.0 , result .get (4 ), 1e-10 );
272+ }
273+ }
0 commit comments