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 javafx.beans.value;
  27 
  28 import javafx.beans.InvalidationListener;
  29 import javafx.beans.InvalidationListenerMock;
  30 import javafx.beans.Observable;
  31 
  32 import org.junit.Before;
  33 import org.junit.Test;
  34 
  35 public class ObservableValueBaseTest {
  36 
  37         private static final Object UNDEFINED_VALUE = new Object();
  38     private static final Object V1 = new Object();
  39     private static final Object V2 = new Object();
  40         
  41     private ObservableObjectValueStub<Object> valueModel;
  42     private InvalidationListenerMock invalidationListener;
  43     private ChangeListenerMock<Object> changeListener;
  44 
  45     @Before
  46     public void setUp() {
  47         valueModel = new ObservableObjectValueStub<Object>();
  48         invalidationListener = new InvalidationListenerMock();
  49         changeListener = new ChangeListenerMock<Object>(UNDEFINED_VALUE);
  50     }
  51 
  52     @Test
  53     public void testInitialState() {
  54         // no exceptions etc.
  55         valueModel.fireValueChangedEvent();
  56     }
  57 
  58     @Test
  59     public void testOneInvalidationListener() {
  60         // adding one observer
  61         valueModel.addListener(invalidationListener);
  62         System.gc(); // making sure we did not not overdo weak references
  63         valueModel.set(V1);
  64         invalidationListener.check(valueModel, 1);
  65 
  66         // remove observer
  67         valueModel.removeListener(invalidationListener);
  68         valueModel.set(V2);
  69         invalidationListener.check(null, 0);
  70 
  71         // remove observer again
  72         valueModel.removeListener(invalidationListener);
  73         valueModel.set(V1);
  74         invalidationListener.check(null, 0);
  75     }
  76 
  77     @Test
  78     public void testOneChangeListener() {
  79         // adding one observer
  80         valueModel.addListener(changeListener);
  81         System.gc(); // making sure we did not not overdo weak references
  82         valueModel.set(V1);
  83         changeListener.check(valueModel, null, V1, 1);
  84         
  85         // set same value again
  86         valueModel.set(V1);
  87         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
  88         
  89         // set null
  90         valueModel.set(null);
  91         changeListener.check(valueModel, V1, null, 1);
  92         valueModel.set(null);
  93         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
  94 
  95         // remove observer
  96         valueModel.removeListener(changeListener);
  97         valueModel.set(V2);
  98         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
  99 
 100         // remove observer again
 101         valueModel.removeListener(changeListener);
 102         valueModel.set(V1);
 103         changeListener.check(null, UNDEFINED_VALUE, UNDEFINED_VALUE, 0);
 104     }
 105 
 106     @Test
 107     public void testTwoObservers() {
 108         final InvalidationListenerMock observer2 = new InvalidationListenerMock();
 109 
 110         // adding two observers
 111         valueModel.addListener(invalidationListener);
 112         valueModel.addListener(observer2);
 113         System.gc(); // making sure we did not not overdo weak references
 114         valueModel.fireValueChangedEvent();
 115         invalidationListener.check(valueModel, 1);
 116         observer2.check(valueModel, 1);
 117 
 118         // remove first observer
 119         invalidationListener.reset();
 120         observer2.reset();
 121         valueModel.removeListener(invalidationListener);
 122         valueModel.fireValueChangedEvent();
 123         invalidationListener.check(null, 0);
 124         observer2.check(valueModel, 1);
 125 
 126         // remove second observer
 127         observer2.reset();
 128         valueModel.removeListener(observer2);
 129         valueModel.fireValueChangedEvent();
 130         invalidationListener.check(null, 0);
 131         observer2.check(null, 0);
 132 
 133         // remove observers in reverse order
 134         observer2.reset();
 135         valueModel.removeListener(observer2);
 136         valueModel.removeListener(invalidationListener);
 137         valueModel.fireValueChangedEvent();
 138         invalidationListener.check(null, 0);
 139         observer2.check(null, 0);
 140     }
 141 
 142     @Test
 143     public void testConcurrentAdd() {
 144         final InvalidationListenerMock observer2 = new AddingListenerMock();
 145         valueModel.addListener(observer2);
 146 
 147         // fire event that adds a second observer
 148         // Note: there is no assumption if observer that is being added is notified
 149         valueModel.fireValueChangedEvent();
 150         observer2.check(valueModel, 1);
 151 
 152         // fire event again, this time both observers need to be notified
 153         invalidationListener.reset();
 154         observer2.reset();
 155         valueModel.fireValueChangedEvent();
 156         invalidationListener.check(valueModel, 1);
 157         observer2.check(valueModel, 1);
 158     }
 159 
 160     @Test
 161     public void testConcurrentRemove() {
 162         final InvalidationListenerMock observer2 = new RemovingListenerMock();
 163         valueModel.addListener(observer2);
 164         valueModel.addListener(invalidationListener);
 165 
 166         // fire event that removes one observer
 167         // Note: there is no assumption if observer that is being removed is notified
 168         valueModel.fireValueChangedEvent();
 169         observer2.check(valueModel, 1);
 170 
 171         // fire event again, this time only non-removed observer is notified
 172         invalidationListener.reset();
 173         observer2.reset();
 174         valueModel.fireValueChangedEvent();
 175         invalidationListener.check(null, 0);
 176         observer2.check(valueModel, 1);
 177     }
 178 
 179     @Test(expected=NullPointerException.class)
 180     public void testAddingNull_InvalidationListener() {
 181         valueModel.addListener((InvalidationListener)null);
 182     }
 183 
 184     @Test(expected=NullPointerException.class)
 185     public void testAddingNull_ChangeListener() {
 186         valueModel.addListener((ChangeListener<Object>)null);
 187     }
 188 
 189     @Test(expected=NullPointerException.class)
 190     public void testRemovingNull_InvalidationListener() {
 191         valueModel.removeListener((InvalidationListener)null);
 192     }
 193 
 194     @Test(expected=NullPointerException.class)
 195     public void testRemovingNull_ChangeListener() {
 196         valueModel.removeListener((ChangeListener<Object>)null);
 197     }
 198 
 199     private class AddingListenerMock extends InvalidationListenerMock {
 200         @Override public void invalidated(Observable valueModel) {
 201             super.invalidated(valueModel);
 202             valueModel.addListener(invalidationListener);
 203         }
 204     }
 205 
 206     private class RemovingListenerMock extends InvalidationListenerMock {
 207         @Override public void invalidated(Observable valueModel) {
 208             super.invalidated(valueModel);
 209             valueModel.removeListener(invalidationListener);
 210         }
 211     }
 212 }