View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.component;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.List;
24  
25  import javax.faces.component.EditableValueHolder;
26  import javax.faces.component.StateHolder;
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.FacesContext;
30  import javax.faces.el.EvaluationException;
31  import javax.faces.el.PropertyNotFoundException;
32  import javax.faces.el.ValueBinding;
33  import javax.faces.render.Renderer;
34  
35  import junit.framework.AssertionFailedError;
36  import junit.framework.Test;
37  import junit.framework.TestSuite;
38  
39  import junit.textui.TestRunner;
40  
41  import org.apache.myfaces.trinidad.model.ModelUtils;
42  import org.apache.myfaces.trinidad.model.SortableModel;
43  
44  /**
45   * Unit tests for UIXTable
46   *
47   */
48  public class UIXTableTest extends UIComponentTestCase
49  {
50    /**
51     * @param testName  the unit test name
52     */
53    public UIXTableTest(
54      String testName)
55    {
56      super(testName);
57    }
58    
59    @Override
60    protected void setUp() throws Exception
61    {
62      super.setUp();
63    }
64    
65    @Override
66    protected void tearDown() throws Exception
67    {
68      super.tearDown();
69    }
70    
71    public static Test suite()
72    {
73      return new TestSuite(UIXTableTest.class);
74    }
75  
76    /**
77     * Tests the initial values for the component attributes.
78     */
79    public void testInitialAttributeValues()
80    {
81      UIXTable table = _createTable();
82      assertEquals(25, table.getRows());
83      assertEquals(0, table.getFirst());
84      assertFalse(table.isImmediate());
85    }
86  
87    /**
88     * Tests the transparency of the component attribute by comparing
89     * bean accessor and mutator methods with attribute map accessor
90     * and mutator methods.
91     */
92    public void testAttributeTransparency()
93    {
94      UIXTable table = _createTable();
95      doTestAttributeTransparency(table, "var", "row", "emp");
96      doTestAttributeTransparency(table, "value", "row", "emp");
97      doTestAttributeTransparency(table, "first", new Integer(0), new Integer(1));
98      doTestAttributeTransparency(table, "immediate", Boolean.TRUE, Boolean.FALSE);
99      doTestAttributeTransparency(table, "rows", new Integer(30), new Integer(10));
100   }
101 
102   public void testModelMethods()
103   {
104     TestTable table = _createTable();
105     SortableModel model = table.model;
106     int sz = model.getRowCount();
107     assertEquals(sz, table.getRowCount());
108     for(int i=0; i<sz; i++)
109     {
110       table.setRowIndex(i);
111       assertEquals(i, model.getRowIndex());
112       assertEquals(model.getRowKey(), table.getRowKey());
113       assertEquals(model.isRowAvailable(), table.isRowAvailable());
114       assertEquals(model.getRowData(), table.getRowData());
115     }
116     table.setRowIndex(-1);
117     assertEquals(-1, model.getRowIndex());
118     assertEquals(model.getRowKey(), table.getRowKey());
119     assertEquals(model.isRowAvailable(), table.isRowAvailable());
120   }
121 
122   public void testProcessDecodes()
123   {
124     TestTable table = _createTable();
125     table.setRows(10);
126     table.setFirst(3);
127     doTestApplyRequestValues(table);
128     _testColumnChild(table, table.column1Child.getDecodesRowData());
129     _testColumnChild(table, table.column2Child.getDecodesRowData());
130 
131     assertEquals(1, table.column1Header.getDecodesRowData().size());
132     assertEquals(1, table.column2Header.getDecodesRowData().size());
133 
134     List<Object> detailData = table.detailStamp.getDecodesRowData();
135     _testDetailStamp(table, detailData);
136 
137   }
138 
139   public void testProcessValidators()
140   {
141     TestTable table = _createTable();
142     table.setRows(10);
143     table.setFirst(3);
144     doTestProcessValidations(table);
145     _testColumnChild(table, table.column1Child.getValidatesRowData());
146     _testColumnChild(table, table.column2Child.getValidatesRowData());
147 
148     assertEquals(1, table.column1Header.getValidatesRowData().size());
149     assertEquals(1, table.column2Header.getValidatesRowData().size());
150 
151     List<Object> detailData = table.detailStamp.getValidatesRowData();
152     _testDetailStamp(table, detailData);
153 
154   }
155 
156   public void testProcessUpdates()
157   {
158     TestTable table = _createTable();
159     table.setRows(10);
160     table.setFirst(3);
161     doTestUpdateModelValues(table);
162     _testColumnChild(table, table.column1Child.getUpdatesRowData());
163     _testColumnChild(table, table.column2Child.getUpdatesRowData());
164 
165     assertEquals(1, table.column1Header.getUpdatesRowData().size());
166     assertEquals(1, table.column2Header.getUpdatesRowData().size());
167 
168     List<Object> detailData = table.detailStamp.getUpdatesRowData();
169     _testDetailStamp(table, detailData);
170 
171   }
172 
173   public void testEditableValueHolderChildren()
174   {
175     TestTable table = _createTable();
176     UIXInput testComp = table.column1Child;
177     UIXInput detailStamp = table.detailStamp;
178 
179     // initialize:
180     table.setRowIndex(0);
181     _setEVHData(testComp, "Foo", true);
182     _setEVHData(detailStamp, "Foo-ds", false);
183 
184     table.setRowIndex(1);
185     _setEVHData(testComp, "Bar", false);
186     _setEVHData(detailStamp, "Bar-ds", true);
187 
188     // now test:
189     table.setRowIndex(0);
190     _testEVHData(testComp, "Foo", true);
191     _testEVHData(detailStamp, "Foo-ds", false);
192 
193     table.setRowIndex(1);
194     _testEVHData(testComp, "Bar", false);
195     _testEVHData(detailStamp, "Bar-ds", true);
196 
197   }
198 
199 
200   public void testSaveRestoreState()
201   {
202     final Object state;
203     {
204       TestTable table = _createTable();
205 
206       UIXInput testComp = table.column1Child;
207 
208       // initialize:
209       table.setRowIndex(0);
210       _setEVHData(testComp, "Foo", true);
211 
212       table.setRowIndex(1);
213       _setEVHData(testComp, "Bar", false);
214 
215       // note that we did not set rowIndex back to -1.
216       // processSaveState should store the stamp data even though rowIndex was
217       // not changed.
218 
219       state = table.processSaveState(facesContext);
220     }
221 
222     TestTable table = _createTable();
223     UIXInput testComp = table.column1Child;
224     table.processRestoreState(facesContext, state);
225 
226     // now test:
227     table.setRowIndex(0);
228     _testEVHData(testComp, "Foo", true);
229 
230     table.setRowIndex(1);
231     _testEVHData(testComp, "Bar", false);
232 
233   }
234 
235   /**
236    * make sure that the model is not executed at invalid or unnecessary times.
237    * valueBindings cannot be called during restoreState.
238    * Also table model must not be executed if rendered="false".
239    * However, saveState is called even if rendered="false" on a component.
240    * Therefore, saveState should not call getValue() on the table.
241    */
242   public void testSaveRestoreStateGetValue()
243   {
244     // make sure that getValue() is not called during restoreState:
245     DoNotCallBinding doNotCall = new DoNotCallBinding();
246     doNotCall.doNotCall = true;
247     final Object state;
248     {
249       TestTable table = _createTable(false);
250       table.setValue(null); // instead use the valueBinding below:
251       table.setValueBinding("value", doNotCall);
252       state = table.processSaveState(facesContext);
253     }
254 
255     TestTable table = _createTable(false);
256     table.setValue(null); // instead use the valueBinding below:
257     // this value binding should be restored during processRestoreState;
258     // however, set it anyway just to catch any getValue() calls prior to
259     // that.
260     table.setValueBinding("value", doNotCall);
261     table.processRestoreState(facesContext, state);
262 
263     assertTrue(table.getValueBinding("value") instanceof DoNotCallBinding);
264 
265   }
266 
267   @SuppressWarnings("unchecked")
268   public void testNotRenderedSaveRestoreState()
269   {
270     // this test is to make sure no exceptions are thrown during save/restore
271     // state of the table. Exceptions were being thrown earlier because
272     // model and certain initial state were computed lazily.
273     final Object state;
274     {
275       UIXPanel panel = new UIXPanel();
276       TestTable table = _createTable(false);
277       table.setRendered(false);
278       panel.getChildren().add(table);
279       state = panel.processSaveState(facesContext);
280     }
281 
282     UIXPanel panel = new UIXPanel();
283     TestTable table = _createTable(false);
284     panel.getChildren().add(table);
285 
286     panel.processRestoreState(facesContext, state);
287 
288   }
289 
290   @SuppressWarnings("unchecked")
291   @Override
292   protected void doTestUpdateModelValues(
293     FacesContext context,
294     UIViewRoot   root,
295     UIComponent  component)
296   {
297     root.getChildren().add(component);
298     root.processUpdates(context);
299   }
300 
301   @SuppressWarnings("unchecked")
302   @Override
303   protected void doTestProcessValidations(
304     FacesContext context,
305     UIViewRoot   root,
306     UIComponent  component)
307   {
308     root.getChildren().add(component);
309     root.processValidators(context);
310   }
311 
312   @Override
313   public void setCurrentContext(FacesContext fc)
314   {
315     // prevent removal of facesContext before we are done testing:
316     if (fc != null)
317       super.setCurrentContext(fc);
318   }
319 
320   private void _setEVHData(
321     EditableValueHolder testComp,
322     String value,
323     boolean isValid)
324   {
325     testComp.setSubmittedValue("submitedValue-"+value);
326     testComp.setValue("Value-"+value);
327     testComp.setValid(isValid);
328   }
329 
330   private void _testEVHData(
331     EditableValueHolder testComp,
332     String value,
333     boolean isValid)
334   {
335     assertEquals("submitedValue-"+value, testComp.getSubmittedValue());
336     assertEquals("Value-"+value, testComp.getLocalValue());
337     assertEquals(isValid, testComp.isValid());
338   }
339 
340   private void _testDetailStamp(TestTable table, List<Object> detailData)
341   {
342     assertEquals(1, detailData.size());
343     table.setRowIndex(_DISCLOSED_INDEX);
344     assertEquals(table.getRowData(), detailData.get(0));
345   }
346 
347   private void _testColumnChild(TestTable table, List<Object> rowData)
348   {
349     // make sure that the rowData values that were seen during this phase
350     // were the correct values:
351     int rows = table.getRows();
352     assertEquals(rows, rowData.size());
353     int first = table.getFirst();
354     for(int i=0; i<rows; i++)
355     {
356       table.setRowIndex(i+first);
357       assertEquals(table.getRowData(), rowData.get(i));
358     }
359   }
360 
361   public static void main(String[] args)
362   {
363     TestRunner.run(UIXTableTest.class);
364 //    UIXTableTest test = new UIXTableTest("aasd");
365 //    test.testSaveRestoreStateGetValue();
366   }
367 
368   @SuppressWarnings("unchecked")
369   @Override
370   protected void doTestApplyRequestValues(
371     FacesContext context,
372     UIViewRoot   root,
373     UIComponent  component)
374   {
375     root.getChildren().add(component);
376     root.processDecodes(context);
377   }
378 
379   @Override
380   protected boolean isRendererUsed()
381   {
382     // we use our own MockRenderer; not the one created by our super class:
383     return false;
384   }
385 
386   private TestTable _createTable()
387   {
388     return _createTable(true);
389   }
390 
391   private TestTable _createTable(boolean useModel)
392   {
393     SortableModel model = useModel ? _createTableData() : null;
394     TestTable table = new TestTable(model);
395     return table;
396   }
397 
398   private static SortableModel _createTableData()
399   {
400     final int sz = 25;
401     List<Object> data = new ArrayList<Object>(sz);
402     for(int i=0; i<sz; i++)
403     {
404       data.add(new Integer(i));
405     }
406     return new SortableModel(ModelUtils.toDataModel(data));
407   }
408 
409   // must be public static so that it can be state-saved:
410   public static final class DoNotCallBinding extends ValueBinding
411     implements StateHolder
412   {
413     @Override
414     public Class<?> getType(FacesContext context) throws EvaluationException, PropertyNotFoundException
415     {
416       throw new AssertionFailedError("This method should not be called");
417     }
418 
419     @Override
420     public Object getValue(FacesContext context) throws EvaluationException, PropertyNotFoundException
421     {
422       if (doNotCall)
423         throw new AssertionFailedError("This method should not be called");
424 
425       return _createTableData();
426     }
427 
428     @Override
429     public boolean isReadOnly(FacesContext context) throws EvaluationException, PropertyNotFoundException
430     {
431       throw new AssertionFailedError("This method should not be called");
432     }
433 
434     @Override
435     public void setValue(FacesContext context, Object value) throws EvaluationException, PropertyNotFoundException
436     {
437       throw new AssertionFailedError("This method should not be called");
438     }
439 
440     public Object saveState(FacesContext context)
441     {
442       return Boolean.FALSE;
443     }
444 
445     public void restoreState(FacesContext context, Object state)
446     {
447       // apon restoring state, to not allow getValue() calls:
448       doNotCall = true;
449     }
450 
451     public boolean isTransient()
452     {
453       return false;
454     }
455 
456     public void setTransient(boolean newTransientValue)
457     {
458     }
459 
460     public boolean doNotCall = false;
461   }
462 
463   private static final class TestComponent extends UIXInput
464   {
465     public TestComponent(String id)
466     {
467       _decodes   = Collections.emptyList();
468       _updates   = Collections.emptyList();
469       _validates = Collections.emptyList();
470       setId(id);
471     }
472 
473     @Override
474     public void processDecodes(FacesContext fc)
475     {
476       _decodes = _addRowData(fc, _decodes);
477     }
478 
479     @Override
480     public void processUpdates(FacesContext fc)
481     {
482       _updates = _addRowData(fc, _updates);
483     }
484 
485     @Override
486     public void processValidators(FacesContext fc)
487     {
488       _validates = _addRowData(fc, _validates);
489     }
490 
491     public List<Object> getDecodesRowData()
492     {
493       return _decodes;
494     }
495 
496     public List<Object> getValidatesRowData()
497     {
498       return _validates;
499     }
500 
501     public List<Object> getUpdatesRowData()
502     {
503       return _updates;
504     }
505 
506     @Override
507     public String toString()
508     {
509       return getId();
510     }
511 
512     @Override
513     protected Renderer getRenderer(FacesContext fc)
514     {
515       // for testing purposes must return null. Otherwise a renderer
516       // will be needed to compute the clientID:
517       return null;
518     }
519 
520     private List<Object> _addRowData(FacesContext fc, List<Object> currList)
521     {
522       if (currList == Collections.emptyList())
523         currList = new ArrayList<Object>(10);
524 
525       Object rowData = fc.getExternalContext().getRequestMap().get(_VAR);
526       currList.add(rowData);
527       return currList;
528     }
529 
530     private List<Object> _decodes;
531     private List<Object> _updates;
532     private List<Object> _validates;
533   }
534 
535   private static final String _VAR = "row";
536 
537   private static final class TestTable extends UIXTable
538   {
539     @SuppressWarnings("unchecked")
540     public TestTable(SortableModel model)
541     {
542       super();
543       setValue(model);
544       this.model = model;
545       setVar(_VAR);
546       setId("table1");
547 
548       UIXColumn column1 = new UIXColumn()
549       {
550         @Override
551         protected Renderer getRenderer(FacesContext fc)
552         {
553           // for testing purposes must return null. Otherwise a renderer
554           // will be needed to compute the clientID:
555           return null;
556         }
557       };
558       column1.setId("col1");
559       column1Header = new TestComponent("col1Header");
560       column1.setHeader(column1Header);
561       column1Child = new TestComponent("col1Child");
562       column1.getChildren().add(column1Child);
563 
564       UIXColumn column2 = new UIXColumn()
565       {
566         @Override
567         protected Renderer getRenderer(FacesContext fc)
568         {
569           // for testing purposes must return null. Otherwise a renderer
570           // will be needed to compute the clientID:
571           return null;
572         }
573       };
574       column2.setId("col2");
575       column2Header = new TestComponent("col2Header");
576       column2.setHeader(column2Header);
577       column2Child = new TestComponent("col2Child");
578       column2.getChildren().add(column2Child);
579 
580       List<UIComponent> kids = getChildren();
581       kids.add(column1);
582       kids.add(column2);
583 
584       detailStamp = new TestComponent("detail");
585       setDetailStamp(detailStamp);
586 
587       if (model != null)
588       {
589         setRowIndex(_DISCLOSED_INDEX);
590         getDisclosedRowKeys().add();
591         setRowIndex(-1);
592       }
593     }
594 
595     @Override
596     protected Renderer getRenderer(FacesContext fc)
597     {
598       // for testing purposes must return null. Otherwise a renderer
599       // will be needed to compute the clientID:
600       return null;
601     }
602 
603     public final SortableModel model;
604     public final TestComponent column1Header, column1Child,
605       column2Header, column2Child, detailStamp;
606   }
607 
608   private static final int _DISCLOSED_INDEX = 5;
609 }