/**
   * Parse the following sensor configuration:
   *
   * <pre>{@code
   * <sensor id = "1098" name = "s1098" type = "custom">
   *   <include type = "command" ref = "98" />
   * </sensor>
   * }</pre>
   *
   * @throws Exception if test fails
   */
  @Test
  public void testArbitraryCustomSensorBuild() throws Exception {
    Sensor s = buildSensor(1098);
    Assert.assertTrue(s.getName().equals("s1098"));
    Assert.assertTrue(s.getProperties().size() == 0);

    Assert.assertTrue(s instanceof StateSensor);

    StateSensor state = (StateSensor) s;

    Assert.assertTrue(state.processEvent("1").getValue().equals("1"));
    Assert.assertTrue(state.processEvent("2").getValue().equals("2"));
    Assert.assertTrue(state.processEvent("3").getValue().equals("3"));
    Assert.assertTrue(state.processEvent("4").getValue().equals("4"));
    Assert.assertTrue(state.processEvent("foo").getValue().equals("foo"));
  }
  /**
   * Parse the following sensor configuration:
   *
   * <pre>{@code
   * Two-state CUSTOM sensor configuration.
   *
   * Read command return value 'on' is mapped to 'open'
   * Read command return value 'off' is mapped to 'close'
   *
   * <sensor id = "1009" name = "Door power sensor" type = "custom">
   *   <include type = "command" ref = "98" />
   *   <state name = "open" value = "on" />
   *   <state name = "close" value = "off" />
   * </sensor>
   * }</pre>
   *
   * @throws Exception if test fails
   */
  @Test
  public void testCustomSensorBuild() throws Exception {
    Sensor s = buildSensor(SensorType.CUSTOM);
    Assert.assertTrue(s.getName().equals("Door power sensor"));
    Assert.assertTrue(s.getProperties().size() == 2);
    Assert.assertTrue(s.getProperties().keySet().contains("state-1"));
    Assert.assertTrue(s.getProperties().keySet().contains("state-2"));
    Assert.assertTrue(s.getProperties().values().contains("on"));
    Assert.assertTrue(s.getProperties().values().contains("off"));

    Assert.assertTrue(s instanceof StateSensor);

    StateSensor state = (StateSensor) s;

    Assert.assertTrue(state.processEvent("on").getValue().equals("open"));
    Assert.assertTrue(state.processEvent("off").getValue().equals("close"));

    Assert.assertTrue(state.processEvent("foo").getValue().equals("foo"));
  }
  /**
   * Parse the following sensor configuration:
   *
   * <pre>{@code
   *  Four-state CUSTOM sensor configuration. Mixed mappings.
   *
   *  Read command return value '1' is mapped to 'one'
   *  Read command return value '2' is not mapped.
   *  Read command return value '3' is mapped to 'three'
   *  Read command return value '4' is not mapped.
   *
   * <sensor id = "1099" name = "Numbers" type = "custom">
   *   <include type = "command" ref = "98" />
   *
   *   <state name = "one" value = "1" />
   *   <state name = "three" value = "3" />
   * </sensor>
   * }</pre>
   *
   * @throws Exception if test fails
   */
  @Test
  public void testPartiallyMappedCustomSensorBuild() throws Exception {
    Sensor s = buildSensor(1099);
    Assert.assertTrue(s.getName().equals("Numbers"));
    Assert.assertTrue(s.getProperties().size() == 2);
    Assert.assertTrue(s.getProperties().keySet().contains("state-1"));
    Assert.assertTrue(s.getProperties().keySet().contains("state-2"));
    Assert.assertTrue(s.getProperties().values().contains("1"));
    Assert.assertTrue(s.getProperties().values().contains("3"));

    Assert.assertTrue(s instanceof StateSensor);

    StateSensor state = (StateSensor) s;

    Assert.assertTrue(state.processEvent("1").getValue().equals("one"));
    Assert.assertTrue(state.processEvent("2").getValue().equals("2"));
    Assert.assertTrue(state.processEvent("3").getValue().equals("three"));
    Assert.assertTrue(state.processEvent("4").getValue().equals("4"));

    Assert.assertTrue(state.processEvent("foo").getValue().equals("foo"));
  }
  /**
   * Same as {@link #testSwitchStateMappingWithNoValue} above, just uses an event listener instead
   * of polling sensor command.
   *
   * <p>See http://jira.openremote.org/browse/ORCJAVA-73
   *
   * @throws Exception if test fails
   */
  @Test
  public void testSwitchStateMappingWithNoValueAndListener() throws Exception {
    Sensor s = buildSensorWithID(727);

    s.start();

    Assert.assertTrue(s.getName().equals("se2"));
    Assert.assertTrue(s.getSensorID() == 727);

    // switch sensor states should not show up as properties, even if mapped...

    Assert.assertTrue(s.getProperties().size() == 0);

    // should get either one depending what the state of the listener is

    String val = cache.queryStatus(727);

    Assert.assertTrue(
        "Expected either 'on' or 'off', got " + val, val.equals("off") || val.equals("on"));

    Assert.assertTrue(s instanceof SwitchSensor);

    StateSensor state = (StateSensor) s;

    // check that states are in place despite funky XML model...

    Assert.assertTrue(state.processEvent("on").getValue().equals("on"));
    Assert.assertTrue(state.processEvent("off").getValue().equals("off"));
    Assert.assertTrue(state.processEvent("foo").getValue().equals(Sensor.UNKNOWN_STATUS));

    String status = cache.queryStatus(727);

    // should have something since its a listener...

    Assert.assertFalse(status.equals(Sensor.UNKNOWN_STATUS));

    Assert.assertTrue(s.isEventListener());
    Assert.assertFalse(s.isPolling());
  }
  /**
   * Test against what could be qualified as a bug that has now become a feature and we need to make
   * sure we don't regress on it unintentionally should we try to fix the bug again.
   *
   * <p>Current tooling generates a style of switch sensors in XML that makes very little sense:
   *
   * <pre>{@code
   * <sensor id = "717" name = "se" type = "switch">
   *   <include type = "command" ref = "96" />
   *   <state name = "on" />
   *   <state name = "off" />
   * </sensor>
   * }</pre>
   *
   * It makes no sense because switch can only ever return on/off as states and no mapping is
   * provided, making the state declarations redundant. But because tooling does generate this, we
   * need to make sure we correctly parse it.
   *
   * <p>See http://jira.openremote.org/browse/ORCJAVA-73
   *
   * @throws Exception if test fails
   */
  @Test
  public void testSwitchStateMappingWithNoValue() throws Exception {
    Sensor s = buildSensorWithID(717);

    Assert.assertTrue(s.getName().equals("se"));
    Assert.assertTrue(s.getSensorID() == 717);

    // switch sensor states should not show up as properties, even if mapped...

    Assert.assertTrue(s.getProperties().size() == 0);

    commandService.trigger("666", "click");

    String offValue = getSensorValueFromCache(717);

    Assert.assertTrue("Expected 'off', got '" + offValue + "'", offValue.equals("off"));

    commandService.trigger("555", "click");

    String onValue = getSensorValueFromCache(717);

    Assert.assertTrue("Expected 'on', got '" + onValue + "'", onValue.equals("on"));

    Assert.assertTrue(s instanceof SwitchSensor);

    StateSensor state = (StateSensor) s;

    // check that states are in place despite funky XML model...

    Assert.assertTrue(state.processEvent("on").getValue().equals("on"));
    Assert.assertTrue(state.processEvent("off").getValue().equals("off"));
    Assert.assertTrue(state.processEvent("foo").getValue().equals(Sensor.UNKNOWN_STATUS));

    Assert.assertTrue(s.isPolling());
    Assert.assertFalse(s.isEventListener());
  }
  /**
   * Parse the following sensor when deployed through a complete controller.xml document.
   *
   * <pre>{@code
   * <sensor id="1013" name="s1013" type="switch">
   *   <include type="command" ref="962" />
   *   <state name="on" value="open" />
   *   <state name="off" value="close" />
   * </sensor>
   *
   * }</pre>
   *
   * @throws Exception if test fails
   */
  @Test
  public void testMappedSwitchSensorBuildWithListener() throws Exception {
    Sensor s = buildSensor(1013);
    Assert.assertTrue(s instanceof SwitchSensor);
    Assert.assertTrue(s.getName().equals("s1013"));
    Assert.assertTrue(s.getProperties().size() == 0);

    Assert.assertTrue(s.getSensorID() == 1013);
    Assert.assertFalse(s.isRunning());
    Assert.assertFalse(s.isPolling());
    Assert.assertTrue(s.isEventListener());

    Assert.assertFalse(s.equals(null));
    Assert.assertTrue(s.equals(s));
    Assert.assertFalse(s.equals(buildSensor(SensorType.SWITCH)));
    Assert.assertFalse(s.equals(buildSensor(SensorType.RANGE)));
  }