1 /*
   2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package test.javafx.beans.value;
  27 
  28 import test.javafx.beans.value.ChangeListenerMock;
  29 import javafx.beans.InvalidationListener;
  30 import test.javafx.beans.InvalidationListenerMock;
  31 import javafx.beans.Observable;
  32 import javafx.beans.value.ChangeListener;
  33 import javafx.beans.value.ObservableObjectValueStub;
  34 
  35 import org.junit.Before;
  36 import org.junit.Test;
  37 
  38 public class ObservableValueBaseTest {
  39 
  40         private static final Object UNDEFINED_VALUE = new Object();
  41     private static final Object V1 = new Object();
  42     private static final Object V2 = new Object();
  43         
  44     private ObservableObjectValueStub<Object> valueModel;
  45     private InvalidationListenerMock invalidationListener;
  46     private ChangeListenerMock<Object> changeListener;
  47 
  48     @Before
  49     public void setUp() {
  50         valueModel = new ObservableObjectValueStub<Object>();
  51         invalidationListener = new InvalidationListenerMock();
  52         changeListener = new ChangeListenerMock<Object>(UNDEFINED_VALUE);
  53     }
  54 
  55     @Test
  56     public void testInitialState() {
  57         // no exceptions etc.
  58         valueModel.fireValueChangedEvent();
  59     }
  60 
  61     @Test
  62     public void testOneInvalidationListener() {
  63         // adding one observer
  64         valueModel.addListener(invalidationListener);
  65         System.gc(); // making sure we did not not overdo weak references
  66         valueModel.set(V1);
  67         invalidationListener.check(valueModel, 1);
  68 
  69         // remove observer
  70         valueModel.removeListener(invalidationListener);
  71         valueModel.set(V2);
  72         invalidationListener.check(null, 0);
  73 
  74         // remove observer again
  75         valueModel.removeListener(invalidationListener);
  76         valueModel.set(V1);
  77         invalidationListener.check(null, 0);
  78     }
  79 
  80     @Test
  81     public void testOneChangeListener() {
  82         // adding one observer
  83         valueModel.addListener(changeListener);
  84         System.gc(); // making sure we did not not overdo weak references
  85         valueModel.set(V1);
  86         changeListener.check(valueModel, null, V1, 1);
  87         
  88         // set same value again
  89         valueModel.set(V1);
  90         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
  91         
  92         // set null
  93         valueModel.set(null);
  94         changeListener.check(valueModel, V1, null, 1);
  95         valueModel.set(null);
  96         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
  97 
  98         // remove observer
  99         valueModel.removeListener(changeListener);
 100         valueModel.set(V2);
 101         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
 102 
 103         // remove observer again
 104         valueModel.removeListener(changeListener);
 105         valueModel.set(V1);
 106         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
 107     }
 108 
 109     @Test
 110     public void testTwoObservers() {
 111         final InvalidationListenerMock observer2 = new InvalidationListenerMock();
 112 
 113         // adding two observers
 114         valueModel.addListener(invalidationListener);
 115         valueModel.addListener(observer2);
 116         System.gc(); // making sure we did not not overdo weak references
 117         valueModel.fireValueChangedEvent();
 118         invalidationListener.check(valueModel, 1);
 119         observer2.check(valueModel, 1);
 120 
 121         // remove first observer
 122         invalidationListener.reset();
 123         observer2.reset();
 124         valueModel.removeListener(invalidationListener);
 125         valueModel.fireValueChangedEvent();
 126         invalidationListener.check(null, 0);
 127         observer2.check(valueModel, 1);
 128 
 129         // remove second observer
 130         observer2.reset();
 131         valueModel.removeListener(observer2);
 132         valueModel.fireValueChangedEvent();
 133         invalidationListener.check(null, 0);
 134         observer2.check(null, 0);
 135 
 136         // remove observers in reverse order
 137         observer2.reset();
 138         valueModel.removeListener(observer2);
 139         valueModel.removeListener(invalidationListener);
 140         valueModel.fireValueChangedEvent();
 141         invalidationListener.check(null, 0);
 142         observer2.check(null, 0);
 143     }
 144 
 145     @Test
 146     public void testConcurrentAdd() {
 147         final InvalidationListenerMock observer2 = new AddingListenerMock();
 148         valueModel.addListener(observer2);
 149 
 150         // fire event that adds a second observer
 151         // Note: there is no assumption if observer that is being added is notified
 152         valueModel.fireValueChangedEvent();
 153         observer2.check(valueModel, 1);
 154 
 155         // fire event again, this time both observers need to be notified
 156         invalidationListener.reset();
 157         observer2.reset();
 158         valueModel.fireValueChangedEvent();
 159         invalidationListener.check(valueModel, 1);
 160         observer2.check(valueModel, 1);
 161     }
 162 
 163     @Test
 164     public void testConcurrentRemove() {
 165         final InvalidationListenerMock observer2 = new RemovingListenerMock();
 166         valueModel.addListener(observer2);
 167         valueModel.addListener(invalidationListener);
 168 
 169         // fire event that removes one observer
 170         // Note: there is no assumption if observer that is being removed is notified
 171         valueModel.fireValueChangedEvent();
 172         observer2.check(valueModel, 1);
 173 
 174         // fire event again, this time only non-removed observer is notified
 175         invalidationListener.reset();
 176         observer2.reset();
 177         valueModel.fireValueChangedEvent();
 178         invalidationListener.check(null, 0);
 179         observer2.check(valueModel, 1);
 180     }
 181 
 182     @Test(expected=NullPointerException.class)
 183     public void testAddingNull_InvalidationListener() {
 184         valueModel.addListener((InvalidationListener)null);
 185     }
 186 
 187     @Test(expected=NullPointerException.class)
 188     public void testAddingNull_ChangeListener() {
 189         valueModel.addListener((ChangeListener<Object>)null);
 190     }
 191 
 192     @Test(expected=NullPointerException.class)
 193     public void testRemovingNull_InvalidationListener() {
 194         valueModel.removeListener((InvalidationListener)null);
 195     }
 196 
 197     @Test(expected=NullPointerException.class)
 198     public void testRemovingNull_ChangeListener() {
 199         valueModel.removeListener((ChangeListener<Object>)null);
 200     }
 201 
 202     private class AddingListenerMock extends InvalidationListenerMock {
 203         @Override public void invalidated(Observable valueModel) {
 204             super.invalidated(valueModel);
 205             valueModel.addListener(invalidationListener);
 206         }
 207     }
 208 
 209     private class RemovingListenerMock extends InvalidationListenerMock {
 210         @Override public void invalidated(Observable valueModel) {
 211             super.invalidated(valueModel);
 212             valueModel.removeListener(invalidationListener);
 213         }
 214     }
 215 }