Jump to: navigation, search

JFace Data Binding/Conformance Tests

The JFace Data Binding TCK is a suite of tests and other files that allow for asserting the conformance of implementations to the abstractions provided by the library.

The TCK is not yet available but will be soon for early adopters.

Observables

The TCK provides tests for the assertion of conformance to the observable specifications. Tests are currently provided for implementations of:

  • IObservable
  • IObservableValue
  • IObservableCollection
  • IObservableList

Tests are broken up into mutable and immutable test cases (e.g. ObservableValueContractTest and MutableObservableValueContractTest). The reason for this is that not all implementations allow for all a consumer to mutate the observable via its API. The TCK tests assert the following when appropriate:

  • Change events and their diffs
  • Realm checking
  • ObservableTracker.getterCalled(IObservable) invocations
  • Value types

The TCK tests don't assert the observed object state. Because the observed object can be of any type and the value can be in any form this isn't something that we feel we can reliably provide. It would be more straightforward for these to remain in your own tests.

Delegates

In order to take advantage of the tests developers will need to create a contract delegate. The delegates allow for implementation specific details to be provided to the TCK. The IObservableContractDelegate is provided below as an example:

public interface IObservableContractDelegate {
	/**
	 * Notifies the delegate of the start of a test.
	 */
	public void setUp();

	/**
	 * Notifies the delegate of the end of a test.
	 */
	public void tearDown();

	/**
	 * Invokes an operation to set the stale state of the provided
	 * observable.
	 * 
	 * @param observable
	 * @param stale
	 */
	public void setStale(IObservable observable, boolean stale);

	/**
	 * Creates a new observable.
	 * 
	 * @param realm realm of the observable
	 * @return observable
	 */
	public IObservable createObservable(Realm realm);

	/**
	 * Invokes a change operation on the observable resulting in a change event
	 * being fired from the observable.
	 * 
	 * @param observable
	 */
	public void change(IObservable observable);
}

The delegate API follows the standard JUnit conventions of setUp() and tearDown(). The other methods will be invoked when necessary by the tests.

The delegates provided are:

  • IObservableContractDelegate
  • IObservableValueContractDelegate
  • IObservableCollectionContractDelegate

Your observable implementation will determine which delegate to constuct.

Integration into Tests

Since the tests are JUnit3 tests you can integrate them into your tests in standard ways. The two most common ways are by subclassing or creating a suite.

Subclassing

public class ButtonObservableValueTest extends SWTObservableValueContractTest {
	public ButtonObservableValueTest() {
		super(new Delegate());
	}
	
	/* package */ static class Delegate extends AbstractObservableValueContractDelegate {
		private Shell shell;
		private Button button;

		public void setUp() {
			shell = new Shell();
			button = new Button(shell, SWT.CHECK);
		}
		
		public void tearDown() {
			shell.dispose();
		}
		
		public IObservableValue createObservableValue(Realm realm) {
			return new ButtonObservableValue(realm, button);
		}
		
		public void change(IObservable observable) {
			boolean value = button.getSelection();
			button.setSelection(!value);
			button.notifyListeners(SWT.Selection, null);
		}
		
		public Object createValue(IObservableValue observable) {
			return (Boolean.TRUE.equals(observable.getValue()) ? Boolean.FALSE : Boolean.TRUE);
		}
		
		public Object getValueType(IObservableValue observable) {
			return Boolean.TYPE;
		}
	}
}

When subclassing, because of single inheritance, you will will have to create multiple implementations to test the mutable and immutable use cases (e.g. there would need to be a ButtonMutableObservableValueTest as well to test the mutable cases). The rest of the implementation should be straightforward. The only thing we ask is that you don't depend upon API other than the constructors. Tests are public because JUnit requires them to be, not because we want to commit to them as API. Over time we would like to have the opportunity to rename, add, remove, or optimize the test methods to ensure that we're getting the best coverage as possible.

JUnit suite()

public class ButtonObservableValueTest extends TestCase {
	public static Test suite() {
		Delegate delegate = new Delegate();

		return new SuiteBuilder().addTests(ButtonObservableValueTest.class)
				.addObservableContractTest(
						SWTObservableValueContractTest.class, delegate)
				.addObservableContractTest(
						SWTMutableObservableValueContractTest.class, delegate)
				.build();
	}

	/* package */ static class Delegate extends AbstractObservableValueContractDelegate {
		private Shell shell;
		private Button button;

		public void setUp() {
			shell = new Shell();
			button = new Button(shell, SWT.CHECK);
		}
		
		public void tearDown() {
			shell.dispose();
		}
		
		public IObservableValue createObservableValue(Realm realm) {
			return new ButtonObservableValue(realm, button);
		}
		
		public void change(IObservable observable) {
			boolean value = button.getSelection();
			button.setSelection(!value);
			button.notifyListeners(SWT.Selection, null);
		}
		
		public Object createValue(IObservableValue observable) {
			return (Boolean.TRUE.equals(observable.getValue()) ? Boolean.FALSE : Boolean.TRUE);
		}
		
		public Object getValueType(IObservableValue observable) {
			return Boolean.TYPE;
		}
	}
} 

By creating a suite() method you can create a custom suite of tests to run. This will allow you to run multiple TestCases from a single test eliminating the need to create multiple implementations for the mutable and immutable cases. The SuiteBuilder implementation allows for a straightforward way to build these suites. The downside to building tests in this fashion is that when ran they don't contain the context of a parent class. In the JUnit view in Eclipse they are children of a junit.framework.TestSuite rather than a named test. Options are being investigated to remedy this problem.