JAL-1889 additional test for clover-added fields
[jalview.git] / test / jalview / viewmodel / styles / ViewStyleTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.viewmodel.styles;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertTrue;
26
27 import jalview.gui.JvOptionPane;
28
29 import java.awt.Color;
30 import java.lang.reflect.Field;
31 import java.util.Random;
32
33 import org.testng.AssertJUnit;
34 import org.testng.annotations.BeforeClass;
35 import org.testng.annotations.Test;
36
37 public class ViewStyleTest
38 {
39
40   @BeforeClass(alwaysRun = true)
41   public void setUpJvOptionPane()
42   {
43     JvOptionPane.setInteractiveMode(false);
44     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
45   }
46
47   Random r = new Random();
48
49   /**
50    * This test uses reflection to set all fields on a ViewStyle, make a copy of
51    * it, and verify all fields match. This test should fail if a getter/setter
52    * pair are added to the class but missing in the copy constructor. Using
53    * reflection in the copy constructor itself is broken by obfuscation when the
54    * applet is built.
55    * 
56    * To prove this test works, simply comment out a line in the ViewStyle copy
57    * constructor, or add a new member field to ViewStyle.
58    * 
59    * @throws IllegalAccessException
60    * @throws IllegalArgumentException
61    */
62   @Test(groups = { "Functional" })
63   public void testCopyConstructor() throws IllegalArgumentException,
64           IllegalAccessException
65   {
66     ViewStyle vs1 = new ViewStyle();
67     Field[] fields = ViewStyle.class.getDeclaredFields();
68     for (Field field : fields)
69     {
70       field.setAccessible(true);
71       if (!copyConstructorIgnores(field))
72       {
73         changeValue(vs1, field);
74       }
75     }
76
77     ViewStyle vs2 = new ViewStyle(vs1);
78
79     for (Field field1 : fields)
80     {
81       if (!copyConstructorIgnores(field1))
82       {
83         final Object value1 = field1.get(vs1);
84         final Object value2 = field1.get(vs2);
85         String msg = "Mismatch in " + field1.getName() + "(" + value1 + "/"
86               + value2 + ") - not set in copy constructor?";
87         assertEquals(msg, value1, value2);
88       }
89     }
90     assertEquals("Hashcode not equals", vs1.hashCode(), vs2.hashCode());
91   }
92
93   /**
94    * Add tests here for any fields that we expect to be ignored by 
95    * the copy constructor
96    * 
97    * @param field
98    * @return
99    */
100   private boolean copyConstructorIgnores(Field field)
101   {
102     /*
103      * ignore instrumentation added by jacoco for test coverage
104      */
105     if (field.isSynthetic())
106     {
107       return true;
108     }
109     if (field.getType().toString().contains("com_atlassian_clover"))
110     {
111       return true;
112     }
113
114     return false;
115   }
116   
117   /**
118   * Change the value of one field in a ViewStyle object
119   * 
120   * @param vs
121   * @param field
122   * @throws IllegalAccessException
123   */
124   protected void changeValue(ViewStyle vs, Field field)
125           throws IllegalAccessException
126   {
127     Class<?> type = field.getType();
128
129     if (type.equals(boolean.class) || type.equals(Boolean.class))
130     {
131       boolean value = (Boolean) field.get(vs);
132       // System.out.println("Setting " + field.getName() + " to " + !value);
133       field.set(vs, !value);
134     }
135     else if (type.equals(short.class) || type.equals(int.class)
136             || type.equals(long.class) || type.equals(float.class)
137             || type.equals(double.class))
138     {
139       final int value = (int) (1 + field.getDouble(vs));
140       // System.out.println("Setting " + field.getName() + " to " + value);
141       field.set(vs, value);
142     }
143     else if (type.equals(Integer.class))
144     {
145       field.set(vs, (int) (1 + getNumberValue(field, vs)));
146     }
147     else if (type.equals(Float.class))
148     {
149       field.set(vs, (float) (1f + getNumberValue(field, vs)));
150     }
151     else if (type.equals(Long.class))
152     {
153       field.set(vs, (long) (1L + getNumberValue(field, vs)));
154     }
155     else if (type.equals(Double.class))
156     {
157       field.set(vs, 1d + getNumberValue(field, vs));
158     }
159     else if (type.equals(Short.class))
160     {
161       field.set(vs, (short) (1 + getNumberValue(field, vs)));
162     }
163     else if (type.equals(Byte.class))
164     {
165       field.set(vs, (byte) (1 + getNumberValue(field, vs)));
166     }
167     else if (type.equals(Character.class))
168     {
169       field.set(vs, (char) (1 + getNumberValue(field, vs)));
170     }
171     else if (type.equals(String.class))
172     {
173       field.set(vs, "Joe" + field.get(vs));
174     }
175     else if (type.equals(Color.class))
176     {
177       field.set(vs, Color.RED.equals(field.get(vs)) ? Color.BLACK
178               : Color.RED);
179     }
180     else
181     {
182       AssertJUnit.fail("Unhandled field type (add to test): "
183               + field.getName() + ":" + type);
184     }
185   }
186
187   private double getNumberValue(Field field, ViewStyle vs)
188           throws IllegalArgumentException, IllegalAccessException
189   {
190     if (field.get(vs) == null)
191     {
192       return 0d;
193     }
194     return ((Number) field.get(vs)).doubleValue();
195   }
196
197   /**
198    * Test that the equals method compares every field by changing them one by
199    * one in a cloned ViewStyle.
200    * 
201    * This test will fail if a new field is added to ViewStyle but not to the
202    * comparisons in ViewStyle.equals().
203    * 
204    * To confirm that this test works, temporarily comment out one of the field
205    * comparisons in ViewStyle.equals()
206    * 
207    * @throws IllegalAccessException
208    * @throws IllegalArgumentException
209    */
210   @Test(groups = { "Functional" })
211   public void testEquals() throws IllegalArgumentException,
212           IllegalAccessException
213   {
214     ViewStyle vs1 = new ViewStyle();
215     ViewStyle vs2 = new ViewStyle(vs1);
216
217     assertFalse(vs1.equals(null));
218     assertFalse(vs1.equals(this));
219     assertTrue(vs1.equals(vs2));
220     assertTrue(vs2.equals(vs1));
221
222     Field[] fields = ViewStyle.class.getDeclaredFields();
223     for (Field field : fields)
224     {
225       if (!copyConstructorIgnores(field))
226       {
227         field.setAccessible(true);
228         Object oldValue = field.get(vs2);
229         changeValue(vs2, field);
230         assertFalse("equals method ignores " + field.getName(),
231               vs1.equals(vs2));
232
233         if (vs1.hashCode() == vs2.hashCode())
234         {
235           // uncomment next line to see which fields hashCode ignores
236           // System.out.println("hashCode ignores " + field.getName());
237         }
238         // restore original value before testing the next field
239         field.set(vs2, oldValue);
240       }
241     }
242   }
243 }