/*******************************************************************************
 * Copyright (c) 2005, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.tests.keys;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.contexts.Context;
import org.eclipse.core.commands.contexts.ContextManager;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.util.Util;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * Test cases covering the various interaction between bindings. Bindings that
 * have been removed. Bindings that have been added. Inheritance of various
 * properties.
 *
 * @since 3.1
 */
public final class BindingInteractionsTest {

	/**
	 * The binding manager to use in each test case. A new binding manager is
	 * created for each test case, and it is disposed when the test is over.
	 */
	private BindingManager bindingManager = null;

	/**
	 * The context manager to use in each test case. A new context manager is
	 * created for each test case, and it is disposed when the test is over.
	 */
	private ContextManager contextManager = null;

	/**
	 * Creates a new context manager and a binding manager for use in the test
	 * cases.
	 */
	@Before
	public void doSetUp() {
		contextManager = new ContextManager();
		bindingManager = new BindingManager(contextManager,
				new CommandManager());
	}

	/**
	 * Releases the context manager and binding manager for garbage collection.
	 */
	@After
	public void doTearDown() {
		contextManager = null;
		bindingManager = null;
	}

	/**
	 * <p>
	 * Tests whether two identical bindings lead to a conflict.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testConflict() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("conflict1", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("conflict2", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);

		TriggerSequence[] activeBindings = bindingManager
				.getActiveBindingsFor(binding1.getParameterizedCommand());
		assertFalse(binding1.equals(binding2));
		assertTrue("Neither binding should be active",
				activeBindings.length == 0);
		activeBindings = bindingManager.getActiveBindingsFor(binding2
				.getParameterizedCommand());
		assertTrue("Neither binding should be active",
				activeBindings.length == 0);
	}

	/**
	 * <p>
	 * Tests whether a plug-in developer can override a binding in a child
	 * context.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testContextOverride() throws NotDefinedException {
		final Context parentContext = contextManager.getContext("parent");
		parentContext.define("parent", "parent context", null);

		final Context childContext = contextManager.getContext("child");
		childContext.define("child", "child context", "parent");

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);

		final Binding binding1 = new TestBinding("parent", "na", "parent",
				null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("child", "na", "child", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);

		// Only "parent"
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("parent");
		contextManager.setActiveContextIds(activeContextIds);
		assertEquals(
				"When only the parent context is active, only the parent binding is active.",
				binding1, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));

		// Only "child"
		activeContextIds.clear();
		activeContextIds.add("child");
		contextManager.setActiveContextIds(activeContextIds);
		assertEquals(
				"When only the child context is active, only the child binding is active.",
				binding2, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));

		// Both "parent" and "child"
		activeContextIds.add("parent");
		contextManager.setActiveContextIds(activeContextIds);
		assertEquals(
				"When both contexts are active, only the child binding is active.",
				binding2, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a user-defined deletion actually works.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDeletedBinding() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding(null, "na", "na", null, null,
				Binding.USER, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("system", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		assertEquals("The user should be able to remove bindings", null,
				bindingManager.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a user-defined deletion in one context will allow a binding
	 * in a parent context to match.  Bug 105655.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDeletedBindingAllowsParent() throws NotDefinedException {
		final Context parentContext = contextManager.getContext("parent");
		parentContext.define("name", "description", null);
		final Context childContext = contextManager.getContext("child");
		childContext.define("name", "description", "parent");

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("parent");
		activeContextIds.add("child");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding childBinding = new TestBinding("childCommand", "na",
				"child", null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(childBinding);
		final Binding deletion = new TestBinding(null, "na", "child", null,
				null, Binding.USER, null);
		bindingManager.addBinding(deletion);
		final Binding parentBinding = new TestBinding("parentCommand", "na",
				"parent", null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(parentBinding);
		assertEquals(
				"The user should be able to remove bindings to allow a parent binding",
				"parentCommand", bindingManager.getPerfectMatch(
						TestBinding.TRIGGER_SEQUENCE).getParameterizedCommand()
						.getId());
	}

	/**
	 * <p>
	 * Tests a common case for binding deletion. The binding is defined on all
	 * platforms, then deleted on a specific platform, and defined again as
	 * something else.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDeletedBindingPlatform() throws NotDefinedException {
		final String na = "na";

		final Context context = contextManager.getContext(na);
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme(na);
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding allPlatforms = new TestBinding("allPlatforms", na, na,
				null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(allPlatforms);
		final Binding deletion = new TestBinding(null, na, na, null, Util.getWS(),
				Binding.SYSTEM, null);
		bindingManager.addBinding(deletion);
		final Binding platformSpecific = new TestBinding("platformSpecific",
				na, na, null, Util.getWS(), Binding.SYSTEM, null);
		bindingManager.addBinding(platformSpecific);
		assertEquals(
				"We should be able to change a binding on a particular platform",
				platformSpecific, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a system deletion for a different locale or platform
	 * actually works. It shouldn't. Deletions should only work if they specify
	 * a matching locale or platform.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDeletedBindingUnnecessarily() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding(null, "na", "na",
				"not the current locale", null, Binding.SYSTEM, null);
		final Binding binding2 = new TestBinding("system", "na", "na", null,
				null, Binding.SYSTEM, null);
		final Binding binding3 = new TestBinding(null, "na", "na", null,
				"not the current platform", Binding.SYSTEM, null);
		final Binding[] bindings = new Binding[3];
		bindings[0] = binding1;
		bindings[1] = binding2;
		bindings[2] = binding3;
		bindingManager.setBindings(bindings);
		assertEquals(
				"A binding should not cause a deletion if its locale or platform doesn't match",
				binding2, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a user can add a binding to the same conditions once
	 * they've deleted the system binding.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDeletedBindingWithAddition() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding(null, "na", "na", null, null,
				Binding.USER, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("system", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		final Binding binding3 = new TestBinding("user", "na", "na", null,
				null, Binding.USER, null);
		bindingManager.addBinding(binding3);
		assertEquals("The user redefine a particular binding", binding3,
				bindingManager.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * This tests the case where a plug-in developer unbinds a key, and then a
	 * user tries to bind to that key. The user should be allowed to bind.
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDeletedBindingWithUserOverride() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding generalBinding = new TestBinding("general", scheme
				.getId(), context.getId(), null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(generalBinding);
		final Binding cancelOnPlatform = new TestBinding(null, scheme.getId(),
				context.getId(), null, bindingManager.getPlatform(),
				Binding.SYSTEM, null);
		bindingManager.addBinding(cancelOnPlatform);
		final Binding userOverride = new TestBinding("user", scheme.getId(),
				context.getId(), null, bindingManager.getPlatform(),
				Binding.USER, null);
		bindingManager.addBinding(userOverride);
		assertEquals("The user redefine a binding deleted in the system",
				userOverride, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));

	}

	/**
	 * Tests that if more than one deletion is defined for the same binding,
	 * that the deletion will still work.  Bug 106574 points out a case where it
	 * is possible for a deletion to clobber another deletion.
	 *
	 * @since 3.2
	 */
	@Test
	public void testDoubleDeletedBinding() {
		final String parent = "parent";
		final String child = "child";

		// Set-up the contexts
		final Context parentContext = contextManager.getContext(parent);
		parentContext.define(parent, parent, null);
		final Context childContext = contextManager.getContext(child);
		childContext.define(child, child, parent);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add(parent);
		activeContextIds.add(child);
		contextManager.setActiveContextIds(activeContextIds);

		// Set-up the schemes.
		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define(parent, parent, null);

		/*
		 * Set-up a binding, with two deletions. The first deletion matches the
		 * context, but the second does not.
		 */
		final Binding binding = new TestBinding("command", "na", parent, null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding);
		final Binding correctDeletion = new TestBinding(null, "na", parent,
				null, null, Binding.USER, null);
		bindingManager.addBinding(correctDeletion);
		final Binding wrongDeletion = new TestBinding(null, "na", child, null,
				null, Binding.USER, null);
		bindingManager.addBinding(wrongDeletion);

		// Test that the deletion worked.
		assertEquals("The parent should not be active", null, bindingManager
				.getPerfectMatch(binding.getTriggerSequence()));
	}

	/**
	 * This tests a complicated scenario that arises with the Emacs key binding
	 * set in the Eclipse workbench. The first binding belongs to a parent
	 * context, but a child scheme. The trigger sequences are not the same.
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testDoubleParent() throws NotDefinedException {
		final String parent = "parent";
		final String child = "child";

		// Set-up the contexts
		final Context parentContext = contextManager.getContext(parent);
		parentContext.define(parent, parent, null);
		final Context childContext = contextManager.getContext(child);
		childContext.define(child, child, parent);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add(parent);
		activeContextIds.add(child);
		contextManager.setActiveContextIds(activeContextIds);

		// Set-up the schemes.
		final Scheme parentScheme = bindingManager.getScheme(parent);
		parentScheme.define(parent, parent, null);
		final Scheme childScheme = bindingManager.getScheme(child);
		childScheme.define(child, child, parent);
		bindingManager.setActiveScheme(childScheme);

		// Add two bindings.
		final Binding parentBinding = new TestBinding(parent, parent, parent,
				null, null, Binding.SYSTEM, null);
		final Binding childBinding = new TestBinding(child, child, child, null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(parentBinding);
		bindingManager.addBinding(childBinding);

		// Test to see that only the child is active.
		assertTrue("The parent should not be active",
				bindingManager.getActiveBindingsFor(parentBinding
						.getParameterizedCommand()).length == 0);
		assertTrue("The child should be active",
				bindingManager.getActiveBindingsFor(childBinding
						.getParameterizedCommand()).length != 0);
	}

	/**
	 * <p>
	 * Tests whether a plug-in developer can override a binding for a particular
	 * locale.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testLocaleOverride() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("base", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding(null, "na", "na", Locale
				.getDefault().toString(), null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		final Binding binding3 = new TestBinding("locale-specific", "na", "na",
				Locale.getDefault().toString(), null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding3);
		assertEquals(
				"A plug-in developer should be able to change a binding for a locale",
				binding3, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a plug-in developer can override a binding for a particular
	 * platform.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testPlatformOverride() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("base", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding(null, "na", "na", null, Util.getWS(),
				Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		final Binding binding3 = new TestBinding("platform-specific", "na",
				"na", null, Util.getWS(), Binding.SYSTEM, null);
		bindingManager.addBinding(binding3);
		assertEquals(
				"A plug-in developer should be able to change a binding for a platform",
				binding3, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a binding in a child scheme will override a binding in a
	 * parent scheme. The test is set-up as follows:
	 * </p>
	 * <ul>
	 * <li>Binding1(commandId="child",schemeId="child",contextId="na",locale=null,platform=null,type=SYSTEM)</li>
	 * <li>Binding2(commandId="parent",schemeId="parent",contextId="na",locale=null,platform=null,type=SYSTEM)</li>
	 * </ul>
	 * <p>
	 * Binding1 should win.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testSchemeOverride() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme parentScheme = bindingManager.getScheme("parent");
		parentScheme.define("parent", "parent scheme", null);

		final Scheme childScheme = bindingManager.getScheme("child");
		childScheme.define("child", "child scheme", "parent");

		bindingManager.setActiveScheme(childScheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("child", "child", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("parent", "parent", "na",
				null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		assertEquals("The binding from the child scheme should be active",
				binding1, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * This tests a Emacs-style scenario. In this scenario a child scheme
	 * defines a binding in a parent context. The parent scheme bindings the
	 * same trigger sequence in a child context. The child scheme definition
	 * should win.
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testSchemeOverrideDifferentContexts()
			throws NotDefinedException {
		final String parent = "parent";
		final String child = "child";

		final Context parentContext = contextManager.getContext(parent);
		parentContext.define("parent", "description", null);
		final Context childContext = contextManager.getContext(child);
		childContext.define("child", "description", parent);

		final Scheme parentScheme = bindingManager.getScheme(parent);
		parentScheme.define("parent", "parent scheme", null);
		final Scheme childScheme = bindingManager.getScheme(child);
		childScheme.define("child", "child scheme", parent);

		bindingManager.setActiveScheme(childScheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add(parentContext.getId());
		activeContextIds.add(childContext.getId());
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("parent",
				parentScheme.getId(), childContext.getId(), null, null,
				Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("child", childScheme.getId(),
				parentContext.getId(), null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		assertEquals("The binding from the child scheme should be active",
				binding2, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a binding in a child scheme will override a binding in a
	 * parent scheme -- regardless of their type. The test is set-up as follows:
	 * </p>
	 * <ul>
	 * <li>Binding1(commandId="child",schemeId="child",contextId="na",locale=null,platform=null,type=SYSTEM)</li>
	 * <li>Binding2(commandId="parent",schemeId="parent",contextId="na",locale=null,platform=null,type=USER)</li>
	 * </ul>
	 * <p>
	 * Binding1 should win.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testSchemeOverrideDifferentTypes() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme parentScheme = bindingManager.getScheme("parent");
		parentScheme.define("parent", "parent scheme", null);

		final Scheme childScheme = bindingManager.getScheme("child");
		childScheme.define("child", "child scheme", "parent");

		bindingManager.setActiveScheme(childScheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("child", "child", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("parent", "parent", "na",
				null, null, Binding.USER, null);
		bindingManager.addBinding(binding2);
		assertEquals("The binding from the child scheme should be active",
				binding1, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether two bindings defined for sibling active contexts leads to a
	 * conflict.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testSiblingContextConflict() throws NotDefinedException {
		final Context context1 = contextManager.getContext("sibling1");
		context1.define("sibling1", "first sibling context", null);

		final Context context2 = contextManager.getContext("sibling2");
		context2.define("sibling2", "second sibling context", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);

		final Binding binding1 = new TestBinding("sibling1", "na", "sibling1",
				null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("sibling2", "na", "sibling2",
				null, null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);

		// One sibling active
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("sibling1");
		contextManager.setActiveContextIds(activeContextIds);
		assertEquals(
				"When only the first sibling is active, only the first binding is active",
				binding1, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));

		// Other sibling active
		activeContextIds.clear();
		activeContextIds.add("sibling2");
		contextManager.setActiveContextIds(activeContextIds);
		assertEquals(
				"When only the second sibling is active, only the second binding is active",
				binding2, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));

		// Both siblings are active
		activeContextIds.add("sibling1");
		contextManager.setActiveContextIds(activeContextIds);
		assertEquals("When both contexts are active, a conflict should occur",
				null, bindingManager
						.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}

	/**
	 * <p>
	 * Tests whether a user-defined binding will override the exact same binding
	 * defined in the system.
	 * </p>
	 * <ul>
	 * <li>Binding1(commandId="user",schemeId="na",contextId="na",locale=null,platform=null,type=USER)</li>
	 * <li>Binding2(commandId="system",schemeId="na",contextId="na",locale=null,platform=null,type=SYSTEM)</li>
	 * </ul>
	 * <p>
	 * Binding1 should win.
	 * </p>
	 *
	 * @throws NotDefinedException
	 *             If the scheme we try to activate is not defined.
	 */
	@Test
	public void testUserOverride() throws NotDefinedException {
		final Context context = contextManager.getContext("na");
		context.define("name", "description", null);

		final Scheme scheme = bindingManager.getScheme("na");
		scheme.define("name", "description", null);

		bindingManager.setActiveScheme(scheme);
		final Set<String> activeContextIds = new HashSet<>();
		activeContextIds.add("na");
		contextManager.setActiveContextIds(activeContextIds);

		final Binding binding1 = new TestBinding("user", "na", "na", null,
				null, Binding.USER, null);
		bindingManager.addBinding(binding1);
		final Binding binding2 = new TestBinding("system", "na", "na", null,
				null, Binding.SYSTEM, null);
		bindingManager.addBinding(binding2);
		assertEquals("The user-defined binding should be active", binding1,
				bindingManager.getPerfectMatch(TestBinding.TRIGGER_SEQUENCE));
	}
}
