1   //----------------------------------------------------------------------
2   // 
3   // PerfectJPattern: "Design patterns are good but components are better!" 
4   // TestSubject.java Copyright (c) 2009 Giovanni Azua Garcia
5   // bravegag@hotmail.com
6   //  
7   // This program 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  // This program is distributed in the hope that it will be useful,
13  // but WITHOUT ANY WARRANTY; without even the implied warranty of
14  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  // GNU General Public License for more details.
16  //
17  // You should have received a copy of the GNU General Public License
18  // along with this program; if not, see <http://www.gnu.org/licenses/>.
19  //
20  //----------------------------------------------------------------------
21  package org.perfectjpattern.core.behavioral.observer;
22  
23  import java.util.*;
24  
25  import junit.framework.*;
26  
27  import org.perfectjpattern.core.api.behavioral.observer.*;
28  import org.perfectjpattern.core.api.behavioral.observer.data.*;
29  import org.perfectjpattern.core.behavioral.observer.data.*;
30  import org.slf4j.*;
31  
32  /**
33   * Test Suite for <code>Subject</code> implementation.
34   * 
35   * @author <a href="mailto:bravegag@hotmail.com">Giovanni Azua</a>
36   * @version $Revision: 1.0 $ $Date: Jun 18, 2007 11:31:53 PM $
37   */
38  public final
39  class TestSubject 
40  extends TestCase 
41  {
42      //------------------------------------------------------------------------
43      // public
44      //------------------------------------------------------------------------
45      @SuppressWarnings("unchecked")
46      public void 
47      testFaultyObserverDetached()
48      {
49          // create a faulty Observer instance
50          IObserver<NullEventData> myObserver = 
51              new IObserver<NullEventData>()
52          {
53              //----------------------------------------------------------------
54              public void 
55              update(NullEventData anEventData)
56              {
57                  throw new RuntimeException("Sorry! I am faulty!");
58              }            
59          };
60          
61          // create Subject instance, attach and notify
62          ISubject<NullEventData> mySubject = new Subject<NullEventData>();
63          mySubject.attach(myObserver);
64          
65          try 
66          {
67              mySubject.notifyObservers(NullEventData.getInstance());
68          }
69          // CHECKSTYLE:OFF
70          catch (RuntimeException anException)
71          // CHECKSTYLE:ON
72          {
73              fail("Subject is not protected from faulty Observer instances");
74          }
75          
76          assertEquals("Subject did not automatically detach faulty Observers", 
77              0, mySubject.size());
78      }
79      
80      //------------------------------------------------------------------------
81      /**
82       * Test that <code>IStatusData</code> notifications were properly sent to
83       * all registered <code>IObserver</code> instances.
84       */
85      public void 
86      testStatusObserver() 
87      {
88          theLogger.debug("Running assertions ... ");
89  
90          assertEquals(
91                  "First observer incorrectly received completion notifications.",
92                  1, theFirstObserver.getSuccessCount());
93          assertEquals(
94                  "First observer incorrectly received failure notifications.",
95                  1, theFirstObserver.getFailureCount());
96  
97          assertEquals(
98                  "Second observer incorrectly missed completion notifications.",
99                  2, theSecondObserver.getSuccessCount());
100         assertEquals(
101                 "Second observer incorrectly missed failure notifications.", 2,
102                 theSecondObserver.getFailureCount());
103 
104         assertEquals(
105                 "Third observer incorrectly missed completion notifications.",
106                 2, theThirdObserver.getSuccessCount());
107         assertEquals(
108                 "Third observer incorrectly missed failure notifications.", 2,
109                 theThirdObserver.getFailureCount());
110 
111         theLogger.debug("Completed test");
112     }
113 
114     //------------------------------------------------------------------------
115     /**
116      * Test that <code>IProgressData</code> notifications were properly sent
117      * to all registered <code>IObserver</code> instances.
118      */
119     public void 
120     testProgressObserver() 
121     {
122         theLogger.debug("Running assertions ... ");
123 
124         List<ProgressData> myExpectedProgress = Arrays
125                 .asList(new ProgressData[] {
126                         ProgressData.STARTED,
127                         new ProgressData(Status.IN_PROGRESS,
128                                 "Testing in progress ...", 20),
129                         new ProgressData(Status.IN_PROGRESS,
130                                 "Testing in progress ...", 40),
131                         new ProgressData(Status.IN_PROGRESS,
132                                 "Testing in progress ...", 60),
133                         new ProgressData(Status.IN_PROGRESS,
134                                 "Testing in progress ...", 80),
135                         ProgressData.COMPLETED });
136 
137         int i = 0;
138         for (ProgressData myProgress : theFourthObserver.getProgress()) 
139         {
140             assertEquals("Invalid progress notification sequence.",
141                     myExpectedProgress.get(i++), myProgress);
142         }
143 
144         theLogger.debug("Completed test");
145     }
146     
147     //------------------------------------------------------------------------
148     @SuppressWarnings("unchecked")
149     public void
150     testClear()
151     {
152         theLogger.debug("Running assertions ... ");
153         
154         ISubject<NullEventData> mySubject = new Subject<NullEventData>();
155         
156         // attach an observer
157         mySubject.attach(new IObserver<NullEventData>()
158         {
159             //----------------------------------------------------------------
160             public void 
161             update(NullEventData anEventData)
162             {
163                 // nothing to do
164             }            
165         });
166         
167         assertEquals("Size method implemented incorrectly.", 1, 
168             mySubject.size());
169 
170         // should empty the collection of observers
171         mySubject.clear();
172         
173         assertEquals("Clear method implemented incorrectly.", 0, 
174             mySubject.size());
175 
176         theLogger.debug("Completed test");
177     }
178 
179     //------------------------------------------------------------------------
180     /**
181      * Test that <code>ISubject</code> keeps a correct number of
182      * <code>IObserver</code> attached.
183      */
184     public void 
185     testSize() 
186     {
187         theLogger.debug("Running assertions ... ");
188 
189         assertEquals("Status Subject contains invalid amount of observers", 2,
190                 theStatusSubject.size());
191         assertEquals("Progress Subject contains invalid amount of observers",
192                 1, theProgressSubject.size());
193 
194         theLogger.debug("Completed test");
195     }
196     
197     //------------------------------------------------------------------------
198     /**
199      * Simple test of the Observer pattern
200      */
201     @SuppressWarnings("unchecked")
202     public void 
203     testSimple() 
204     {
205         theLogger.debug("Started simple Observer usage test");
206         
207         theLogger.debug("Step #1 := Define Event data type");
208         class MyEventData implements IEventData 
209         {
210             //----------------------------------------------------------------
211             public 
212             MyEventData(final String aValue)
213             {
214                 theValue = aValue;
215             }
216             
217             //----------------------------------------------------------------
218             public final String 
219             getValue() 
220             {
221                 return theValue;
222             }            
223             
224             //----------------------------------------------------------------
225             private final String theValue;
226         }
227         
228         theLogger.debug("Step #2 := Define Observer instance");
229         IObserver<MyEventData> myObserver = new IObserver<MyEventData>() 
230         {
231             //----------------------------------------------------------------
232             public void 
233             update(MyEventData anEventData) 
234             {
235                 theLogger.debug("update call received with data: " + 
236                     anEventData.getValue());
237             }
238         };
239         
240         theLogger.debug("Step #3 := Create ISubject instance");
241         ISubject<MyEventData> mySubject = new Subject<MyEventData>();
242                 
243         theLogger.debug("Step #4 := Attach Observer to Subject");
244         mySubject.attach(myObserver);
245         
246         theLogger.debug("Step #5 := Notify observers");
247         mySubject.notifyObservers(new MyEventData("Hello Observer!"));
248         
249         theLogger.debug("Completed test");
250     }
251     
252     //------------------------------------------------------------------------
253     // protected
254     //------------------------------------------------------------------------
255     @Override
256     protected void 
257     setUp() 
258     throws Exception 
259     {
260         super.setUp();
261 
262         theLogger.debug("Creating Observer pattern test fixture ... ");
263 
264         // fixture for the usage of the Observer Pattern
265         fixture();
266 
267         theLogger.debug("Completed Observer pattern test fixture.");
268     }
269 
270     //------------------------------------------------------------------------
271     // private
272     //------------------------------------------------------------------------
273     /**
274      * Example usage of the Observer Pattern.
275      */
276     @SuppressWarnings("unchecked")
277     private void
278     fixture() 
279     {
280         //
281         // Status notification example
282         //
283         theLogger.debug("Step #1 := Create ISubject instance.");
284         theStatusSubject = new Subject<StatusData>();
285 
286         theLogger.debug("Step #2 := Create IObserver instances.");
287         theFirstObserver = new TestStatusObserver();
288         theSecondObserver = new TestStatusObserver();
289         theThirdObserver = new TestStatusObserver();
290 
291         theLogger.debug("Step #3 := Attach IObserver to ISubject.");
292         theStatusSubject.attach(theFirstObserver, theSecondObserver, 
293             theThirdObserver);
294 
295         theLogger.debug("Step #4 := Send notifications.");
296         theStatusSubject.notifyObservers(StatusData.FAILED);
297         theStatusSubject.notifyObservers(StatusData.COMPLETED);
298 
299         theLogger.debug("Eventually detach observers.");
300         theStatusSubject.detach(theFirstObserver);
301 
302         theLogger.debug("Send more notifications.");
303         theStatusSubject.notifyObservers(StatusData.FAILED);
304         theStatusSubject.notifyObservers(StatusData.COMPLETED);
305 
306         //
307         // Progress notification example
308         //
309         theLogger.debug("Step #1 := Create ISubject instance.");
310         theProgressSubject = new Subject<ProgressData>();
311 
312         theLogger.debug("Step #2 := Create IObserver instances.");
313         theFourthObserver = new TestProgressObserver();
314 
315         theLogger.debug("Step #3 := Attach IObserver to ISubject.");
316         theProgressSubject.attach(theFourthObserver);
317 
318         theLogger.debug("Step #4 := Send notifications.");
319         theProgressSubject.notifyObservers(ProgressData.STARTED);
320         for (int myI = 1; myI < 5; myI++) 
321         {
322             theProgressSubject.notifyObservers(new ProgressData(
323                 Status.IN_PROGRESS, "Testing in progress ...", 5, myI));
324         }
325         theProgressSubject.notifyObservers(ProgressData.COMPLETED);
326     }    
327     
328     //------------------------------------------------------------------------
329     // inner classes
330     //------------------------------------------------------------------------
331     /**
332      * Concrete <code>IObserver&lt;StatusData&gt;</code> implementation.
333      */
334     private static final 
335     class TestStatusObserver 
336     implements IObserver<StatusData> 
337     {
338         //---------------------------------------------------------------------
339         public final int 
340         getSuccessCount() 
341         {
342             return theSuccessCount;
343         }
344 
345         //---------------------------------------------------------------------
346         public final int 
347         getFailureCount() 
348         {
349             return theFailureCount;
350         }
351 
352         //---------------------------------------------------------------------
353         public void 
354         update(StatusData aStatusData) 
355         {
356             if (Status.COMPLETED == aStatusData.getStatus()) 
357             {
358                 theSuccessCount++;
359             } 
360             else 
361             if (Status.FAILED == aStatusData.getStatus()) 
362             {
363                 theFailureCount++;
364             }
365         }
366         
367         //---------------------------------------------------------------------
368         // members
369         //---------------------------------------------------------------------
370         private int theFailureCount = 0;
371         private int theSuccessCount = 0;
372     }    
373 
374     //------------------------------------------------------------------------
375     /**
376      * Example of concrete <code>IObserver&lt;ProgressData&gt;</code>
377      * implementation.
378      */
379     private static final 
380     class TestProgressObserver 
381     implements IObserver<ProgressData> 
382     {
383         //---------------------------------------------------------------------
384         public void 
385         update(ProgressData aProgressData) 
386         {
387             theProgress.add(aProgressData);
388         }
389 
390         //---------------------------------------------------------------------
391         public final 
392         Collection<ProgressData> 
393         getProgress() 
394         {
395             return theProgress;
396         }
397 
398         //---------------------------------------------------------------------
399         // members
400         //---------------------------------------------------------------------
401         private Collection<ProgressData> theProgress = 
402             new ArrayList<ProgressData>();
403     }
404 
405     //------------------------------------------------------------------------
406     // members
407     //------------------------------------------------------------------------
408     private ISubject<StatusData> theStatusSubject = null;
409     private ISubject<ProgressData> theProgressSubject = null;
410     private TestStatusObserver theFirstObserver = null;
411     private TestStatusObserver theSecondObserver = null;
412     private TestStatusObserver theThirdObserver = null;
413     private TestProgressObserver theFourthObserver = null;
414     
415     /**
416      * Provides logging services for this class.
417      */
418     private final Logger theLogger = LoggerFactory.getLogger(this.getClass());
419 }