/** @author Symphorien Wanko */
public class PopupMessageHandlerSLick extends TestSuite implements BundleActivator {
  /** Logger for this class */
  private static Logger logger = Logger.getLogger(PopupMessageHandlerSLick.class);

  /** our bundle context */
  protected static BundleContext bundleContext = null;

  /** implements BundleActivator.start() */
  public void start(BundleContext bc) throws Exception {
    logger.info("starting popup message test ");

    bundleContext = bc;

    setName("PopupMessageHandlerSLick");

    Hashtable<String, String> properties = new Hashtable<String, String>();

    properties.put("service.pid", getName());

    // we maybe are running on machine without WM and systray
    // (test server machine), skip tests
    if (ServiceUtils.getService(bc, SystrayService.class) != null) {
      addTest(TestPopupMessageHandler.suite());
    }

    bundleContext.registerService(getClass().getName(), this, properties);
  }

  /** implements BundleActivator.stop() */
  public void stop(BundleContext bc) throws Exception {}
}
/**
 * Tests in this class verify whether a precreated contact list is still there and whether it
 * creating contact groups works as expected.
 *
 * @author Emil Ivov
 */
public class TestOperationSetPersistentPresence extends TestCase {
  private static final Logger logger = Logger.getLogger(TestOperationSetPersistentPresence.class);

  private GibberishSlickFixture fixture = new GibberishSlickFixture();
  private OperationSetPersistentPresence opSetPersPresence1 = null;
  private OperationSetPersistentPresence opSetPersPresence2 = null;
  private static final String testGroupName = "NewGroup";
  private static final String testGroupName2 = "Renamed";

  public TestOperationSetPersistentPresence(String name) {
    super(name);
  }

  /**
   * Creates a test suite containing all tests of this class followed by test methods that we want
   * executed in a specified order.
   *
   * @return the Test suite to run
   */
  public static Test suite() {
    TestSuite suite = new TestSuite();

    // the following 2 need to be run in the specified order.
    // (postTestRemoveGroup() needs the group created from
    // postTestCreateGroup() )
    suite.addTest(new TestOperationSetPersistentPresence("postTestCreateGroup"));

    // rename
    suite.addTest(new TestOperationSetPersistentPresence("postTestRenameGroup"));

    suite.addTest(new TestOperationSetPersistentPresence("postTestRemoveGroup"));

    // create the contact list
    suite.addTest(new TestOperationSetPersistentPresence("prepareContactList"));

    suite.addTestSuite(TestOperationSetPersistentPresence.class);

    return suite;
  }

  @Override
  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();

    Map<String, OperationSet> supportedOperationSets1 =
        fixture.provider1.getSupportedOperationSets();

    if (supportedOperationSets1 == null || supportedOperationSets1.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this Gibberish implementation. ");

    // get the operation set presence here.
    opSetPersPresence1 =
        (OperationSetPersistentPresence)
            supportedOperationSets1.get(OperationSetPersistentPresence.class.getName());

    // if still null then the implementation doesn't offer a presence
    // operation set which is unacceptable for gibberish.
    if (opSetPersPresence1 == null)
      throw new NullPointerException(
          "An implementation of the gibberish service must provide an "
              + "implementation of at least the one of the Presence "
              + "Operation Sets");

    // lets do it once again for the second provider
    Map<String, OperationSet> supportedOperationSets2 =
        fixture.provider2.getSupportedOperationSets();

    if (supportedOperationSets2 == null || supportedOperationSets2.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this Gibberish implementation. ");

    // get the operation set presence here.
    opSetPersPresence2 =
        (OperationSetPersistentPresence)
            supportedOperationSets2.get(OperationSetPersistentPresence.class.getName());

    // if still null then the implementation doesn't offer a presence
    // operation set which is unacceptable for Gibberish.
    if (opSetPersPresence2 == null)
      throw new NullPointerException(
          "An implementation of the Gibberish service must provide an "
              + "implementation of at least the one of the Presence "
              + "Operation Sets");
  }

  @Override
  protected void tearDown() throws Exception {
    fixture.tearDown();
    super.tearDown();
  }

  /**
   * Retrieves a server stored contact list and checks whether it contains all contacts that have
   * been added there during the initialization phase by the testerAgent.
   */
  public void testRetrievingServerStoredContactList() {
    ContactGroup rootGroup = opSetPersPresence1.getServerStoredContactListRoot();

    logger.debug("=========== Server Stored Contact List =================");

    logger.debug(
        "rootGroup="
            + rootGroup.getGroupName()
            + " rootGroup.childContacts="
            + rootGroup.countContacts()
            + "rootGroup.childGroups="
            + rootGroup.countSubgroups()
            + "Printing rootGroupContents=\n"
            + rootGroup.toString());

    Hashtable<String, List<String>> expectedContactList =
        GibberishSlickFixture.preInstalledBuddyList;

    logger.debug("============== Expected Contact List ===================");
    logger.debug(expectedContactList);

    // Go through the contact list retrieved by the persistence presence set
    // and remove the name of every contact and group that we find there from
    // the expected contct list hashtable.
    Iterator<ContactGroup> groups = rootGroup.subgroups();
    while (groups.hasNext()) {
      ContactGroup group = groups.next();

      List<String> expectedContactsInGroup = expectedContactList.get(group.getGroupName());

      // When sending the offline message
      // the sever creates a group NotInContactList,
      // because the buddy we are sending message to is not in
      // the contactlist. So this group must be ignored
      if (!group.getGroupName().equals("NotInContactList")) {
        assertNotNull(
            "Group "
                + group.getGroupName()
                + " was returned by "
                + "the server but was not in the expected contact list.",
            expectedContactsInGroup);

        Iterator<Contact> contactsIter = group.contacts();
        while (contactsIter.hasNext()) {
          String contactID = contactsIter.next().getAddress();
          expectedContactsInGroup.remove(contactID);
        }

        // If we've removed all the sub contacts, remove the group too.
        if (expectedContactsInGroup.size() == 0) expectedContactList.remove(group.getGroupName());
      }
    }

    // whatever we now have in the expected contact list snapshot are groups,
    // that have been added by the testerAgent but that were not retrieved
    // by the persistent presence operation set.
    assertTrue(
        "The following contacts were on the server sidec contact "
            + "list, but were not returned by the pers. pres. op. set"
            + expectedContactList.toString(),
        expectedContactList.isEmpty());
  }

  /**
   * Creates a group in the server stored contact list, makes sure that the corresponding event has
   * been generated and verifies that the group is in the list.
   *
   * @throws java.lang.Exception
   */
  public void postTestCreateGroup() throws Exception {
    // first clear the list
    fixture.clearProvidersLists();

    Object o = new Object();
    synchronized (o) {
      o.wait(3000);
    }

    logger.trace("testing creation of server stored groups");
    // first add a listener
    GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
    opSetPersPresence1.addServerStoredGroupChangeListener(groupChangeCollector);

    // create the group
    opSetPersPresence1.createServerStoredContactGroup(
        opSetPersPresence1.getServerStoredContactListRoot(), testGroupName);

    groupChangeCollector.waitForEvent(10000);

    opSetPersPresence1.removeServerStoredGroupChangeListener(groupChangeCollector);

    // check whether we got group created event
    assertEquals("Collected Group Change events: ", 1, groupChangeCollector.collectedEvents.size());

    assertEquals(
        "Group name.",
        testGroupName,
        ((ServerStoredGroupEvent) groupChangeCollector.collectedEvents.get(0))
            .getSourceGroup()
            .getGroupName());

    // check whether the group is retrievable
    ContactGroup group =
        opSetPersPresence1.getServerStoredContactListRoot().getGroup(testGroupName);

    assertNotNull("A newly created group was not in the contact list.", group);

    assertEquals("New group name", testGroupName, group.getGroupName());

    // when opearting with groups . the group must have entries
    // so changes to take effect. Otherwise group will be lost after loggingout
    try {
      opSetPersPresence1.subscribe(group, fixture.userID2);

      synchronized (o) {
        o.wait(1500);
      }
    } catch (Exception ex) {
      fail("error adding entry to group : " + group.getGroupName() + " " + ex.getMessage());
    }
  }

  /**
   * Removes the group created in the server stored contact list by the create group test, makes
   * sure that the corresponding event has been generated and verifies that the group is not in the
   * list any more.
   */
  public void postTestRemoveGroup() {
    logger.trace("testing removal of server stored groups");

    // first add a listener
    GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
    opSetPersPresence1.addServerStoredGroupChangeListener(groupChangeCollector);

    try {
      // remove the group
      opSetPersPresence1.removeServerStoredContactGroup(
          opSetPersPresence1.getServerStoredContactListRoot().getGroup(testGroupName2));
    } catch (OperationFailedException ex) {
      logger.error("error removing group", ex);
    }

    groupChangeCollector.waitForEvent(10000);

    opSetPersPresence1.removeServerStoredGroupChangeListener(groupChangeCollector);

    // check whether we got group created event
    assertEquals("Collected Group Change event", 1, groupChangeCollector.collectedEvents.size());

    assertEquals(
        "Group name.",
        testGroupName2,
        ((ServerStoredGroupEvent) groupChangeCollector.collectedEvents.get(0))
            .getSourceGroup()
            .getGroupName());

    // check whether the group is still on the contact list
    ContactGroup group =
        opSetPersPresence1.getServerStoredContactListRoot().getGroup(testGroupName2);

    assertNull("A freshly removed group was still on the contact list.", group);
  }

  /**
   * Renames our test group and checks whether corresponding events are triggered. Verifies whether
   * the group has really changed its name and whether it is findable by its new name. Also makes
   * sure that it does not exist under its previous name any more.
   */
  public void postTestRenameGroup() {
    logger.trace("Testing renaming groups.");

    ContactGroup group =
        opSetPersPresence1.getServerStoredContactListRoot().getGroup(testGroupName);

    // first add a listener
    GroupChangeCollector groupChangeCollector = new GroupChangeCollector();
    opSetPersPresence1.addServerStoredGroupChangeListener(groupChangeCollector);

    // change the name and wait for a confirmation event
    opSetPersPresence1.renameServerStoredContactGroup(group, testGroupName2);

    groupChangeCollector.waitForEvent(10000);

    opSetPersPresence1.removeServerStoredGroupChangeListener(groupChangeCollector);

    // examine the event
    assertEquals("Collected Group Change event", 1, groupChangeCollector.collectedEvents.size());

    assertEquals(
        "Group name.",
        testGroupName2,
        ((ServerStoredGroupEvent) groupChangeCollector.collectedEvents.get(0))
            .getSourceGroup()
            .getGroupName());

    // check whether the group is still on the contact list
    ContactGroup oldGroup =
        opSetPersPresence1.getServerStoredContactListRoot().getGroup(testGroupName);

    assertNull("A group was still findable by its old name after renaming.", oldGroup);

    // make sure that we could find the group by its new name.
    ContactGroup newGroup =
        opSetPersPresence1.getServerStoredContactListRoot().getGroup(testGroupName2);

    assertNotNull("Could not find a renamed group by its new name.", newGroup);
  }

  /**
   * Create the contact list. Later will be test to be sure that creating is ok
   *
   * @throws Exception
   */
  public void prepareContactList() throws Exception {
    fixture.clearProvidersLists();

    Object o = new Object();
    synchronized (o) {
      o.wait(3000);
    }

    String contactList =
        System.getProperty(GibberishProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME, null);

    logger.debug(
        "The "
            + GibberishProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME
            + " property is set to="
            + contactList);

    if (contactList == null || contactList.trim().length() < 6) // at least 4 for a UIN, 1 for the
      // dot and 1 for the grp name
      throw new IllegalArgumentException(
          "The "
              + GibberishProtocolProviderServiceLick.CONTACT_LIST_PROPERTY_NAME
              + " property did not contain a contact list.");
    StringTokenizer tokenizer = new StringTokenizer(contactList, " \n\t");

    logger.debug("tokens contained by the CL tokenized=" + tokenizer.countTokens());

    Hashtable<String, List<String>> contactListToCreate = new Hashtable<String, List<String>>();

    // go over all group.uin tokens
    while (tokenizer.hasMoreTokens()) {
      String groupUinToken = tokenizer.nextToken();
      int dotIndex = groupUinToken.indexOf(".");

      if (dotIndex == -1) {
        throw new IllegalArgumentException(groupUinToken + " is not a valid Group.UIN token");
      }

      String groupName = groupUinToken.substring(0, dotIndex);
      String uin = groupUinToken.substring(dotIndex + 1);

      if (groupName.trim().length() < 1 || uin.trim().length() < 4) {
        throw new IllegalArgumentException(
            groupName + " or " + uin + " are not a valid group name or Gibberish user id.");
      }

      // check if we've already seen this group and if not - add it
      List<String> uinInThisGroup = contactListToCreate.get(groupName);
      if (uinInThisGroup == null) {
        uinInThisGroup = new ArrayList<String>();
        contactListToCreate.put(groupName, uinInThisGroup);
      }

      uinInThisGroup.add(uin);
    }

    // now init the list
    Enumeration<String> newGroupsEnum = contactListToCreate.keys();

    // go over all groups in the contactsToAdd table
    while (newGroupsEnum.hasMoreElements()) {
      String groupName = newGroupsEnum.nextElement();
      logger.debug("Will add group " + groupName);

      opSetPersPresence1.createServerStoredContactGroup(
          opSetPersPresence1.getServerStoredContactListRoot(), groupName);

      ContactGroup newlyCreatedGroup =
          opSetPersPresence1.getServerStoredContactListRoot().getGroup(groupName);

      Iterator<String> contactsToAddToThisGroup = contactListToCreate.get(groupName).iterator();
      while (contactsToAddToThisGroup.hasNext()) {
        String id = contactsToAddToThisGroup.next();

        logger.debug("Will add buddy " + id);
        opSetPersPresence1.subscribe(newlyCreatedGroup, id);
      }
    }

    // store the created contact list for later reference
    GibberishSlickFixture.preInstalledBuddyList = contactListToCreate;
  }

  /**
   * The class would listen for and store received events delivered to
   * <tt>ServerStoredGroupListener</tt>s.
   */
  private class GroupChangeCollector implements ServerStoredGroupListener {
    public ArrayList<EventObject> collectedEvents = new ArrayList<EventObject>();

    /**
     * Blocks until at least one event is received or until waitFor miliseconds pass (whicever
     * happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedEvents.size() > 0) return;

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    /**
     * Called whnever an indication is received that a new server stored group is created.
     *
     * @param evt a ServerStoredGroupChangeEvent containing a reference to the newly created group.
     */
    public void groupCreated(ServerStoredGroupEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called when an indication is received that the name of a server stored contact group has
     * changed.
     *
     * @param evt a ServerStoredGroupChangeEvent containing the details of the name change.
     */
    public void groupNameChanged(ServerStoredGroupEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called whnever an indication is received that an existing server stored group has been
     * removed.
     *
     * @param evt a ServerStoredGroupChangeEvent containing a reference to the newly created group.
     */
    public void groupRemoved(ServerStoredGroupEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called whnever an indication is received that an existing server stored group has been
     * resolved.
     *
     * @param evt a ServerStoredGroupChangeEvent containing a reference to the resolved group.
     */
    public void groupResolved(ServerStoredGroupEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }
  }

  /** The class would listen for and store received subscription modification events. */
  private class SubscriptionEventCollector implements SubscriptionListener {
    public ArrayList<EventObject> collectedEvents = new ArrayList<EventObject>();

    /**
     * Blocks until at least one event is received or until waitFor milliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of milliseconds that we should be waiting for an event before
     *     simply bailing out.
     */
    public void waitForEvent(long waitFor) {
      logger.trace("Waiting for a persistent subscription event");

      synchronized (this) {
        if (collectedEvents.size() > 0) {
          logger.trace("SubEvt already received. " + collectedEvents);
          return;
        }

        try {
          wait(waitFor);
          if (collectedEvents.size() > 0) logger.trace("Received a SubEvt in provider status.");
          else logger.trace("No SubEvt received for " + waitFor + "ms.");
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    /**
     * Stores the received subsctiption and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionCreated(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subsctiption and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionRemoved(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subsctiption and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionFailed(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subsctiption and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionResolved(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subsctiption and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionMoved(SubscriptionMovedEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subsctiption and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void contactModified(ContactPropertyChangeEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }
  }
}
/**
 * Performs testing on protocol provider methods.
 *
 * @todo add more detailed docs once the tests are written.
 * @author Emil Ivov
 * @author Valentin Martinet
 */
public class TestProtocolProviderServiceJabberImpl extends TestCase {
  private static final Logger logger =
      Logger.getLogger(TestProtocolProviderServiceJabberImpl.class);

  private JabberSlickFixture fixture = new JabberSlickFixture();

  /** An event adapter that would collec registation state change events */
  public RegistrationEventCollector regEvtCollector1 = new RegistrationEventCollector();

  /** An event adapter that would collec registation state change events */
  public RegistrationEventCollector regEvtCollector2 = new RegistrationEventCollector();

  /** An event adapter that would collec registation state change events */
  public RegistrationEventCollector regEvtCollector3 = new RegistrationEventCollector();

  /**
   * Creates a test encapsulator for the method with the specified name.
   *
   * @param name the name of the method this test should run.
   */
  public TestProtocolProviderServiceJabberImpl(String name) {
    super(name);
  }

  /**
   * Initializes the fixture.
   *
   * @throws Exception if super.setUp() throws one.
   */
  @Override
  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();
  }

  /**
   * Tears the fixture down.
   *
   * @throws Exception if fixture.tearDown() fails.
   */
  @Override
  protected void tearDown() throws Exception {
    fixture.tearDown();
    super.tearDown();
  }

  /**
   * Makes sure that the instance of the Jabber protocol provider that we're going to use for
   * testing is properly initialized and registered with a Jabber registrar. This MUST be called
   * before any other online testing of the Jabber provider so that we won't have to reregister for
   * every single test.
   *
   * <p>The method also verifies that a registration event is fired upon succesful registration and
   * collected by our event collector.
   *
   * @throws OperationFailedException if provider.register() fails.
   */
  public void testRegister() throws OperationFailedException {
    // add an event collector that will collect all events during the
    // registration and allow us to later inspect them and make sure
    // they were properly dispatched.
    fixture.provider1.addRegistrationStateChangeListener(regEvtCollector1);
    fixture.provider2.addRegistrationStateChangeListener(regEvtCollector2);
    fixture.provider3.addRegistrationStateChangeListener(regEvtCollector3);

    // register our three providers
    fixture.provider1.register(
        new SecurityAuthorityImpl(
            System.getProperty(
                    JabberProtocolProviderServiceLick.ACCOUNT_1_PREFIX
                        + ProtocolProviderFactory.PASSWORD)
                .toCharArray()));
    fixture.provider2.register(
        new SecurityAuthorityImpl(
            System.getProperty(
                    JabberProtocolProviderServiceLick.ACCOUNT_2_PREFIX
                        + ProtocolProviderFactory.PASSWORD)
                .toCharArray()));
    fixture.provider3.register(
        new SecurityAuthorityImpl(
            System.getProperty(
                    JabberProtocolProviderServiceLick.ACCOUNT_3_PREFIX
                        + ProtocolProviderFactory.PASSWORD)
                .toCharArray()));

    // give it enough time to register. We won't really have to wait all this
    // time since the registration event collector would notify us the moment
    // we get signed on.
    logger.debug("Waiting for registration to complete ...");

    regEvtCollector1.waitForEvent(15000);
    regEvtCollector2.waitForEvent(40000);
    regEvtCollector3.waitForEvent(60000);

    // make sure that the registration process trigerred the corresponding
    // events.
    assertTrue(
        "No events were dispatched during the registration process.",
        regEvtCollector1.collectedNewStates.size() > 0);

    assertTrue(
        "No registration event notifying of registration was dispatched. "
            + "All events were: "
            + regEvtCollector1.collectedNewStates,
        regEvtCollector1.collectedNewStates.contains(RegistrationState.REGISTERED));

    // now the same for provider 2
    assertTrue(
        "No events were dispatched during the registration process " + "of provider2.",
        regEvtCollector2.collectedNewStates.size() > 0);

    assertTrue(
        "No registration event notifying of registration was dispatched. "
            + "All events were: "
            + regEvtCollector2.collectedNewStates,
        regEvtCollector2.collectedNewStates.contains(RegistrationState.REGISTERED));

    // now the same for provider 3
    assertTrue(
        "No events were dispatched during the registration process " + "of provider3.",
        regEvtCollector3.collectedNewStates.size() > 0);

    assertTrue(
        "No registration event notifying of registration was dispatched. "
            + "All events were: "
            + regEvtCollector3.collectedNewStates,
        regEvtCollector3.collectedNewStates.contains(RegistrationState.REGISTERED));

    fixture.provider1.removeRegistrationStateChangeListener(regEvtCollector1);
    fixture.provider2.removeRegistrationStateChangeListener(regEvtCollector2);
    fixture.provider3.removeRegistrationStateChangeListener(regEvtCollector3);
  }

  /**
   * Verifies that all operation sets have the type they are declarded to have.
   *
   * @throws java.lang.Exception if a class indicated in one of the keys could not be forName()ed.
   */
  public void testOperationSetTypes() throws Exception {
    Map<String, OperationSet> supportedOperationSets =
        fixture.provider1.getSupportedOperationSets();

    // make sure that keys (which are supposed to be class names) correspond
    // what the class of the values recorded against them.
    for (Map.Entry<String, OperationSet> entry : supportedOperationSets.entrySet()) {
      String setName = entry.getKey();
      Object opSet = entry.getValue();

      assertTrue(
          opSet + " was not an instance of " + setName + " as declared",
          Class.forName(setName).isInstance(opSet));
    }
  }

  /**
   * A class that would plugin as a registration listener to a protocol provider and simply record
   * all events that it sees and notifyAll() if it sees an event that notifies us of a completed
   * registration.
   */
  public class RegistrationEventCollector implements RegistrationStateChangeListener {
    public List<RegistrationState> collectedNewStates = new LinkedList<RegistrationState>();

    /**
     * The method would simply register all received events so that they could be available for
     * later inspection by the unit tests. In the case where a registration event notifying us of a
     * completed registration is seen, the method would call notifyAll().
     *
     * @param evt ProviderStatusChangeEvent the event describing the status change.
     */
    public void registrationStateChanged(RegistrationStateChangeEvent evt) {
      logger.debug("Received a RegistrationStateChangeEvent: " + evt);

      collectedNewStates.add(evt.getNewState());

      if (evt.getNewState().equals(RegistrationState.REGISTERED)) {
        logger.debug("We're registered and will notify those who wait");
        synchronized (this) {
          notifyAll();
        }
      }
    }

    /**
     * Blocks until an event notifying us of the awaited state change is received or until waitFor
     * miliseconds pass (whichever happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      logger.trace("Waiting for a RegistrationStateChangeEvent ");

      synchronized (this) {
        if (collectedNewStates.contains(RegistrationState.REGISTERED)) {
          logger.trace("Event already received. " + collectedNewStates);
          return;
        }

        try {
          wait(waitFor);

          if (collectedNewStates.size() > 0)
            logger.trace("Received a RegistrationStateChangeEvent.");
          else logger.trace("No RegistrationStateChangeEvent received for " + waitFor + "ms.");

        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a " + "RegistrationStateChangeEvent", ex);
        }
      }
    }
  }
}
/**
 * Tests ICQ implementations of a Presence Operation Set. Tests in this class verify functionality
 * such as: Changing local (our own) status and corresponding event dispatching; Querying status of
 * contacts, Subscribing for presence notifications upong status changes of specific contacts.
 *
 * <p>Using a custom suite() method, we make sure that apart from standard test methods (those with
 * a <tt>test</tt> prefix) we also execute those that we want run in a specific order like for
 * example - postTestSubscribe() and postTestUnsubscribe().
 *
 * <p>
 *
 * @author Emil Ivov
 * @author Damian Minkov
 */
public class TestOperationSetPresence extends TestCase {
  private static final Logger logger = Logger.getLogger(TestOperationSetPresence.class);

  private IcqSlickFixture fixture = new IcqSlickFixture();
  private OperationSetPresence operationSetPresence = null;
  private String statusMessageRoot = new String("Our status is now: ");

  // be sure its only one
  private static AuthEventCollector authEventCollector = new AuthEventCollector();

  public TestOperationSetPresence(String name) {
    super(name);
  }

  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();

    Map<String, OperationSet> supportedOperationSets = fixture.provider.getSupportedOperationSets();

    if (supportedOperationSets == null || supportedOperationSets.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this ICQ implementation. ");

    // get the operation set presence here.
    operationSetPresence =
        (OperationSetPresence) supportedOperationSets.get(OperationSetPresence.class.getName());

    // if the op set is null then the implementation doesn't offer a presence
    // operation set which is unacceptable for icq.
    if (operationSetPresence == null) {
      throw new NullPointerException(
          "An implementation of the ICQ service must provide an "
              + "implementation of at least the one of the Presence "
              + "Operation Sets");
    }
  }

  protected void tearDown() throws Exception {
    super.tearDown();

    fixture.tearDown();
  }

  /**
   * Creates a test suite containing all tests of this class followed by test methods that we want
   * executed in a specified order.
   *
   * @return Test
   */
  public static Test suite() {
    // return an (almost) empty suite if we're running in offline mode.
    if (IcqSlickFixture.onlineTestingDisabled) {
      TestSuite suite = new TestSuite();
      // the only test around here that we could run without net
      // connectivity
      suite.addTest(new TestOperationSetPresence("testSupportedStatusSetForCompleteness"));
      return suite;
    }

    TestSuite suite = new TestSuite(TestOperationSetPresence.class);

    // the following 2 need to be run in the specified order.
    // (postTestUnsubscribe() needs the subscription created from
    // postTestSubscribe() )
    suite.addTest(new TestOperationSetPresence("postTestSubscribe"));
    suite.addTest(new TestOperationSetPresence("postTestUnsubscribe"));

    // execute this test after postTestSubscribe
    // to be sure that AuthorizationHandler is installed
    suite.addTest(new TestOperationSetPresence("postTestReceiveAuthorizatinonRequest"));

    return suite;
  }

  /** Verifies that all necessary ICQ test states are supported by the implementation. */
  public void testSupportedStatusSetForCompleteness() {
    // first create a local list containing the presence status instances
    // supported by the underlying implementation.
    Iterator<PresenceStatus> supportedStatusSetIter = operationSetPresence.getSupportedStatusSet();

    List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>();
    while (supportedStatusSetIter.hasNext()) {
      supportedStatusSet.add(supportedStatusSetIter.next());
    }

    // create a copy of the MUST status set and remove any matching status
    // that is also present in the supported set.
    List<?> requiredStatusSetCopy = (List<?>) IcqStatusEnum.icqStatusSet.clone();

    requiredStatusSetCopy.removeAll(supportedStatusSet);

    // if we have anything left then the implementation is wrong.
    int unsupported = requiredStatusSetCopy.size();
    assertTrue(
        "There are " + unsupported + " statuses as follows:" + requiredStatusSetCopy,
        unsupported == 0);
  }

  /**
   * Verify that changing state to AWAY works as supposed to and that it generates the corresponding
   * event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToAway() throws Exception {
    subtestStateTransition(IcqStatusEnum.AWAY);
  }

  /**
   * Verify that changing state to NOT_AVAILABLE works as supposed to and that it generates the
   * corresponding event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToNotAvailable() throws Exception {
    subtestStateTransition(IcqStatusEnum.NOT_AVAILABLE);
  }

  /**
   * Verify that changing state to DND works as supposed to and that it generates the corresponding
   * event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToDnd() throws Exception {
    subtestStateTransition(IcqStatusEnum.DO_NOT_DISTURB);
  }

  /**
   * Verify that changing state to INVISIBLE works as supposed to and that it generates the
   * corresponding event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToInvisible() throws Exception {
    subtestStateTransition(IcqStatusEnum.INVISIBLE);
  }

  /**
   * Verify that changing state to OCCUPIED works as supposed to and that it generates the
   * corresponding event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToOccupied() throws Exception {
    subtestStateTransition(IcqStatusEnum.OCCUPIED);
  }

  /**
   * Verify that changing state to FREE_FOR_CHAT works as supposed to and that it generates the
   * corresponding event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToFreeForChat() throws Exception {
    subtestStateTransition(IcqStatusEnum.FREE_FOR_CHAT);
  }

  /**
   * Verify that changing state to ONLINE works as supposed to and that it generates the
   * corresponding event.
   *
   * @throws Exception in case a failure occurs while the operation set is switching to the new
   *     state.
   */
  public void testChangingStateToOnline() throws Exception {
    //
    // java.util.logging.Logger.getLogger("net.kano").setLevel(java.util.logging.Level.FINEST);
    subtestStateTransition(IcqStatusEnum.ONLINE);
    //
    // java.util.logging.Logger.getLogger("net.kano").setLevel(java.util.logging.Level.WARNING);
  }

  /**
   * Used by methods testing state transiotions
   *
   * @param newStatus the IcqStatusEnum field corresponding to the status that we'd like the
   *     opeation set to enter.
   * @throws Exception in case changing the state causes an exception
   */
  public void subtestStateTransition(IcqStatusEnum newStatus) throws Exception {
    logger.trace(" --=== beginning state transition test ===--");

    PresenceStatus oldStatus = operationSetPresence.getPresenceStatus();
    String oldStatusMessage = operationSetPresence.getCurrentStatusMessage();
    String newStatusMessage = statusMessageRoot + newStatus;

    logger.debug(
        "old status is=" + oldStatus.getStatusName() + " new status=" + newStatus.getStatusName());

    // First register a listener to make sure that all corresponding
    // events have been generated.
    PresenceStatusEventCollector statusEventCollector = new PresenceStatusEventCollector();
    operationSetPresence.addProviderPresenceStatusListener(statusEventCollector);

    // change the status
    operationSetPresence.publishPresenceStatus(newStatus, newStatusMessage);

    // test event notification.
    statusEventCollector.waitForPresEvent(10000);
    statusEventCollector.waitForStatMsgEvent(10000);

    // sometimes we don't get response from the server for the
    // changed status. we will query it once again.
    // and wait for the response
    if (statusEventCollector.collectedPresEvents.size() == 0) {
      logger.trace("Will query again status as we haven't received one");
      operationSetPresence.queryContactStatus(fixture.icqAccountID.getUserID());
      statusEventCollector.waitForPresEvent(10000);
    }

    operationSetPresence.removeProviderPresenceStatusListener(statusEventCollector);

    assertEquals(
        "Events dispatched during an event transition.",
        1,
        statusEventCollector.collectedPresEvents.size());
    assertEquals(
        "A status changed event contained wrong old status.",
        oldStatus,
        ((ProviderPresenceStatusChangeEvent) statusEventCollector.collectedPresEvents.get(0))
            .getOldStatus());
    assertEquals(
        "A status changed event contained wrong new status.",
        newStatus,
        ((ProviderPresenceStatusChangeEvent) statusEventCollector.collectedPresEvents.get(0))
            .getNewStatus());

    // verify that the operation set itself is aware of the status change
    assertEquals(
        "opSet.getPresenceStatus() did not return properly.",
        newStatus,
        operationSetPresence.getPresenceStatus());

    IcqStatusEnum actualStatus =
        fixture.testerAgent.getBuddyStatus(fixture.icqAccountID.getUserID());
    assertEquals(
        "The underlying implementation did not switch to the " + "requested presence status.",
        newStatus,
        actualStatus);

    // check whether the server returned the status message that we've set.
    assertEquals(
        "No status message events.", 1, statusEventCollector.collectedStatMsgEvents.size());
    assertEquals(
        "A status message event contained wrong old value.",
        oldStatusMessage,
        ((PropertyChangeEvent) statusEventCollector.collectedStatMsgEvents.get(0)).getOldValue());
    assertEquals(
        "A status message event contained wrong new value.",
        newStatusMessage,
        ((PropertyChangeEvent) statusEventCollector.collectedStatMsgEvents.get(0)).getNewValue());

    // verify that the operation set itself is aware of the new status msg.
    assertEquals(
        "opSet.getCurrentStatusMessage() did not return properly.",
        newStatusMessage,
        operationSetPresence.getCurrentStatusMessage());

    logger.trace(" --=== finished test ===--");
    // make it sleep a bit cause the aol server gets mad otherwise.
    pauseBetweenStateChanges();
  }

  /**
   * The AIM server doesn't like it if we change states too often and we use this method to slow
   * things down.
   */
  private void pauseBetweenStateChanges() {
    try {
      Thread.sleep(5000);
    } catch (InterruptedException ex) {
      logger.debug("Pausing between state changes was interrupted", ex);
    }
  }
  /**
   * Verifies that querying status works fine. The ICQ tester agent would change status and the
   * operation set would have to return the right status after every change.
   *
   * @throws java.lang.Exception if one of the transitions fails
   */
  public void testQueryContactStatus() throws Exception {
    // --- AWAY ---
    logger.debug("Will Query an AWAY contact.");
    subtestQueryContactStatus(FullUserInfo.ICQSTATUS_AWAY, IcqStatusEnum.AWAY);

    pauseBetweenStateChanges();

    // --- NA ---
    logger.debug("Will Query an NA contact.");
    subtestQueryContactStatus(FullUserInfo.ICQSTATUS_NA, IcqStatusEnum.NOT_AVAILABLE);

    pauseBetweenStateChanges();

    // --- DND ---
    logger.debug("Will Query a DND contact.");
    subtestQueryContactStatus(FullUserInfo.ICQSTATUS_DND, IcqStatusEnum.DO_NOT_DISTURB);

    pauseBetweenStateChanges();

    // --- FFC ---
    logger.debug("Will Query a Free For Chat contact.");
    subtestQueryContactStatus(FullUserInfo.ICQSTATUS_FFC, IcqStatusEnum.FREE_FOR_CHAT);

    pauseBetweenStateChanges();

    // --- INVISIBLE ---
    logger.debug("Will Query an Invisible contact.");
    subtestQueryContactStatus(FullUserInfo.ICQSTATUS_INVISIBLE, IcqStatusEnum.INVISIBLE);

    pauseBetweenStateChanges();

    // --- Occupied ---
    logger.debug("Will Query an Occupied contact.");
    subtestQueryContactStatus(FullUserInfo.ICQSTATUS_OCCUPIED, IcqStatusEnum.OCCUPIED);

    pauseBetweenStateChanges();

    // --- Online ---
    logger.debug("Will Query an Online contact.");
    subtestQueryContactStatus(IcqTesterAgent.ICQ_ONLINE_MASK, IcqStatusEnum.ONLINE);

    pauseBetweenStateChanges();
  }

  /**
   * Used by functions testing the queryContactStatus method of the presence operation set.
   *
   * @param taStatusLong the icq status as specified by FullUserInfo, that the tester agent should
   *     switch to.
   * @param expectedReturn the PresenceStatus that the presence operation set should see the tester
   *     agent in once it has switched to taStatusLong.
   * @throws java.lang.Exception if querying the status causes some exception.
   */
  public void subtestQueryContactStatus(long taStatusLong, PresenceStatus expectedReturn)
      throws Exception {
    if (!fixture.testerAgent.enterStatus(taStatusLong)) {
      throw new RuntimeException(
          "Tester UserAgent Failed to switch to the " + expectedReturn.getStatusName() + " state.");
    }

    PresenceStatus actualReturn =
        operationSetPresence.queryContactStatus(fixture.testerAgent.getIcqUIN());
    assertEquals(
        "Querying a " + expectedReturn.getStatusName() + " state did not return as expected",
        expectedReturn,
        actualReturn);
  }

  /**
   * The method would add a subscription for a contact, wait for a subscription event confirming the
   * subscription, then change the status of the newly added contact (which is actually the
   * IcqTesterAgent) and make sure that the corresponding notification events have been generated.
   *
   * @throws java.lang.Exception if an exception occurs during testing.
   */
  public void postTestSubscribe() throws Exception {
    logger.debug("Testing Subscription and Subscription Event Dispatch.");

    // First create a subscription and verify that it really gets created.
    SubscriptionEventCollector subEvtCollector = new SubscriptionEventCollector();

    logger.trace("set Auth Handler");
    operationSetPresence.setAuthorizationHandler(authEventCollector);

    synchronized (authEventCollector) {
      authEventCollector.authorizationRequestReason = "Please deny my request!";
      fixture.testerAgent.getAuthCmdFactory().responseReasonStr =
          "First authorization I will Deny!!!";
      fixture.testerAgent.getAuthCmdFactory().ACCEPT = false;
      operationSetPresence.subscribe(fixture.testerAgent.getIcqUIN());

      // this one collects event that the buddy has been added
      // to the list as awaiting
      SubscriptionEventCollector moveEvtCollector = new SubscriptionEventCollector();
      operationSetPresence.addSubscriptionListener(moveEvtCollector);

      logger.debug("Waiting for authorization error and authorization response...");
      authEventCollector.waitForAuthResponse(15000);
      assertTrue(
          "Error adding buddy not recieved or the buddy("
              + fixture.testerAgent.getIcqUIN()
              + ") doesn't require authorization",
          authEventCollector.isAuthorizationRequestSent);

      assertNotNull(
          "Agent haven't received any reason for authorization",
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);
      assertEquals(
          "Error sent request reason is not as the received one",
          authEventCollector.authorizationRequestReason,
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);

      logger.debug(
          "authEventCollector.isAuthorizationResponseReceived "
              + authEventCollector.isAuthorizationResponseReceived);

      assertTrue("Response not received!", authEventCollector.isAuthorizationResponseReceived);

      boolean isAcceptedAuthReuest =
          authEventCollector.response.getResponseCode().equals(AuthorizationResponse.ACCEPT);
      assertEquals(
          "Response is not as the sent one",
          fixture.testerAgent.getAuthCmdFactory().ACCEPT,
          isAcceptedAuthReuest);
      assertNotNull(
          "We didn't receive any reason! ", authEventCollector.authorizationResponseString);

      assertEquals(
          "The sent response reason is not as the received one",
          fixture.testerAgent.getAuthCmdFactory().responseReasonStr,
          authEventCollector.authorizationResponseString);

      // here we must wait for server to move the awaiting buddy
      // to the first specified  group
      synchronized (moveEvtCollector) {
        moveEvtCollector.waitForEvent(20000);
        // don't want any more events
        operationSetPresence.removeSubscriptionListener(moveEvtCollector);
      }

      Contact c = operationSetPresence.findContactByID(fixture.testerAgent.getIcqUIN());
      logger.debug("I will remove " + c + " from group : " + c.getParentContactGroup());

      UnsubscribeWait unsubscribeEvtCollector = new UnsubscribeWait();
      operationSetPresence.addSubscriptionListener(unsubscribeEvtCollector);

      synchronized (unsubscribeEvtCollector) {
        operationSetPresence.unsubscribe(c);
        logger.debug("Waiting to be removed...");
        unsubscribeEvtCollector.waitForUnsubscribre(20000);

        logger.debug("Received unsubscribed ok or we lost patients!");

        // don't want any more events
        operationSetPresence.removeSubscriptionListener(unsubscribeEvtCollector);
      }

      // so we haven't asserted so everithing is fine lets try to be authorized
      authEventCollector.authorizationRequestReason = "Please accept my request!";
      fixture.testerAgent.getAuthCmdFactory().responseReasonStr =
          "Second authorization I will Accept!!!";
      fixture.testerAgent.getAuthCmdFactory().ACCEPT = true;

      // clear some things
      authEventCollector.isAuthorizationRequestSent = false;
      authEventCollector.isAuthorizationResponseReceived = false;
      authEventCollector.authorizationResponseString = null;

      logger.debug(
          "I will add buddy does it exists ?  "
              + (operationSetPresence.findContactByID(fixture.testerAgent.getIcqUIN()) != null));
      // add the listener beacuse now our authorization will be accepted
      // and so the buddy will be finally added to the list
      operationSetPresence.addSubscriptionListener(subEvtCollector);
      // subscribe again so we can trigger again the authorization procedure
      operationSetPresence.subscribe(fixture.testerAgent.getIcqUIN());

      logger.debug(
          "Waiting ... Subscribe must fail and the authorization process "
              + "to be trigered again so waiting for auth response ...");
      authEventCollector.waitForAuthResponse(15000);

      assertTrue(
          "Error adding buddy not recieved or the buddy("
              + fixture.testerAgent.getIcqUIN()
              + ") doesn't require authorization",
          authEventCollector.isAuthorizationRequestSent);

      assertNotNull(
          "Agent haven't received any reason for authorization",
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);

      // not working for now
      assertEquals(
          "Error sent request reason",
          authEventCollector.authorizationRequestReason,
          fixture.testerAgent.getAuthCmdFactory().requestReasonStr);

      // wait for authorization process to be finnished
      // the modification of buddy (server will inform us
      // that he removed - awaiting authorization flag)
      Object obj = new Object();
      synchronized (obj) {
        logger.debug("wait for authorization process to be finnished");
        obj.wait(10000);
        logger.debug("Stop waiting!");
      }

      subEvtCollector.waitForEvent(10000);
      // don't want any more events
      operationSetPresence.removeSubscriptionListener(subEvtCollector);
    }

    // after adding awaitingAuthorization group here are catched 3 events
    // 1 - creating unresolved contact
    // 2 - move of the contact to awaitingAuthorization group
    // 3 - move of the contact from awaitingAuthorization group to original group
    assertTrue(
        "Subscription event dispatching failed.", subEvtCollector.collectedEvents.size() > 0);

    EventObject evt = null;

    Iterator<EventObject> events = subEvtCollector.collectedEvents.iterator();
    while (events.hasNext()) {
      EventObject elem = events.next();
      if (elem instanceof SubscriptionEvent) {
        if (((SubscriptionEvent) elem).getEventID() == SubscriptionEvent.SUBSCRIPTION_CREATED)
          evt = (SubscriptionEvent) elem;
      }
    }

    Object source = null;
    Contact srcContact = null;
    ProtocolProviderService srcProvider = null;

    // the event can be SubscriptionEvent and the new added one
    // SubscriptionMovedEvent

    if (evt instanceof SubscriptionEvent) {
      SubscriptionEvent subEvt = (SubscriptionEvent) evt;

      source = subEvt.getSource();
      srcContact = subEvt.getSourceContact();
      srcProvider = subEvt.getSourceProvider();
    }

    assertEquals(
        "SubscriptionEvent Source:",
        fixture.testerAgent.getIcqUIN(),
        ((Contact) source).getAddress());
    assertEquals(
        "SubscriptionEvent Source Contact:",
        fixture.testerAgent.getIcqUIN(),
        srcContact.getAddress());
    assertSame("SubscriptionEvent Source Provider:", fixture.provider, srcProvider);

    subEvtCollector.collectedEvents.clear();

    // make the user agent tester change its states and make sure we are
    // notified
    logger.debug("Testing presence notifications.");
    IcqStatusEnum testerAgentOldStatus = fixture.testerAgent.getPresneceStatus();
    IcqStatusEnum testerAgentNewStatus = IcqStatusEnum.FREE_FOR_CHAT;
    long testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_FFC;

    // in case we are by any chance already in a FREE_FOR_CHAT status, we'll
    // be changing to something else
    if (testerAgentOldStatus.equals(testerAgentNewStatus)) {
      testerAgentNewStatus = IcqStatusEnum.DO_NOT_DISTURB;
      testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_DND;
    }

    // now do the actual status notification testing
    ContactPresenceEventCollector contactPresEvtCollector =
        new ContactPresenceEventCollector(fixture.testerAgent.getIcqUIN(), testerAgentNewStatus);
    operationSetPresence.addContactPresenceStatusListener(contactPresEvtCollector);

    synchronized (contactPresEvtCollector) {
      if (!fixture.testerAgent.enterStatus(testerAgentNewStatusLong)) {
        throw new RuntimeException(
            "Tester UserAgent Failed to switch to the "
                + testerAgentNewStatus.getStatusName()
                + " state.");
      }
      // we may already have the event, but it won't hurt to check.
      contactPresEvtCollector.waitForEvent(12000);
      operationSetPresence.removeContactPresenceStatusListener(contactPresEvtCollector);
    }

    if (contactPresEvtCollector.collectedEvents.size() == 0) {
      logger.info(
          "PROBLEM. Authorisation process doesn't have finnished "
              + "Server doesn't report us for changing authorization flag! Will try to authorize once again");

      fixture.testerAgent.sendAuthorizationReplay(
          fixture.icqAccountID.getUserID(),
          fixture.testerAgent.getAuthCmdFactory().responseReasonStr,
          fixture.testerAgent.getAuthCmdFactory().ACCEPT);

      Object obj = new Object();
      synchronized (obj) {
        logger.debug("wait for authorization process to be finnished for second time");
        obj.wait(10000);
        logger.debug("Stop waiting!");
      }

      testerAgentOldStatus = fixture.testerAgent.getPresneceStatus();
      testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_FFC;

      // in case we are by any chance already in a FREE_FOR_CHAT status, we'll
      // be changing to something else
      if (testerAgentOldStatus.equals(testerAgentNewStatus)) {
        testerAgentNewStatus = IcqStatusEnum.OCCUPIED;
        testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_OCCUPIED;
      }

      contactPresEvtCollector =
          new ContactPresenceEventCollector(fixture.testerAgent.getIcqUIN(), testerAgentNewStatus);
      operationSetPresence.addContactPresenceStatusListener(contactPresEvtCollector);

      synchronized (contactPresEvtCollector) {
        if (!fixture.testerAgent.enterStatus(testerAgentNewStatusLong)) {
          throw new RuntimeException(
              "Tester UserAgent Failed to switch to the "
                  + testerAgentNewStatus.getStatusName()
                  + " state.");
        }
        // we may already have the event, but it won't hurt to check.
        contactPresEvtCollector.waitForEvent(12000);
        operationSetPresence.removeContactPresenceStatusListener(contactPresEvtCollector);
      }
    }

    assertEquals(
        "Presence Notif. event dispatching failed.",
        1,
        contactPresEvtCollector.collectedEvents.size());
    ContactPresenceStatusChangeEvent presEvt =
        (ContactPresenceStatusChangeEvent) contactPresEvtCollector.collectedEvents.get(0);

    assertEquals(
        "Presence Notif. event  Source:",
        fixture.testerAgent.getIcqUIN(),
        ((Contact) presEvt.getSource()).getAddress());
    assertEquals(
        "Presence Notif. event  Source Contact:",
        fixture.testerAgent.getIcqUIN(),
        presEvt.getSourceContact().getAddress());
    assertSame(
        "Presence Notif. event  Source Provider:", fixture.provider, presEvt.getSourceProvider());

    PresenceStatus reportedNewStatus = presEvt.getNewStatus();
    PresenceStatus reportedOldStatus = presEvt.getOldStatus();

    assertEquals("Reported new PresenceStatus: ", testerAgentNewStatus, reportedNewStatus);

    // don't require equality between the reported old PresenceStatus and
    // the actual presence status of the tester agent because a first
    // notification is not supposed to have the old status as it really was.
    assertNotNull("Reported old PresenceStatus: ", reportedOldStatus);

    /** @todo tester agent changes status message we see the new message */
    /** @todo we should see the alias of the tester agent. */
    Object obj = new Object();
    synchronized (obj) {
      logger.debug("wait a moment. give time to server");
      obj.wait(4000);
    }
  }

  /**
   * We unsubscribe from presence notification deliveries concerning IcqTesterAgent's presence
   * status and verify that we receive the subscription removed event. We then make the tester agent
   * change status and make sure that no notifications are delivered.
   *
   * @throws java.lang.Exception in case unsubscribing fails.
   */
  public void postTestUnsubscribe() throws Exception {
    logger.debug("Testing Unsubscribe and unsubscription event dispatch.");

    // First create a subscription and verify that it really gets created.
    SubscriptionEventCollector subEvtCollector = new SubscriptionEventCollector();
    operationSetPresence.addSubscriptionListener(subEvtCollector);

    Contact icqTesterAgentContact =
        operationSetPresence.findContactByID(fixture.testerAgent.getIcqUIN());

    assertNotNull(
        "Failed to find an existing subscription for the tester agent", icqTesterAgentContact);

    synchronized (subEvtCollector) {
      operationSetPresence.unsubscribe(icqTesterAgentContact);
      subEvtCollector.waitForEvent(40000);
      // don't want any more events
      operationSetPresence.removeSubscriptionListener(subEvtCollector);
    }

    assertEquals(
        "Subscription event dispatching failed.", 1, subEvtCollector.collectedEvents.size());
    SubscriptionEvent subEvt = (SubscriptionEvent) subEvtCollector.collectedEvents.get(0);

    assertEquals("SubscriptionEvent Source:", icqTesterAgentContact, subEvt.getSource());

    assertEquals(
        "SubscriptionEvent Source Contact:", icqTesterAgentContact, subEvt.getSourceContact());

    assertSame("SubscriptionEvent Source Provider:", fixture.provider, subEvt.getSourceProvider());

    subEvtCollector.collectedEvents.clear();

    // make the user agent tester change its states and make sure we don't
    // get notifications as we're now unsubscribed.
    logger.debug("Testing (lack of) presence notifications.");
    IcqStatusEnum testerAgentOldStatus = fixture.testerAgent.getPresneceStatus();
    IcqStatusEnum testerAgentNewStatus = IcqStatusEnum.FREE_FOR_CHAT;
    long testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_FFC;

    // in case we are by any chance already in a FREE_FOR_CHAT status, we'll
    // be changing to something else
    if (testerAgentOldStatus.equals(testerAgentNewStatus)) {
      testerAgentNewStatus = IcqStatusEnum.DO_NOT_DISTURB;
      testerAgentNewStatusLong = FullUserInfo.ICQSTATUS_DND;
    }

    // now do the actual status notification testing
    ContactPresenceEventCollector contactPresEvtCollector =
        new ContactPresenceEventCollector(fixture.testerAgent.getIcqUIN(), null);
    operationSetPresence.addContactPresenceStatusListener(contactPresEvtCollector);

    synchronized (contactPresEvtCollector) {
      if (!fixture.testerAgent.enterStatus(testerAgentNewStatusLong)) {
        throw new RuntimeException(
            "Tester UserAgent Failed to switch to the "
                + testerAgentNewStatus.getStatusName()
                + " state.");
      }
      // we may already have the event, but it won't hurt to check.
      contactPresEvtCollector.waitForEvent(10000);
      operationSetPresence.removeContactPresenceStatusListener(contactPresEvtCollector);
    }

    assertEquals(
        "Presence Notifications were received after unsubscibing.",
        0,
        contactPresEvtCollector.collectedEvents.size());
  }

  /**
   * An event collector that would collect all events generated by a provider after a status change.
   * The collector would also do a notidyAll every time it receives an event.
   */
  private class PresenceStatusEventCollector implements ProviderPresenceStatusListener {
    public ArrayList<EventObject> collectedPresEvents = new ArrayList<EventObject>();
    public ArrayList<EventObject> collectedStatMsgEvents = new ArrayList<EventObject>();

    public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedPresEvents.size() + ")= " + evt);
        collectedPresEvents.add(evt);
        notifyAll();
      }
    }

    public void providerStatusMessageChanged(PropertyChangeEvent evt) {
      synchronized (this) {
        logger.debug("Collected stat.msg. evt(" + collectedPresEvents.size() + ")= " + evt);
        collectedStatMsgEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Blocks until at least one event is received or until waitFor milliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of milliseconds that we should be waiting for an event before
     *     simply bailing out.
     */
    public void waitForPresEvent(long waitFor) {
      logger.trace("Waiting for a change in provider status.");
      synchronized (this) {
        if (collectedPresEvents.size() > 0) {
          logger.trace("Change already received. " + collectedPresEvents);
          return;
        }

        try {
          wait(waitFor);
          if (collectedPresEvents.size() > 0) logger.trace("Received a change in provider status.");
          else logger.trace("No change received for " + waitFor + "ms.");
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a provider evt", ex);
        }
      }
    }

    /**
     * Blocks until at least one status message event is received or until waitFor milliseconds pass
     * (whichever happens first).
     *
     * @param waitFor the number of milliseconds that we should be waiting for a status message
     *     event before simply bailing out.
     */
    public void waitForStatMsgEvent(long waitFor) {
      logger.trace("Waiting for a provider status message event.");
      synchronized (this) {
        if (collectedStatMsgEvents.size() > 0) {
          logger.trace("Stat msg. evt already received. " + collectedStatMsgEvents);
          return;
        }

        try {
          wait(waitFor);
          if (collectedStatMsgEvents.size() > 0) logger.trace("Received a prov. stat. msg. evt.");
          else logger.trace("No prov. stat msg. received for " + waitFor + "ms.");
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a status msg evt", ex);
        }
      }
    }
  }

  /** The class would listen for and store received subscription modification events. */
  private class SubscriptionEventCollector implements SubscriptionListener {
    public ArrayList<EventObject> collectedEvents = new ArrayList<EventObject>();

    /**
     * Blocks until at least one event is received or until waitFor milliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of milliseconds that we should be waiting for an event before
     *     simply bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedEvents.size() > 0) return;

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    /**
     * Stores the received subscription and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionCreated(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subscription and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionRemoved(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subscription and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void contactModified(ContactPropertyChangeEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subscription and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionMoved(SubscriptionMovedEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subscription and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionFailed(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Stores the received subscription and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void subscriptionResolved(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }
  }

  /**
   * The class would listen for and store received events caused by changes in contact presence
   * states.
   */
  private class ContactPresenceEventCollector implements ContactPresenceStatusListener {
    public ArrayList<EventObject> collectedEvents = new ArrayList<EventObject>();
    private String trackedScreenName = null;
    private IcqStatusEnum status = null;

    ContactPresenceEventCollector(String screenname, IcqStatusEnum wantedStatus) {
      this.trackedScreenName = screenname;
      this.status = wantedStatus;
    }

    /**
     * Blocks until at least one event is received or until waitFor milliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of milliseconds that we should be waiting for an event before
     *     simply bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedEvents.size() > 0) return;

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    /**
     * Stores the received status change event and notifies all waiting on this object
     *
     * @param evt the SubscriptionEvent containing the corresponding contact
     */
    public void contactPresenceStatusChanged(ContactPresenceStatusChangeEvent evt) {
      synchronized (this) {
        // if the user has specified event details and the received
        // event does not match - then ignore it.
        if (this.trackedScreenName != null
            && !evt.getSourceContact().getAddress().equals(trackedScreenName)) return;
        if (status != null && status != evt.getNewStatus()) return;

        logger.debug("Collected evt(" + collectedEvents.size() + ")= " + evt);
        collectedEvents.add(evt);
        notifyAll();
      }
    }
  }

  /**
   * Authorization handler for the implementation tests
   *
   * <p>1. when authorization request is received we answer with the already set Authorization
   * response, but before that wait some time as a normal user
   *
   * <p>2. When authorization request is required for adding buddy the request is made with already
   * set authorization reason
   *
   * <p>3. When authorization replay is received - we store that it is received and the reason that
   * was received
   */
  private static class AuthEventCollector implements AuthorizationHandler {
    boolean isAuthorizationRequestSent = false;
    String authorizationRequestReason = null;

    boolean isAuthorizationResponseReceived = false;
    AuthorizationResponse response = null;
    String authorizationResponseString = null;

    // receiving auth request
    AuthorizationResponse responseToRequest = null;
    boolean isAuthorizationRequestReceived = false;

    public AuthorizationResponse processAuthorisationRequest(
        AuthorizationRequest req, Contact sourceContact) {
      logger.debug("Processing in " + this);
      synchronized (this) {
        logger.trace("processAuthorisationRequest " + req + " " + sourceContact);

        isAuthorizationRequestReceived = true;
        authorizationRequestReason = req.getReason();

        notifyAll();

        // will wait as a normal user
        Object lock = new Object();
        synchronized (lock) {
          try {
            lock.wait(2000);
          } catch (Exception ex) {
          }
        }

        return responseToRequest;
      }
    }

    public AuthorizationRequest createAuthorizationRequest(Contact contact) {
      logger.trace("createAuthorizationRequest " + contact);

      AuthorizationRequest authReq = new AuthorizationRequest();
      authReq.setReason(authorizationRequestReason);

      isAuthorizationRequestSent = true;

      return authReq;
    }

    public void processAuthorizationResponse(
        AuthorizationResponse response, Contact sourceContact) {
      synchronized (this) {
        isAuthorizationResponseReceived = true;
        this.response = response;
        authorizationResponseString = response.getReason();

        logger.trace(
            "processAuthorizationResponse '"
                + authorizationResponseString
                + "' "
                + response.getResponseCode()
                + " "
                + sourceContact);

        notifyAll();
      }
    }

    public void waitForAuthResponse(long waitFor) {
      synchronized (this) {
        if (isAuthorizationResponseReceived) return;
        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    public void waitForAuthRequest(long waitFor) {
      synchronized (this) {
        if (isAuthorizationRequestReceived) return;
        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }
  }

  /**
   * Used to wait till buddy is removed from our contact list. Used in the authorization process
   * tests
   */
  private static class UnsubscribeWait extends SubscriptionAdapter {
    public void waitForUnsubscribre(long waitFor) {
      synchronized (this) {
        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    public void subscriptionRemoved(SubscriptionEvent evt) {
      synchronized (this) {
        logger.debug("Got subscriptionRemoved " + evt);
        notifyAll();
      }
    }
  }

  /** Tests for receiving authorization requests */
  public void postTestReceiveAuthorizatinonRequest() {
    logger.debug("Testing receive of authorization request!");

    // set first response isAccepted and responseString
    // the first authorization process is negative
    // the agent try to add us to his contact list and ask us for
    // authorization but we deny him
    String firstRequestResponse = "First Request will be denied!!!";
    authEventCollector.responseToRequest =
        new AuthorizationResponse(AuthorizationResponse.REJECT, firstRequestResponse);
    logger.debug("authEventCollector " + authEventCollector);
    authEventCollector.isAuthorizationRequestReceived = false;
    authEventCollector.authorizationRequestReason = null;
    fixture.testerAgent.getAuthCmdFactory().requestReasonStr = "Deny my first request!";
    fixture.testerAgent.getAuthCmdFactory().isErrorAddingReceived = false;
    fixture.testerAgent.getAuthCmdFactory().responseReasonStr = null;
    fixture.testerAgent.getAuthCmdFactory().isRequestAccepted = false;

    // be sure buddy is not already in the list
    fixture.testerAgent.deleteBuddy(fixture.ourUserID);
    fixture.testerAgent.addBuddy(fixture.ourUserID);

    // wait agent to receive error and to request us for our authorization
    authEventCollector.waitForAuthRequest(25000);

    // check have we received authorization request?
    assertTrue(
        "Error adding buddy not recieved or the buddy("
            + fixture.ourUserID
            + ") doesn't require authorization 1",
        fixture.testerAgent.getAuthCmdFactory().isErrorAddingReceived);

    assertTrue(
        "We haven't received any authorization request ",
        authEventCollector.isAuthorizationRequestReceived);

    assertNotNull(
        "We haven't received any reason for authorization",
        authEventCollector.authorizationRequestReason);

    assertEquals(
        "Error sent request reason is not as the received one",
        fixture.testerAgent.getAuthCmdFactory().requestReasonStr,
        authEventCollector.authorizationRequestReason);

    // wait agent to receive our response
    Object lock = new Object();
    synchronized (lock) {
      try {
        lock.wait(5000);
      } catch (Exception ex) {
      }
    }

    // check is correct - the received response from the agent
    assertNotNull(
        "Agent haven't received any reason from authorization reply",
        authEventCollector.authorizationRequestReason);

    assertEquals(
        "Received auth response from agent is not as the sent one",
        fixture.testerAgent.getAuthCmdFactory().responseReasonStr,
        firstRequestResponse);

    boolean isAcceptedAuthReuest =
        authEventCollector.responseToRequest.getResponseCode().equals(AuthorizationResponse.ACCEPT);
    assertEquals(
        "Agent received Response is not as the sent one",
        fixture.testerAgent.getAuthCmdFactory().isRequestAccepted,
        isAcceptedAuthReuest);

    // delete us from his list
    // be sure buddy is not already in the list
    fixture.testerAgent.deleteBuddy(fixture.ourUserID);

    // set second response isAccepted and responseString
    // the second test is the same as first, but this time we accept
    // the request and check that everything is OK.
    String secondRequestResponse = "Second Request will be accepted!!!";
    authEventCollector.responseToRequest =
        new AuthorizationResponse(AuthorizationResponse.ACCEPT, secondRequestResponse);
    authEventCollector.isAuthorizationRequestReceived = false;
    authEventCollector.authorizationRequestReason = null;
    fixture.testerAgent.getAuthCmdFactory().requestReasonStr = "Accept my second request!";
    fixture.testerAgent.getAuthCmdFactory().isErrorAddingReceived = false;
    fixture.testerAgent.getAuthCmdFactory().responseReasonStr = null;
    fixture.testerAgent.getAuthCmdFactory().isRequestAccepted = false;

    // add us to his list again
    fixture.testerAgent.addBuddy(fixture.ourUserID);

    // wait agent to receive error and to request us for our authorization
    authEventCollector.waitForAuthRequest(25000);

    // check have we received authorization request?
    assertTrue(
        "Error adding buddy not recieved or the buddy("
            + fixture.ourUserID
            + ") doesn't require authorization 2",
        fixture.testerAgent.getAuthCmdFactory().isErrorAddingReceived);

    assertTrue(
        "We haven't received any authorization request ",
        authEventCollector.isAuthorizationRequestReceived);

    assertNotNull(
        "We haven't received any reason for authorization",
        authEventCollector.authorizationRequestReason);

    assertEquals(
        "Error sent request reason is not as the received one",
        fixture.testerAgent.getAuthCmdFactory().requestReasonStr,
        authEventCollector.authorizationRequestReason);
    // wait agent to receive our response
    synchronized (lock) {
      try {
        lock.wait(5000);
      } catch (Exception ex) {
      }
    }
    // check is correct the received response from the agent
    assertNotNull(
        "Agent haven't received any reason from authorization reply",
        authEventCollector.authorizationRequestReason);

    assertEquals(
        "Received auth response from agent is not as the sent one",
        fixture.testerAgent.getAuthCmdFactory().responseReasonStr,
        secondRequestResponse);

    isAcceptedAuthReuest =
        authEventCollector.responseToRequest.getResponseCode().equals(AuthorizationResponse.ACCEPT);
    assertEquals(
        "Agent received Response is not as the sent one",
        fixture.testerAgent.getAuthCmdFactory().isRequestAccepted,
        isAcceptedAuthReuest);
  }
}
/**
 * Tests whether accaounts are uninstalled properly. It is important that tests from this class be
 * called last since they will install the accounts that have been used to test the implementations.
 * Apart from uninstallation tests the class also contains tests that remove and reinstall the
 * protocol provider bundle in order to verify that accounts are persistent.
 *
 * @author Emil Ivov
 */
public class TestAccountUninstallation extends TestCase {
  private static final Logger logger = Logger.getLogger(TestAccountUninstallation.class);

  private SipSlickFixture fixture = new SipSlickFixture();

  /**
   * Constructs a test instance
   *
   * @param name The name of the test.
   */
  public TestAccountUninstallation(String name) {
    super(name);
  }

  /**
   * JUnit setup method.
   *
   * @throws Exception in case anything goes wrong.
   */
  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();
  }

  /**
   * JUnit teardown method.
   *
   * @throws Exception in case anything goes wrong.
   */
  protected void tearDown() throws Exception {
    fixture.tearDown();
    super.tearDown();
  }

  /**
   * Returns a suite containing tests in this class in the order that we'd like them executed.
   *
   * @return a Test suite containing tests in this class in the order that we'd like them executed.
   */
  public static Test suite() {
    TestSuite suite = new TestSuite();

    suite.addTest(new TestAccountUninstallation("testProviderUnregister"));
    suite.addTest(new TestAccountUninstallation("testInstallationPersistency"));
    suite.addTest(new TestAccountUninstallation("testUninstallAccount"));

    return suite;
  }

  /**
   * Unregisters both providers and verifies whether they have changed state accordingly.
   *
   * @throws OperationFailedException if unregister fails with an error.
   */
  public void testProviderUnregister() throws OperationFailedException {
    // make sure providers are still registered
    assertEquals(fixture.provider1.getRegistrationState(), RegistrationState.REGISTERED);
    assertEquals(fixture.provider2.getRegistrationState(), RegistrationState.REGISTERED);

    UnregistrationEventCollector collector1 = new UnregistrationEventCollector();
    UnregistrationEventCollector collector2 = new UnregistrationEventCollector();

    fixture.provider1.addRegistrationStateChangeListener(collector1);
    fixture.provider2.addRegistrationStateChangeListener(collector2);

    // unregister both providers
    fixture.provider1.unregister();
    fixture.provider2.unregister();

    collector1.waitForEvent(10000);
    collector2.waitForEvent(10000);

    assertTrue(
        "Provider did not distribute unregister events", 2 <= collector1.collectedNewStates.size());
    assertTrue(
        "Provider did not distribute unregister events", 2 <= collector2.collectedNewStates.size());

    // make sure both providers are now unregistered.
    assertEquals(
        "Provider state after calling unregister().",
        RegistrationState.UNREGISTERED,
        fixture.provider1.getRegistrationState());
    assertEquals(
        "Provider state after calling unregister().",
        RegistrationState.UNREGISTERED,
        fixture.provider2.getRegistrationState());
  }

  /**
   * Stops and removes the tested bundle, verifies that it has unregistered its provider, then
   * reloads and restarts the bundle and verifies that the protocol provider is reRegistered in the
   * bundle context.
   *
   * @throws java.lang.Exception if an exception occurs during testing.
   */
  public void testInstallationPersistency() throws Exception {
    Bundle providerBundle = fixture.findProtocolProviderBundle(fixture.provider1);

    // set the global providerBundle reference that we will be using
    // in the last series of tests (Account uninstallation persistency)
    SipSlickFixture.providerBundle = providerBundle;

    assertNotNull("Couldn't find a bundle for the tested provider", providerBundle);

    providerBundle.stop();

    assertTrue(
        "Couldn't stop the protocol provider bundle. State was " + providerBundle.getState(),
        Bundle.ACTIVE != providerBundle.getState() && Bundle.STOPPING != providerBundle.getState());

    providerBundle.uninstall();

    assertEquals(
        "Couldn't stop the protocol provider bundle.",
        Bundle.UNINSTALLED,
        providerBundle.getState());

    // verify that the provider is no longer available
    ServiceReference[] sipProviderRefs = null;
    try {
      sipProviderRefs =
          fixture.bc.getServiceReferences(
              ProtocolProviderService.class.getName(),
              "(&"
                  + "("
                  + ProtocolProviderFactory.PROTOCOL
                  + "="
                  + ProtocolNames.SIP
                  + ")"
                  + "("
                  + ProtocolProviderFactory.USER_ID
                  + "="
                  + fixture.userID1
                  + ")"
                  + ")");
    } catch (InvalidSyntaxException ex) {
      fail("We apparently got our filter wrong: " + ex.getMessage());
    }

    // make sure we didn't see a service
    assertTrue(
        "A Protocol Provider Service was still regged as an osgi service "
            + "for SIP URI:"
            + fixture.userID1
            + "After it was explicitly uninstalled",
        sipProviderRefs == null || sipProviderRefs.length == 0);

    // verify that the provider factory knows that we have uninstalled the
    // provider.
    assertTrue(
        "The SIP provider factory kept a reference to the provider we just "
            + "uninstalled (uri="
            + fixture.userID1
            + ")",
        fixture.providerFactory.getRegisteredAccounts().isEmpty()
            && fixture.providerFactory.getProviderForAccount(fixture.provider1.getAccountID())
                == null);

    // Now reinstall the bundle
    providerBundle = fixture.bc.installBundle(providerBundle.getLocation());

    // set the global providerBundle reference that we will be using
    // in the last series of tests (Account uninstallation persistency)
    SipSlickFixture.providerBundle = providerBundle;

    assertEquals(
        "Couldn't re-install protocol provider bundle.",
        Bundle.INSTALLED,
        providerBundle.getState());

    AccountManagerUtils.startBundleAndWaitStoredAccountsLoaded(
        fixture.bc, providerBundle, ProtocolNames.SIP);
    assertEquals(
        "Couldn't re-start protocol provider bundle.", Bundle.ACTIVE, providerBundle.getState());

    // Make sure that the provider is there again.
    // verify that the provider is no longer available
    try {
      sipProviderRefs =
          fixture.bc.getServiceReferences(
              ProtocolProviderService.class.getName(),
              "(&"
                  + "("
                  + ProtocolProviderFactory.PROTOCOL
                  + "="
                  + ProtocolNames.SIP
                  + ")"
                  + "("
                  + ProtocolProviderFactory.USER_ID
                  + "="
                  + fixture.userID1
                  + ")"
                  + ")");
    } catch (InvalidSyntaxException ex) {
      fail("We apparently got our filter wrong " + ex.getMessage());
    }

    // make sure we didn't see a service
    assertTrue(
        "A Protocol Provider Service was not restored after being"
            + "reinstalled. SIP URI:"
            + fixture.userID1,
        sipProviderRefs != null && sipProviderRefs.length > 0);

    ServiceReference[] sipFactoryRefs = null;
    try {
      sipFactoryRefs =
          fixture.bc.getServiceReferences(
              ProtocolProviderFactory.class.getName(),
              "(" + ProtocolProviderFactory.PROTOCOL + "=" + ProtocolNames.SIP + ")");
    } catch (InvalidSyntaxException ex) {
      fail("We apparently got our filter wrong " + ex.getMessage());
    }

    // we're the ones who've reinstalled the factory so it's our
    // responsibility to update the fixture.
    fixture.providerFactory = (ProtocolProviderFactory) fixture.bc.getService(sipFactoryRefs[0]);
    fixture.provider1 = (ProtocolProviderService) fixture.bc.getService(sipProviderRefs[0]);

    // verify that the provider is also restored in the provider factory
    // itself
    assertTrue(
        "The SIP provider did not restore its own reference to the provider "
            + "that we just reinstalled (URI="
            + fixture.userID1
            + ")",
        !fixture.providerFactory.getRegisteredAccounts().isEmpty()
            && fixture.providerFactory.getProviderForAccount(fixture.provider1.getAccountID())
                != null);
  }

  /** Uinstalls our test account and makes sure it really has been removed. */
  public void testUninstallAccount() {
    assertFalse(
        "No installed accounts found", fixture.providerFactory.getRegisteredAccounts().isEmpty());

    assertNotNull(
        "Found no provider corresponding to URI " + fixture.userID1,
        fixture.providerFactory.getProviderForAccount(fixture.provider1.getAccountID()));

    assertTrue(
        "Failed to remove a provider corresponding to URI " + fixture.userID1,
        fixture.providerFactory.uninstallAccount(fixture.provider1.getAccountID()));
    assertTrue(
        "Failed to remove a provider corresponding to URI " + fixture.userID1,
        fixture.providerFactory.uninstallAccount(fixture.provider2.getAccountID()));

    // make sure no providers have remained installed.
    ServiceReference[] sipProviderRefs = null;
    try {
      sipProviderRefs =
          fixture.bc.getServiceReferences(
              ProtocolProviderService.class.getName(),
              "(" + ProtocolProviderFactory.PROTOCOL + "=" + ProtocolNames.SIP + ")");
    } catch (InvalidSyntaxException ex) {
      fail("We apparently got our filter wrong " + ex.getMessage());
    }

    // make sure we didn't see a service
    assertTrue(
        "A Protocol Provider Service was still regged as an osgi "
            + "service for SIP URI:"
            + fixture.userID1
            + "After it was explicitly uninstalled",
        sipProviderRefs == null || sipProviderRefs.length == 0);

    // verify that the provider factory knows that we have uninstalled the
    // provider.
    assertTrue(
        "The SIP provider factory kept a reference to the provider we just "
            + "uninstalled (uri="
            + fixture.userID1
            + ")",
        fixture.providerFactory.getRegisteredAccounts().isEmpty()
            && fixture.providerFactory.getProviderForAccount(fixture.provider1.getAccountID())
                == null);
  }

  /**
   * A class that would plugin as a registration listener to a protocol provider and simply record
   * all events that it sees and notifyAll() if it sees an event that notifies us of a completed
   * registration.
   */
  public class UnregistrationEventCollector implements RegistrationStateChangeListener {
    public List<RegistrationState> collectedNewStates = new LinkedList<RegistrationState>();

    /**
     * The method would simply register all received events so that they could be available for
     * later inspection by the unit tests. In the case where a registraiton event notifying us of a
     * completed registration is seen, the method would call notifyAll().
     *
     * @param evt ProviderStatusChangeEvent the event describing the status change.
     */
    public void registrationStateChanged(RegistrationStateChangeEvent evt) {
      logger.debug("Received a RegistrationStateChangeEvent: " + evt);

      collectedNewStates.add(evt.getNewState());

      if (evt.getNewState().equals(RegistrationState.UNREGISTERED)) {
        logger.debug("We're registered and will notify those who wait");
        synchronized (this) {
          notifyAll();
        }
      }
    }

    /**
     * Blocks until an event notifying us of the awaited state change is received or until waitFor
     * miliseconds pass (whichever happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      logger.trace("Waiting for a RegistrationStateChangeEvent");

      synchronized (this) {
        if (collectedNewStates.contains(RegistrationState.UNREGISTERED)) {
          logger.trace("Event already received. " + collectedNewStates);
          return;
        }

        try {
          wait(waitFor);

          if (collectedNewStates.size() > 0)
            logger.trace("Received a RegistrationStateChangeEvent.");
          else logger.trace("No RegistrationStateChangeEvent received for " + waitFor + "ms.");

        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a " + "RegistrationStateChangeEvent", ex);
        }
      }
    }
  }
}
/**
 * Performs testing of the basic instant messaging operation set. Tests include going over basic
 * functionality such as sending a message from the tested implementation and asserting reception by
 * the tester agent and vice versa.
 *
 * @author Emil Ivov
 */
public class TestOperationSetBasicInstantMessaging extends TestCase {
  private static final Logger logger =
      Logger.getLogger(TestOperationSetBasicInstantMessaging.class);

  private GibberishSlickFixture fixture = new GibberishSlickFixture();

  private OperationSetBasicInstantMessaging opSetBasicIM1 = null;
  private OperationSetBasicInstantMessaging opSetBasicIM2 = null;

  private OperationSetPresence opSetPresence1 = null;
  private OperationSetPresence opSetPresence2 = null;

  public TestOperationSetBasicInstantMessaging(String name) {
    super(name);
  }

  /**
   * Get a reference to the basic IM operation set.
   *
   * @throws Exception if this is not a good day.
   */
  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();

    Map<String, OperationSet> supportedOperationSets1 =
        fixture.provider1.getSupportedOperationSets();

    if (supportedOperationSets1 == null || supportedOperationSets1.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this implementation. ");

    // get the operation set presence here.
    opSetBasicIM1 =
        (OperationSetBasicInstantMessaging)
            supportedOperationSets1.get(OperationSetBasicInstantMessaging.class.getName());

    if (opSetBasicIM1 == null) {
      throw new NullPointerException("No implementation for basic IM was found");
    }

    // we also need the presence op set in order to retrieve contacts.
    opSetPresence1 =
        (OperationSetPresence) supportedOperationSets1.get(OperationSetPresence.class.getName());

    // if the op set is null show that we're not happy.
    if (opSetPresence1 == null) {
      throw new NullPointerException(
          "An implementation of the service must provide an "
              + "implementation of at least one of the PresenceOperationSets");
    }

    Map<String, OperationSet> supportedOperationSets2 =
        fixture.provider2.getSupportedOperationSets();

    if (supportedOperationSets2 == null || supportedOperationSets2.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this implementation. ");

    // get the operation set presence here.
    opSetBasicIM2 =
        (OperationSetBasicInstantMessaging)
            supportedOperationSets2.get(OperationSetBasicInstantMessaging.class.getName());

    if (opSetBasicIM2 == null) {
      throw new NullPointerException("No implementation for basic IM was found");
    }

    opSetPresence2 =
        (OperationSetPresence) supportedOperationSets2.get(OperationSetPresence.class.getName());

    // if the op set is null show that we're not happy.
    if (opSetPresence2 == null) {
      throw new NullPointerException(
          "An implementation of the service must provide an "
              + "implementation of at least one of the PresenceOperationSets");
    }
  }

  protected void tearDown() throws Exception {
    super.tearDown();

    fixture.tearDown();
  }

  /**
   * Creates a test suite containing tests of this class in a specific order. We'll first execute
   * tests beginning with the "test" prefix and then go to ordered tests.We first execture tests for
   * receiving messagese, so that a volatile contact is created for the sender. we'll then be able
   * to retrieve this volatile contact and send them a message on our turn. We need to do things
   * this way as the contact corresponding to the tester agent has been removed in the previous test
   * and we no longer have it in our contact list.
   *
   * @return Test a testsuite containing all tests to execute.
   */
  public static Test suite() {
    TestSuite suite = new TestSuite();

    suite.addTest(new TestOperationSetBasicInstantMessaging("prepareContactList"));

    suite.addTestSuite(TestOperationSetBasicInstantMessaging.class);

    // the following 2 need to be run in the specified order.
    suite.addTest(new TestOperationSetBasicInstantMessaging("firstTestReceiveMessage"));
    suite.addTest(new TestOperationSetBasicInstantMessaging("thenTestSendMessage"));

    return suite;
  }

  /**
   * Create the list to be sure that contacts exchanging messages exists in each other lists
   *
   * @throws Exception
   */
  public void prepareContactList() throws Exception {
    fixture.clearProvidersLists();

    Object o = new Object();
    synchronized (o) {
      o.wait(2000);
    }

    try {
      opSetPresence1.subscribe(fixture.userID2);
    } catch (OperationFailedException ex) {
      // the contact already exist its OK
    }

    try {
      opSetPresence2.subscribe(fixture.userID1);
    } catch (OperationFailedException ex1) {
      // the contact already exist its OK
    }

    synchronized (o) {
      o.wait(2000);
    }
  }

  /**
   * Send an instant message from the tested operation set and assert reception by the tester agent.
   */
  public void firstTestReceiveMessage() {
    String body = "This is an IM coming from the tester agent" + " on " + new Date().toString();

    ImEventCollector evtCollector = new ImEventCollector();

    // add a msg listener and register to the op set and send an instant
    // msg from the tester agent.
    opSetBasicIM1.addMessageListener(evtCollector);

    Contact testerAgentContact = opSetPresence2.findContactByID(fixture.userID1);

    logger.debug("Will send message " + body + " to: " + testerAgentContact);

    opSetBasicIM2.sendInstantMessage(testerAgentContact, opSetBasicIM2.createMessage(body));

    evtCollector.waitForEvent(10000);

    opSetBasicIM1.removeMessageListener(evtCollector);

    // assert reception of a message event
    assertTrue(
        "No events delivered upon a received message", evtCollector.collectedEvents.size() > 0);

    // assert event instance of Message Received Evt
    assertTrue(
        "Received evt was not an instance of " + MessageReceivedEvent.class.getName(),
        evtCollector.collectedEvents.get(0) instanceof MessageReceivedEvent);

    // assert source contact == testAgent.uin
    MessageReceivedEvent evt = (MessageReceivedEvent) evtCollector.collectedEvents.get(0);
    assertEquals("message sender ", evt.getSourceContact().getAddress(), fixture.userID2);

    // assert messageBody == body
    assertEquals("message body", body, evt.getSourceMessage().getContent());
  }

  /**
   * Send an instant message from the tester agent and assert reception by the tested implementation
   */
  public void thenTestSendMessage() {
    logger.debug(
        "Printing Server Stored list to see if message fails are contacts in each other lists");
    ContactGroup rootGroup1 =
        ((OperationSetPersistentPresence) opSetPresence1).getServerStoredContactListRoot();

    logger.debug("=========== Server Stored Contact List 1 =================");

    logger.debug(
        "rootGroup="
            + rootGroup1.getGroupName()
            + " rootGroup.childContacts="
            + rootGroup1.countContacts()
            + "rootGroup.childGroups="
            + rootGroup1.countSubgroups()
            + "Printing rootGroupContents=\n"
            + rootGroup1.toString());

    ContactGroup rootGroup2 =
        ((OperationSetPersistentPresence) opSetPresence2).getServerStoredContactListRoot();

    logger.debug("=========== Server Stored Contact List 2 =================");

    logger.debug(
        "rootGroup="
            + rootGroup2.getGroupName()
            + " rootGroup.childContacts="
            + rootGroup2.countContacts()
            + "rootGroup.childGroups="
            + rootGroup2.countSubgroups()
            + "Printing rootGroupContents=\n"
            + rootGroup2.toString());

    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();

    // create the message
    net.java.sip.communicator.service.protocol.Message msg = opSetBasicIM1.createMessage(body);

    // register a listener in the op set
    ImEventCollector imEvtCollector1 = new ImEventCollector();
    opSetBasicIM1.addMessageListener(imEvtCollector1);

    // register a listener in the tester agent
    ImEventCollector imEvtCollector2 = new ImEventCollector();
    opSetBasicIM2.addMessageListener(imEvtCollector2);

    Contact testerAgentContact = opSetPresence1.findContactByID(fixture.userID2);

    opSetBasicIM1.sendInstantMessage(testerAgentContact, msg);

    imEvtCollector1.waitForEvent(10000);
    imEvtCollector2.waitForEvent(10000);

    opSetBasicIM1.removeMessageListener(imEvtCollector1);
    opSetBasicIM2.removeMessageListener(imEvtCollector2);

    // verify that the message delivered event was dispatched
    assertTrue(
        "No events delivered when sending a message", imEvtCollector1.collectedEvents.size() > 0);

    assertTrue(
        "Received evt was not an instance of " + MessageDeliveredEvent.class.getName(),
        imEvtCollector1.collectedEvents.get(0) instanceof MessageDeliveredEvent);

    MessageDeliveredEvent evt = (MessageDeliveredEvent) imEvtCollector1.collectedEvents.get(0);
    assertEquals("message destination ", evt.getDestinationContact().getAddress(), fixture.userID2);

    assertSame("source message", msg, evt.getSourceMessage());

    // verify that the message has successfully arived at the destination
    assertTrue(
        "No messages received by the tester agent", imEvtCollector2.collectedEvents.size() > 0);

    assertFalse(
        "Message was unable to deliver !",
        imEvtCollector2.collectedEvents.get(0) instanceof MessageDeliveryFailedEvent);

    String receivedBody =
        ((MessageReceivedEvent) imEvtCollector2.collectedEvents.get(0))
            .getSourceMessage()
            .getContent();

    assertEquals("received message body", msg.getContent(), receivedBody);
  }

  /** Creates an Message through the simple createMessage() method and inspects its parameters. */
  public void testCreateMessage1() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();
    net.java.sip.communicator.service.protocol.Message msg = opSetBasicIM1.createMessage(body);

    assertEquals("message body", body, msg.getContent());
    assertTrue("message body bytes", Arrays.equals(body.getBytes(), msg.getRawData()));
    assertEquals("message length", body.length(), msg.getSize());
    assertEquals(
        "message content type",
        OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
        msg.getContentType());

    assertEquals(
        "message encoding",
        OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING,
        msg.getEncoding());

    assertNotNull("message uid", msg.getMessageUID());

    // a further test on message uid.
    net.java.sip.communicator.service.protocol.Message msg2 = opSetBasicIM1.createMessage(body);
    assertFalse("message uid", msg.getMessageUID().equals(msg2.getMessageUID()));
  }

  /** Creates an Message through the advance createMessage() method and inspects its parameters. */
  public void testCreateMessage2() throws UnsupportedEncodingException {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();
    String contentType = "text/html";
    String encoding = "UTF-16";
    String subject = "test message";
    net.java.sip.communicator.service.protocol.Message msg =
        opSetBasicIM1.createMessage(body, contentType, encoding, subject);
    byte[] bodyBytes = body.getBytes(encoding);

    assertEquals("message body", body, msg.getContent());
    assertTrue("message body bytes", Arrays.equals(bodyBytes, msg.getRawData()));
    assertEquals("message length", bodyBytes.length, msg.getSize());
    assertEquals("message content type", contentType, msg.getContentType());
    assertEquals("message encoding", encoding, msg.getEncoding());
    assertNotNull("message uid", msg.getMessageUID());

    // a further test on message uid.
    net.java.sip.communicator.service.protocol.Message msg2 = opSetBasicIM1.createMessage(body);
    assertFalse("message uid", msg.getMessageUID().equals(msg2.getMessageUID()));
  }

  /** Collects instant messaging events. */
  private class ImEventCollector implements MessageListener {
    private List<EventObject> collectedEvents = new LinkedList<EventObject>();
    /**
     * Called when a new incoming <tt>Message</tt> has been received.
     *
     * @param evt the <tt>MessageReceivedEvent</tt> containing the newly received message, its
     *     sender and other details.
     */
    public void messageReceived(MessageReceivedEvent evt) {
      logger.debug("Received a MessageReceivedEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called to indicated that delivery of a message sent earlier has failed. Reason code and
     * phrase are contained by the <tt>MessageFailedEvent</tt>
     *
     * @param evt the <tt>MessageFailedEvent</tt> containing the ID of the message whose delivery
     *     has failed.
     */
    public void messageDeliveryFailed(MessageDeliveryFailedEvent evt) {
      logger.debug("Received a MessageDeliveryFailedEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called when the underlying implementation has received an indication that a message, sent
     * earlier has been successfully received by the destination.
     *
     * @param evt the MessageDeliveredEvent containing the id of the message that has caused the
     *     event.
     */
    public void messageDelivered(MessageDeliveredEvent evt) {
      logger.debug("Received a MessageDeliveredEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Blocks until at least one event is received or until waitFor miliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedEvents.size() > 0) return;

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a message evt", ex);
        }
      }
    }
  }
}
/** Functional tests on the simulated content generator. */
public class FuncSimulatedContent extends LockssTestCase {
  static final Logger log = Logger.getLogger("FuncSimulatedContent");

  private PluginManager pluginMgr;
  private Plugin simPlugin;
  private SimulatedArchivalUnit sau1;
  private SimulatedContentGenerator scgen = null;
  private MockLockssDaemon theDaemon;
  String tempDirPath;
  String tempDirPath2;

  private static String DAMAGED_CACHED_URL = "/branch2/branch2/002file.txt";

  public FuncSimulatedContent(String msg) {
    super(msg);
  }

  public void setUp() throws Exception {
    super.setUp();
    tempDirPath = getTempDir().getAbsolutePath() + File.separator;

    theDaemon = getMockLockssDaemon();
    theDaemon.getAlertManager();
    theDaemon.getPluginManager().setLoadablePluginsReady(true);
    theDaemon.getHashService();
    MockSystemMetrics metrics = new MyMockSystemMetrics();
    metrics.initService(theDaemon);
    theDaemon.setSystemMetrics(metrics);

    theDaemon.setDaemonInited(true);

    Properties props = new Properties();
    props.setProperty(SystemMetrics.PARAM_HASH_TEST_DURATION, "1000");
    props.setProperty(SystemMetrics.PARAM_HASH_TEST_BYTE_STEP, "1024");
    props.setProperty(ConfigManager.PARAM_PLATFORM_DISK_SPACE_LIST, tempDirPath);
    ConfigurationUtil.setCurrentConfigFromProps(props);

    pluginMgr = theDaemon.getPluginManager();
    pluginMgr.startService();
    theDaemon.getHashService().startService();
    metrics.startService();
    metrics.setHashSpeed(100);

    simPlugin = PluginTestUtil.findPlugin(SimulatedPlugin.class);
  }

  public void tearDown() throws Exception {
    theDaemon.getLockssRepository(sau1).stopService();
    theDaemon.getNodeManager(sau1).stopService();
    theDaemon.getPluginManager().stopService();
    theDaemon.getHashService().stopService();
    theDaemon.getSystemMetrics().stopService();
    theDaemon.stopDaemon();
    super.tearDown();
  }

  SimulatedArchivalUnit setupSimAu(Configuration auConfig)
      throws ArchivalUnit.ConfigurationException {
    ArchivalUnit au = PluginTestUtil.createAndStartAu(simPlugin, auConfig);
    return (SimulatedArchivalUnit) au;
  }

  Configuration simAuConfig(String rootPath) {
    Configuration conf = ConfigManager.newConfiguration();
    conf.put("root", rootPath);
    conf.put("depth", "2");
    conf.put("branch", "2");
    conf.put("numFiles", "2");
    conf.put("badCachedFileLoc", "2,2");
    conf.put("badCachedFileNum", "2");
    return conf;
  }

  void enableFilter(SimulatedArchivalUnit sau, boolean enable)
      throws ArchivalUnit.ConfigurationException {
    Configuration auConfig = sau.getConfiguration().copy();
    // no bad file when playing with filtering
    auConfig.remove("badCachedFileLoc");
    auConfig.remove("badCachedFileNum");
    if (enable) {
      auConfig.put(SimulatedPlugin.AU_PARAM_HASH_FILTER_SPEC, "true");
    } else {
      auConfig.remove(SimulatedPlugin.AU_PARAM_HASH_FILTER_SPEC);
    }
    sau.setConfiguration(auConfig);
  }

  public void testSimulatedContent() throws Exception {
    sau1 = setupSimAu(simAuConfig(tempDirPath));
    createContent(sau1);
    crawlContent(sau1);
    checkContent(sau1);
    doDamageRemoveTest(sau1); // must be before content read again
    checkFilter(sau1);
    hashContent(sau1);

    // this resets AU's config, do last to avoid messing up toBeDamaged set
  }

  public void testDualContentHash() throws Exception {
    sau1 = setupSimAu(simAuConfig(tempDirPath));
    createContent(sau1);
    crawlContent(sau1);
    CachedUrlSet set = sau1.getAuCachedUrlSet();
    byte[] nameH = getHash(set, true);
    byte[] contentH = getHash(set, false);

    tempDirPath2 = getTempDir().getAbsolutePath() + File.separator;
    SimulatedArchivalUnit sau2 = setupSimAu(simAuConfig(tempDirPath2));

    createContent(sau2);
    crawlContent(sau2);
    set = sau2.getAuCachedUrlSet();
    byte[] nameH2 = getHash(set, true);
    byte[] contentH2 = getHash(set, false);
    assertEquals(nameH, nameH2);
    assertEquals(contentH, contentH2);
  }

  public void testBaseUrl() throws Exception {
    sau1 = setupSimAu(simAuConfig(tempDirPath));
    createContent(sau1);
    crawlContent(sau1);
    CachedUrlSet cus1 = sau1.getAuCachedUrlSet();

    tempDirPath2 = getTempDir().getAbsolutePath() + File.separator;
    Configuration config2 = simAuConfig(tempDirPath2);
    config2.put("base_url", "http://anotherhost.org/");
    SimulatedArchivalUnit sau2 = setupSimAu(config2);
    createContent(sau2);
    crawlContent(sau2);
    CachedUrlSet cus2 = sau1.getAuCachedUrlSet();
    List urls1 = auUrls(sau1);
    List urls2 = auUrls(sau2);

    Pattern pat = Pattern.compile("http://([^/]+)(/.*)$");
    List<String> l1 = auUrls(sau1);
    List<String> l2 = auUrls(sau2);
    assertEquals(l1.size(), l2.size());
    for (int ix = 0; ix < l1.size(); ix++) {
      Matcher m1 = pat.matcher(l1.get(ix));
      assertTrue(m1.matches());
      Matcher m2 = pat.matcher(l2.get(ix));
      assertTrue(m2.matches());
      assertEquals("www.example.com", m1.group(1));
      assertEquals("anotherhost.org", m2.group(1));
      assertEquals(m1.group(2), m2.group(2));
    }
  }

  public void testBaseUrlPath() throws Exception {
    sau1 = setupSimAu(simAuConfig(tempDirPath));
    createContent(sau1);
    crawlContent(sau1);
    CachedUrlSet cus1 = sau1.getAuCachedUrlSet();

    tempDirPath2 = getTempDir().getAbsolutePath() + File.separator;
    Configuration config2 = simAuConfig(tempDirPath2);
    config2.put("base_url", "http://anotherhost.org/some/path/");
    SimulatedArchivalUnit sau2 = setupSimAu(config2);
    createContent(sau2);
    crawlContent(sau2);
    CachedUrlSet cus2 = sau1.getAuCachedUrlSet();
    List urls1 = auUrls(sau1);
    List urls2 = auUrls(sau2);

    Pattern pat1 = Pattern.compile("http://www\\.example\\.com(/.*)$");
    Pattern pat2 = Pattern.compile("http://anotherhost\\.org/some/path(/.*)$");
    List<String> l1 = auUrls(sau1);
    List<String> l2 = auUrls(sau2);
    assertEquals(l1.size(), l2.size());
    for (int ix = 0; ix < l1.size(); ix++) {
      Matcher m1 = pat1.matcher(l1.get(ix));
      assertTrue(m1.matches());
      Matcher m2 = pat2.matcher(l2.get(ix));
      assertTrue(m2.matches());
      assertEquals(m1.group(1), m2.group(1));
    }
  }

  List<String> auUrls(ArchivalUnit au) {
    List<String> res = new ArrayList<String>();
    for (Iterator iter = au.getAuCachedUrlSet().contentHashIterator(); iter.hasNext(); ) {
      CachedUrlSetNode cusn = (CachedUrlSetNode) iter.next();
      if (cusn.hasContent()) {
        res.add(cusn.getUrl());
      }
    }
    return res;
  }

  protected void createContent(SimulatedArchivalUnit sau) {
    log.debug("createContent()");
    scgen = sau.getContentGenerator();
    scgen.setFileTypes(
        SimulatedContentGenerator.FILE_TYPE_HTML + SimulatedContentGenerator.FILE_TYPE_TXT);
    scgen.setAbnormalFile("1,1", 1);
    scgen.setOddBranchesHaveContent(true);

    sau.deleteContentTree();
    sau.generateContentTree();
    assertTrue(scgen.isContentTree());
  }

  protected void crawlContent(SimulatedArchivalUnit sau) {
    log.debug("crawlContent()");
    CrawlSpec spec = new SpiderCrawlSpec(sau.getNewContentCrawlUrls(), null);
    Crawler crawler = new NoCrawlEndActionsNewContentCrawler(sau, spec, new MockAuState());
    crawler.doCrawl();
  }

  protected void checkContent(SimulatedArchivalUnit sau) throws IOException {
    log.debug("checkContent()");
    checkRoot(sau);
    checkLeaf(sau);
    checkStoredContent(sau);
    checkDepth(sau);
  }

  protected void checkFilter(SimulatedArchivalUnit sau) throws Exception {
    log.debug("checkFilter()");
    CachedUrl cu = sau.makeCachedUrl(sau.getUrlRoot() + "/001file.html");

    enableFilter(sau, true);
    InputStream is = cu.openForHashing();
    String expected = "001file.html This is file 1, depth 0, branch 0. foobar ";
    assertEquals(expected, StringUtil.fromInputStream(is));
    is.close();
    enableFilter(sau, false);
    cu = sau.makeCachedUrl(sau.getUrlRoot() + "/001file.html");
    is = cu.openForHashing();
    expected =
        "<HTML><HEAD><TITLE>001file.html</TITLE></HEAD><BODY>\n"
            + "This is file 1, depth 0, branch 0.<br><!-- comment -->    "
            + "Citation String   foobar<br><script>"
            + "(defun fact (n) (cond ((= n 0) 1) (t (fact (sub1 n)))))</script>\n"
            + "</BODY></HTML>";
    assertEquals(expected, StringUtil.fromInputStream(is));
    is.close();
  }

  private byte[] fromHex(String hex) {
    return ByteArray.fromHexString(hex);
  }

  protected void hashContent(SimulatedArchivalUnit sau) throws Exception {
    log.debug("hashContent()");
    measureHashSpeed(sau);

    // If any changes are made to the contents or shape of the simulated
    // content tree, these hash values will have to be changed
    checkHashSet(sau, true, false, fromHex("6AB258B4E1FFD9F9B45316B4F54111FF5E5948D2"));
    checkHashSet(sau, true, true, fromHex("6AB258B4E1FFD9F9B45316B4F54111FF5E5948D2"));
    checkHashSet(sau, false, false, fromHex("409893F1A603F4C276632694DB1621B639BD5164"));
    checkHashSet(sau, false, true, fromHex("85E6213C3771BEAC5A4602CAF7982C6C222800D5"));
  }

  protected void checkDepth(SimulatedArchivalUnit sau) {
    log.debug("checkDepth()");
    String URL_ROOT = sau.getUrlRoot();
    assertEquals(0, sau.getLinkDepth(URL_ROOT + "/index.html"));
    assertEquals(0, sau.getLinkDepth(URL_ROOT + "/"));
    assertEquals(1, sau.getLinkDepth(URL_ROOT + "/001file.html"));
    assertEquals(1, sau.getLinkDepth(URL_ROOT + "/branch1/index.html"));
    assertEquals(1, sau.getLinkDepth(URL_ROOT + "/branch1/"));
    assertEquals(2, sau.getLinkDepth(URL_ROOT + "/branch1/001file.html"));
  }

  protected void checkRoot(SimulatedArchivalUnit sau) {
    log.debug("checkRoot()");
    CachedUrlSet set = sau.getAuCachedUrlSet();
    Iterator setIt = set.flatSetIterator();
    ArrayList childL = new ArrayList(1);
    CachedUrlSet cus = null;
    while (setIt.hasNext()) {
      cus = (CachedUrlSet) setIt.next();
      childL.add(cus.getUrl());
    }

    String urlRoot = sau.getUrlRoot();

    String[] expectedA = new String[1];
    expectedA[0] = urlRoot;
    assertIsomorphic(expectedA, childL);

    setIt = cus.flatSetIterator();
    childL = new ArrayList(7);
    while (setIt.hasNext()) {
      childL.add(((CachedUrlSetNode) setIt.next()).getUrl());
    }

    expectedA =
        new String[] {
          urlRoot + "/001file.html",
          urlRoot + "/001file.txt",
          urlRoot + "/002file.html",
          urlRoot + "/002file.txt",
          urlRoot + "/branch1",
          urlRoot + "/branch2",
          urlRoot + "/index.html"
        };
    assertIsomorphic(expectedA, childL);
  }

  protected void checkLeaf(SimulatedArchivalUnit sau) {
    log.debug("checkLeaf()");
    String parent = sau.getUrlRoot() + "/branch1";
    CachedUrlSetSpec spec = new RangeCachedUrlSetSpec(parent);
    CachedUrlSet set = sau.makeCachedUrlSet(spec);
    Iterator setIt = set.contentHashIterator();
    ArrayList childL = new ArrayList(16);
    while (setIt.hasNext()) {
      childL.add(((CachedUrlSetNode) setIt.next()).getUrl());
    }
    String[] expectedA =
        new String[] {
          parent,
          parent + "/001file.html",
          parent + "/001file.txt",
          parent + "/002file.html",
          parent + "/002file.txt",
          parent + "/branch1",
          parent + "/branch1/001file.html",
          parent + "/branch1/001file.txt",
          parent + "/branch1/002file.html",
          parent + "/branch1/002file.txt",
          parent + "/branch1/index.html",
          parent + "/branch2",
          parent + "/branch2/001file.html",
          parent + "/branch2/001file.txt",
          parent + "/branch2/002file.html",
          parent + "/branch2/002file.txt",
          parent + "/branch2/index.html",
          parent + "/index.html",
        };
    assertIsomorphic(expectedA, childL);
  }

  protected void checkUrlContent(
      SimulatedArchivalUnit sau,
      String path,
      int fileNum,
      int depth,
      int branchNum,
      boolean isAbnormal,
      boolean isDamaged)
      throws IOException {
    String file = sau.getUrlRoot() + path;
    CachedUrl url = sau.makeCachedUrl(file);
    String content = getUrlContent(url);
    String expectedContent;
    if (path.endsWith(".html")) {
      String fn = path.substring(path.lastIndexOf("/") + 1);
      expectedContent = scgen.getHtmlFileContent(fn, fileNum, depth, branchNum, isAbnormal);
    } else {
      expectedContent = scgen.getTxtContent(fileNum, depth, branchNum, isAbnormal);
    }
    if (isDamaged) {
      assertNotEquals(expectedContent, content);
    } else {
      assertEquals(expectedContent, content);
    }
  }

  protected void checkStoredContent(SimulatedArchivalUnit sau) throws IOException {
    checkUrlContent(sau, "/001file.txt", 1, 0, 0, false, false);
    checkUrlContent(sau, "/branch1/branch1/001file.txt", 1, 2, 1, true, false);
    checkUrlContent(sau, DAMAGED_CACHED_URL, 2, 2, 2, false, true);
  }

  protected void doDamageRemoveTest(SimulatedArchivalUnit sau) throws Exception {
    /* Cache the file again; this time the damage should be gone */
    String file = sau.getUrlRoot() + DAMAGED_CACHED_URL;
    UrlCacher uc = sau.makeUrlCacher(file);
    BitSet fetchFlags = new BitSet();
    fetchFlags.set(UrlCacher.REFETCH_FLAG);
    uc.setFetchFlags(fetchFlags);
    uc.cache();
    checkUrlContent(sau, DAMAGED_CACHED_URL, 2, 2, 2, false, false);
  }

  private void measureHashSpeed(SimulatedArchivalUnit sau) throws Exception {
    MessageDigest dig = null;
    try {
      dig = MessageDigest.getInstance("SHA-1");
    } catch (NoSuchAlgorithmException ex) {
      fail("No algorithm.");
    }
    CachedUrlSet set = sau.getAuCachedUrlSet();
    CachedUrlSetHasher hasher = set.getContentHasher(dig);
    SystemMetrics metrics = theDaemon.getSystemMetrics();
    int estimate = metrics.getBytesPerMsHashEstimate(hasher, dig);
    // should be protected against this being zero by MyMockSystemMetrics,
    // but otherwise use the proper calculation.  This avoids test failure
    // due to really slow machines
    assertTrue(estimate > 0);
    long estimatedTime = set.estimatedHashDuration();
    long size = ((Long) PrivilegedAccessor.getValue(set, "totalNodeSize")).longValue();
    assertTrue(size > 0);
    System.out.println("b/ms: " + estimate);
    System.out.println("size: " + size);
    System.out.println("estimate: " + estimatedTime);
    assertEquals(estimatedTime, theDaemon.getHashService().padHashEstimate(size / estimate));
  }

  private void checkHashSet(
      SimulatedArchivalUnit sau, boolean namesOnly, boolean filter, byte[] expected)
      throws Exception {
    enableFilter(sau, filter);
    CachedUrlSet set = sau.getAuCachedUrlSet();
    byte[] hash = getHash(set, namesOnly);
    assertEquals(expected, hash);

    String parent = sau.getUrlRoot() + "/branch1";
    CachedUrlSetSpec spec = new RangeCachedUrlSetSpec(parent);
    set = sau.makeCachedUrlSet(spec);
    byte[] hash2 = getHash(set, namesOnly);
    assertFalse(Arrays.equals(hash, hash2));
  }

  private byte[] getHash(CachedUrlSet set, boolean namesOnly) throws IOException {
    MessageDigest dig = null;
    try {
      dig = MessageDigest.getInstance("SHA-1");
    } catch (NoSuchAlgorithmException ex) {
      fail("No algorithm.");
    }
    hash(set, dig, namesOnly);
    return dig.digest();
  }

  private void hash(CachedUrlSet set, MessageDigest dig, boolean namesOnly) throws IOException {
    CachedUrlSetHasher hasher = null;
    if (namesOnly) {
      hasher = set.getNameHasher(dig);
    } else {
      hasher = set.getContentHasher(dig);
    }
    int bytesHashed = 0;
    long timeTaken = System.currentTimeMillis();
    while (!hasher.finished()) {
      bytesHashed += hasher.hashStep(256);
    }
    timeTaken = System.currentTimeMillis() - timeTaken;
    if ((timeTaken > 0) && (bytesHashed > 500)) {
      System.out.println("Bytes hashed: " + bytesHashed);
      System.out.println("Time taken: " + timeTaken + "ms");
      System.out.println("Bytes/sec: " + (bytesHashed * 1000 / timeTaken));
    } else {
      System.out.println("No time taken, or insufficient bytes hashed.");
      System.out.println("Bytes hashed: " + bytesHashed);
      System.out.println("Time taken: " + timeTaken + "ms");
    }
  }

  private String getUrlContent(CachedUrl url) throws IOException {
    InputStream content = url.getUnfilteredInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    StreamUtil.copy(content, baos);
    content.close();
    String contentStr = new String(baos.toByteArray());
    baos.close();
    return contentStr;
  }

  // this version doesn't fully override the 'measureHashSpeed()' function, but
  // protects against it returning '0' by returning the set speed
  private class MyMockSystemMetrics extends MockSystemMetrics {
    public int measureHashSpeed(CachedUrlSetHasher hasher, MessageDigest digest)
        throws IOException {
      int speed = super.measureHashSpeed(hasher, digest);
      if (speed == 0) {
        speed = getHashSpeed();
        if (speed <= 0) {
          throw new RuntimeException("No hash speed set.");
        }
      }
      return speed;
    }
  }

  public static void main(String[] argv) {
    String[] testCaseList = {FuncSimulatedContent.class.getName()};
    junit.swingui.TestRunner.main(testCaseList);
  }

  public static Test suite() {
    return new TestSuite(FuncSimulatedContent.class);
  }
}
  public void testStatelessSearch()
      throws org.jzkit.configuration.api.ConfigurationException, org.jzkit.search.SearchException,
          org.jzkit.search.util.ResultSet.IRResultSetException,
          org.jzkit.search.util.QueryModel.InvalidQueryException {

    Logger log = Logger.getLogger(TestService.class.getName());

    log.info("Starting jzkit2 server...");

    RecordFormatSpecification request_spec = new ArchetypeRecordFormatSpecification("F");
    ExplicitRecordFormatSpecification display_spec =
        new ExplicitRecordFormatSpecification("text:html:F");

    ApplicationContext app_context =
        new ClassPathXmlApplicationContext("TestApplicationContext.xml");
    log.info("JZKit server startup completed");

    Vector collection_ids = new Vector();
    collection_ids.add("LC/BOOKS");

    QueryModel qm = new PrefixString("@attrset bib-1 @attr 1=4 Science");

    System.err.println("Processing search......");

    try {
      Map additional_properties = new HashMap();
      additional_properties.put("base_dir", "/a/b/c/d");

      StatelessQueryService stateless_query_service =
          (StatelessQueryService) app_context.getBean("StatelessQueryService");

      org.jzkit.search.landscape.SimpleLandscapeSpecification landscape =
          new org.jzkit.search.landscape.SimpleLandscapeSpecification(collection_ids);
      // Test 1 - Kick off a search
      StatelessSearchResultsPageDTO rp =
          stateless_query_service.getResultsPageFor(
              null, qm, landscape, 1, 5, request_spec, display_spec, additional_properties);

      if (rp != null) {
        System.err.println(
            "Result Set Size....."
                + rp.total_hit_count
                + " records - result contains "
                + rp.number_of_records
                + " records");
        System.err.println("Result Set ID : " + rp.result_set_id);
      } else {
        System.err.println("Results page was null");
      }

      if (rp.records != null) {
        for (int i = 0; ((i < rp.records.length) && (i < 25)); i++) {
          System.err.println(
              "Getting next record (" + i + " out of " + rp.number_of_records + ").....");
          InformationFragment frag = rp.records[i];
          System.err.println(frag);
        }
      }

      // Test 2 - use the result set ID to get a page of requests
      rp =
          stateless_query_service.getResultsPageFor(
              rp.result_set_id,
              qm,
              landscape,
              6,
              5,
              request_spec,
              display_spec,
              additional_properties);

      if (rp.records != null) {
        for (int i = 0; ((i < rp.records.length) && (i < 25)); i++) {
          System.err.println(
              "Getting next record (" + i + " out of " + rp.number_of_records + ").....");
          InformationFragment frag = rp.records[i];
          System.err.println(frag);
        }
      }

      // Test 3 - Use the query to get a cache hit
      rp =
          stateless_query_service.getResultsPageFor(
              null, qm, landscape, 6, 5, request_spec, display_spec, additional_properties);

      if (rp.records != null) {
        for (int i = 0; ((i < rp.records.length) && (i < 25)); i++) {
          System.err.println(
              "Getting next record (" + i + " out of " + rp.number_of_records + ").....");
          InformationFragment frag = rp.records[i];
          System.err.println(frag);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
/**
 * Performs testing of the basic instant messaging operation set. Tests include going over basic
 * functionality such as sending a message from the tested implementation and asserting reception by
 * the tester agent and vice versa.
 *
 * @author Emil Ivov
 */
public class TestOperationSetBasicInstantMessaging extends TestCase {
  private static final Logger logger =
      Logger.getLogger(TestOperationSetBasicInstantMessaging.class);

  private IcqSlickFixture fixture = new IcqSlickFixture();

  private OperationSetBasicInstantMessaging opSetBasicIM = null;
  private OperationSetPresence opSetPresence = null;

  public TestOperationSetBasicInstantMessaging(String name) {
    super(name);
  }

  /**
   * Get a reference to the basic IM operation set.
   *
   * @throws Exception if this is not a good day.
   */
  protected void setUp() throws Exception {
    super.setUp();
    fixture.setUp();

    Map supportedOperationSets = fixture.provider.getSupportedOperationSets();

    if (supportedOperationSets == null || supportedOperationSets.size() < 1)
      throw new NullPointerException(
          "No OperationSet implementations are supported by " + "this ICQ implementation. ");

    // get the operation set presence here.
    opSetBasicIM =
        (OperationSetBasicInstantMessaging)
            supportedOperationSets.get(OperationSetBasicInstantMessaging.class.getName());

    // if the op set is null then the implementation doesn't offer a typing.
    // operation set which is unacceptable for icq.
    if (opSetBasicIM == null) {
      throw new NullPointerException("No implementation for basic IM was found");
    }

    // we also need the presence op set in order to retrieve contacts.
    opSetPresence =
        (OperationSetPresence) supportedOperationSets.get(OperationSetPresence.class.getName());

    // if the op set is null show that we're not happy.
    if (opSetPresence == null) {
      throw new NullPointerException(
          "An implementation of the ICQ service must provide an "
              + "implementation of at least one of the PresenceOperationSets");
    }
  }

  protected void tearDown() throws Exception {
    super.tearDown();

    fixture.tearDown();
  }

  /**
   * Creates a test suite containing tests of this class in a specific order. We'll first execute
   * tests beginning with the "test" prefix and then go to ordered tests.We first execture tests for
   * receiving messagese, so that a volatile contact is created for the sender. we'll then be able
   * to retrieve this volatile contact and send them a message on our turn. We need to do things
   * this way as the contact corresponding to the tester agent has been removed in the previous test
   * and we no longer have it in our contact list.
   *
   * @return Test a testsuite containing all tests to execute.
   */
  public static Test suite() {
    TestSuite suite = new TestSuite(TestOperationSetBasicInstantMessaging.class);

    // the following 2 need to be run in the specified order.
    suite.addTest(new TestOperationSetBasicInstantMessaging("firstTestReceiveMessage"));
    suite.addTest(new TestOperationSetBasicInstantMessaging("thenTestSendMessage"));

    return suite;
  }

  /**
   * Send an instant message from the tested operation set and assert reception by the icq tester
   * agent.
   */
  public void firstTestReceiveMessage() {
    String body = "This is an IM coming from the tester agent" + " on " + new Date().toString();

    ImEventCollector evtCollector = new ImEventCollector();

    // add a msg listener and register to the op set and send an instant
    // msg from the tester agent.
    opSetBasicIM.addMessageListener(evtCollector);

    fixture.testerAgent.sendMessage(fixture.ourUserID, body);

    evtCollector.waitForEvent(10000);

    opSetBasicIM.removeMessageListener(evtCollector);

    // assert reception of a message event
    assertTrue(
        "No events delivered upon a received message", evtCollector.collectedEvents.size() > 0);

    // assert event instance of Message Received Evt
    assertTrue(
        "Received evt was not an instance of " + MessageReceivedEvent.class.getName(),
        evtCollector.collectedEvents.get(0) instanceof MessageReceivedEvent);

    // assert source contact == testAgent.uin
    MessageReceivedEvent evt = (MessageReceivedEvent) evtCollector.collectedEvents.get(0);
    assertEquals(
        "message sender ", evt.getSourceContact().getAddress(), fixture.testerAgent.getIcqUIN());

    // assert messageBody == body
    assertEquals("message body", body, evt.getSourceMessage().getContent());
  }

  /**
   * Send an instant message from the tester agent and assert reception by the tested implementation
   */
  public void thenTestSendMessage() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();

    // create the message
    net.java.sip.communicator.service.protocol.Message msg = opSetBasicIM.createMessage(body);

    // register a listener in the op set
    ImEventCollector imEvtCollector = new ImEventCollector();
    opSetBasicIM.addMessageListener(imEvtCollector);

    // register a listener in the tester agent
    JoustSimMessageEventCollector jsEvtCollector = new JoustSimMessageEventCollector();
    fixture.testerAgent.addConversationListener(fixture.ourUserID, jsEvtCollector);

    Contact testerAgentContact = opSetPresence.findContactByID(fixture.testerAgent.getIcqUIN());

    opSetBasicIM.sendInstantMessage(testerAgentContact, msg);

    imEvtCollector.waitForEvent(10000);
    jsEvtCollector.waitForEvent(10000);

    fixture.testerAgent.removeConversationListener(fixture.ourUserID, jsEvtCollector);
    opSetBasicIM.removeMessageListener(imEvtCollector);

    // verify that the message delivered event was dispatched
    assertTrue(
        "No events delivered when sending a message", imEvtCollector.collectedEvents.size() > 0);

    assertTrue(
        "Received evt was not an instance of " + MessageDeliveredEvent.class.getName(),
        imEvtCollector.collectedEvents.get(0) instanceof MessageDeliveredEvent);

    MessageDeliveredEvent evt = (MessageDeliveredEvent) imEvtCollector.collectedEvents.get(0);
    assertEquals(
        "message destination ",
        evt.getDestinationContact().getAddress(),
        fixture.testerAgent.getIcqUIN());

    assertSame("source message", msg, evt.getSourceMessage());

    // verify that the message has successfully arived at the destination
    assertTrue(
        "No messages received by the tester agent", jsEvtCollector.collectedMessageInfo.size() > 0);
    String receivedBody =
        ((MessageInfo) jsEvtCollector.collectedMessageInfo.get(0)).getMessage().getMessageBody();

    assertEquals("received message body", msg.getContent(), receivedBody);
  }

  /** Creates an Message through the simple createMessage() method and inspects its parameters. */
  public void testCreateMessage1() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();
    net.java.sip.communicator.service.protocol.Message msg = opSetBasicIM.createMessage(body);

    assertEquals("message body", body, msg.getContent());
    assertTrue("message body bytes", Arrays.equals(body.getBytes(), msg.getRawData()));
    assertEquals("message length", body.length(), msg.getSize());
    assertEquals(
        "message content type",
        OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
        msg.getContentType());

    assertEquals(
        "message encoding",
        OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING,
        msg.getEncoding());

    assertNotNull("message uid", msg.getMessageUID());

    // a further test on message uid.
    net.java.sip.communicator.service.protocol.Message msg2 = opSetBasicIM.createMessage(body);
    assertFalse("message uid", msg.getMessageUID().equals(msg2.getMessageUID()));
  }

  /** Creates an Message through the advance createMessage() method and inspects its parameters. */
  public void testCreateMessage2() {
    String body =
        "This is an IM coming from the tested implementation" + " on " + new Date().toString();
    String contentType = "text/html";
    String encoding = "UTF-16";
    String subject = "test message";
    net.java.sip.communicator.service.protocol.Message msg =
        opSetBasicIM.createMessage(body.getBytes(), contentType, encoding, subject);

    assertEquals("message body", body, msg.getContent());
    assertTrue("message body bytes", Arrays.equals(body.getBytes(), msg.getRawData()));
    assertEquals("message length", body.length(), msg.getSize());
    assertEquals("message content type", contentType, msg.getContentType());
    assertEquals("message encoding", encoding, msg.getEncoding());
    assertNotNull("message uid", msg.getMessageUID());

    // a further test on message uid.
    net.java.sip.communicator.service.protocol.Message msg2 = opSetBasicIM.createMessage(body);
    assertFalse("message uid", msg.getMessageUID().equals(msg2.getMessageUID()));
  }

  /** Collects instant messaging events. */
  private class ImEventCollector implements MessageListener {
    private List collectedEvents = new LinkedList();
    /**
     * Called when a new incoming <tt>Message</tt> has been received.
     *
     * @param evt the <tt>MessageReceivedEvent</tt> containing the newly received message, its
     *     sender and other details.
     */
    public void messageReceived(MessageReceivedEvent evt) {
      logger.debug("Received a MessageReceivedEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called to indicated that delivery of a message sent earlier has failed. Reason code and
     * phrase are contained by the <tt>MessageFailedEvent</tt>
     *
     * @param evt the <tt>MessageFailedEvent</tt> containing the ID of the message whose delivery
     *     has failed.
     */
    public void messageDeliveryFailed(MessageDeliveryFailedEvent evt) {
      logger.debug("Received a MessageDeliveryFailedEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Called when the underlying implementation has received an indication that a message, sent
     * earlier has been successfully received by the destination.
     *
     * @param evt the MessageDeliveredEvent containing the id of the message that has caused the
     *     event.
     */
    public void messageDelivered(MessageDeliveredEvent evt) {
      logger.debug("Received a MessageDeliveredEvent: " + evt);

      synchronized (this) {
        collectedEvents.add(evt);
        notifyAll();
      }
    }

    /**
     * Blocks until at least one event is received or until waitFor miliseconds pass (whichever
     * happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedEvents.size() > 0) return;

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a message evt", ex);
        }
      }
    }
  }

  /** The oscar.jar lib sends us typing events through this listener. */
  private class JoustSimMessageEventCollector implements ConversationListener {
    private List collectedMessageInfo = new LinkedList();

    /**
     * Adds <tt>minfo</tt> into the list of collected messages.
     *
     * @param c Conversation
     * @param minfo MessageInfo
     */
    public void gotMessage(Conversation c, MessageInfo minfo) {

      logger.debug("Message: [" + minfo.getMessage() + "] received from: " + c.getBuddy());
      synchronized (this) {
        collectedMessageInfo.add(minfo);
        notifyAll();
      }
    }

    /**
     * Blocks until at least one event is received or until waitFor miliseconds pass (whicever
     * happens first).
     *
     * @param waitFor the number of miliseconds that we should be waiting for an event before simply
     *     bailing out.
     */
    public void waitForEvent(long waitFor) {
      synchronized (this) {
        if (collectedMessageInfo.size() > 0) {
          logger.trace("evt already received. " + collectedMessageInfo);
          return;
        }

        try {
          wait(waitFor);
        } catch (InterruptedException ex) {
          logger.debug("Interrupted while waiting for a subscription evt", ex);
        }
      }
    }

    // the follwoing methods only have dummy implementations here as they
    // do not interest us. complete implementatios are provider in the
    // basic instant messaging operation set.
    public void buddyInfoUpdated(IcbmService service, Screenname buddy, IcbmBuddyInfo info) {}

    public void conversationClosed(Conversation c) {}

    public void gotOtherEvent(Conversation conversation, ConversationEventInfo event) {}

    public void sentOtherEvent(Conversation conversation, ConversationEventInfo event) {}

    public void canSendMessageChanged(Conversation c, boolean canSend) {}

    public void conversationOpened(Conversation c) {}

    public void newConversation(IcbmService service, Conversation conv) {}

    public void sentMessage(Conversation c, MessageInfo minfo) {}
  }

  /**
   * A method that would simply send messages to a group of people so that they would get notified
   * that tests are being run.
   */
  public void testSendFunMessages() {
    String hostname = "";

    try {
      hostname = java.net.InetAddress.getLocalHost().getHostName() + ": ";
    } catch (UnknownHostException ex) {
    }

    String message =
        hostname
            + "Hello this is the SIP Communicator (version "
            + System.getProperty("sip-communicator.version")
            + ") build on: "
            + new Date().toString()
            + ". Have a very nice day!";

    String list = System.getProperty("accounts.reporting.ICQ_REPORT_LIST");

    logger.debug("Will send message " + message + " to: " + list);

    // if no property is specified - return
    if (list == null || list.trim().length() == 0) return;

    StringTokenizer tokenizer = new StringTokenizer(list, " ");

    while (tokenizer.hasMoreTokens()) {
      fixture.testerAgent.sendMessage(tokenizer.nextToken(), message);
    }
  }

  /** Tests whether there is a offline message received and whether is the one we have send */
  public void testReceiveOfflineMessages() {
    String messageText = fixture.offlineMsgCollector.getMessageText();

    Message receiveMessage = fixture.offlineMsgCollector.getReceivedMessage();

    assertNotNull("No Offline messages have been received", receiveMessage);
    assertEquals("message body", messageText, receiveMessage.getContent());
  }
}
Example #10
0
public class CodingTest extends TestCase {

  private static final Logger LOG = Logger.getLogger(CodingTest.class.getName());

  /**
   * Constructor for this unit test.
   *
   * @param testName the name of the unit test
   */
  public CodingTest(String testName) {
    super(testName);
  }

  /**
   * Allows unit tests to be run together in a suite.
   *
   * @return a test suite that contains a single test - this one
   */
  public static Test suite() {
    return new TestSuite(CodingTest.class);
  }

  /**
   * Allows test to be run stand-alone from the command-line.
   *
   * <p>java -Djava.library.path=/opt/honeycomb/lib -classpath
   * test/lib/junit-3.8.1.jar:test/classes:classes com.sun.honeycomb.coding.CodingTest
   */
  public static void main(String args[]) {
    junit.textui.TestRunner.run(suite());
  }

  public void setUp() {}

  public void tearDown() {}

  public void testByteBufferCoder() {
    Persistent3 three =
        new Persistent3(
            10,
            (float) 4.444,
            555555555555L,
            true,
            "this is the fourth persistent string",
            new Date(),
            new byte[] {10, 20, 30, 40, 50, 60, 70, 80, 90, 100},
            null);

    Persistent2 two =
        new Persistent2(
            1,
            (float) 2.222,
            333333333333L,
            false,
            "this is the first persistent string",
            new Date(),
            new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
            three,
            4,
            "this is the second persistent string",
            (ByteBuffer) ByteBuffer.allocateDirect(17).putInt(1234).rewind());

    Persistent1 one =
        new Persistent1(
            2,
            (float) 3.333,
            4444444444444L,
            true,
            "this is the third persistent string",
            new Date(),
            new byte[] {10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
            two);

    ByteBuffer buffer = ByteBuffer.allocateDirect(8 * 1024);
    ByteBufferCoder encoder = new ByteBufferCoder(buffer, true);

    encoder.encodeCodable(one);
    buffer.rewind();

    ByteBufferCoder decoder = new ByteBufferCoder(buffer, true, new Delegate());
    Persistent1 anotherOne = (Persistent1) decoder.decodeCodable();

    if (!one.equals(anotherOne)) {
      fail("unarchived object with type checking does not equal archived one");
    }

    buffer.clear();
    encoder = new ByteBufferCoder(buffer, false);

    encoder.encodeCodable(one);
    buffer.rewind();

    decoder = new ByteBufferCoder(buffer, false, new Delegate());
    anotherOne = (Persistent1) decoder.decodeCodable();

    if (!one.equals(anotherOne)) {
      fail("unarchived object without type checking does not equal archived one");
    }
  }

  public static class Persistent1 implements Codable, Serializable {
    private int int1;
    private float float1;
    private long long1;
    private boolean boolean1;
    private String string1;
    private Date date1;
    private byte[] bytes1;
    private Codable codable1;

    public Persistent1() {}

    public Persistent1(
        int newInt,
        float newFloat,
        long newLong,
        boolean newBoolean,
        String newString,
        Date newDate,
        byte[] newBytes,
        Codable newCodable) {
      int1 = newInt;
      float1 = newFloat;
      long1 = newLong;
      boolean1 = newBoolean;
      string1 = newString;
      date1 = newDate;
      bytes1 = newBytes;
      codable1 = newCodable;
    }

    public void encode(Encoder encoder) {
      encoder.encodeInt(int1);
      encoder.encodeFloat(float1);
      encoder.encodeLong(long1);
      encoder.encodeBoolean(boolean1);
      encoder.encodeString(string1);
      encoder.encodeDate(date1);
      encoder.encodeBytes(bytes1);
      encoder.encodeCodable(codable1);
    }

    public void decode(Decoder decoder) {
      int1 = decoder.decodeInt();
      float1 = decoder.decodeFloat();
      long1 = decoder.decodeLong();
      boolean1 = decoder.decodeBoolean();
      string1 = decoder.decodeString();
      date1 = decoder.decodeDate();
      bytes1 = decoder.decodeBytes();
      codable1 = decoder.decodeCodable();
    }

    public boolean equals(Object other) {
      Persistent1 otherPersistent1 = (Persistent1) other;

      if (int1 != otherPersistent1.int1) {
        System.out.println("int1 failed");
        return false;
      }

      if (float1 != otherPersistent1.float1) {
        System.out.println("float1 failed");
        return false;
      }

      if (long1 != otherPersistent1.long1) {
        System.out.println("long1 failed");
        return false;
      }

      if (boolean1 != otherPersistent1.boolean1) {
        System.out.println("boolean1 failed");
        return false;
      }

      if (string1 != otherPersistent1.string1
          && (string1 != null
              && !string1.equals(otherPersistent1.string1)
              && otherPersistent1.string1 != null)) {
        System.out.println("string1 failed");
        return false;
      }

      if (date1 != otherPersistent1.date1
          && (date1 != null
              && !date1.equals(otherPersistent1.date1)
              && otherPersistent1.date1 != null)) {
        System.out.println("date1 failed");
        return false;
      }

      if (bytes1 != otherPersistent1.bytes1
          && (bytes1 != null
              && !Arrays.equals(bytes1, otherPersistent1.bytes1)
              && otherPersistent1.bytes1 != null)) {
        System.out.println("bytes1 failed");
        return false;
      }

      if (codable1 != otherPersistent1.codable1
          && (codable1 != null
              && !codable1.equals(otherPersistent1.codable1)
              && otherPersistent1.codable1 != null)) {
        System.out.println("codable1 failed");
        return false;
      }

      return true;
    }
  }

  private static class Persistent2 extends Persistent1 {
    private int int2;
    private String string2;
    private ByteBuffer buffer2;

    public Persistent2() {}

    public Persistent2(
        int newInt,
        float newFloat,
        long newLong,
        boolean newBoolean,
        String newString,
        Date newDate,
        byte[] newBytes,
        Codable newCodable,
        int newInt2,
        String newString2,
        ByteBuffer newBuffer2) {
      super(newInt, newFloat, newLong, newBoolean, newString, newDate, newBytes, newCodable);

      int2 = newInt2;
      string2 = newString2;
      buffer2 = newBuffer2;
    }

    public void encode(Encoder encoder) {
      super.encode(encoder);

      encoder.encodeInt(int2);
      encoder.encodeString(string2);
      encoder.encodeInt(buffer2.remaining());
      encoder.encodeKnownLengthBuffer(buffer2);
    }

    public void decode(Decoder decoder) {
      super.decode(decoder);

      int2 = decoder.decodeInt();
      string2 = decoder.decodeString();

      int length = decoder.decodeInt();
      buffer2 = decoder.decodeKnownLengthBuffer(length, true);
    }

    public boolean equals(Object other) {
      if (!super.equals(other)) {
        return false;
      }

      Persistent2 otherPersistent2 = (Persistent2) other;

      if (int2 != otherPersistent2.int2) {
        System.out.println("int2 failed");
        return false;
      }

      if (string2 != otherPersistent2.string2
          && (string2 != null
              && !string2.equals(otherPersistent2.string2)
              && otherPersistent2.string2 != null)) {
        System.out.println("string2 failed");
        return false;
      }

      if (buffer2 != otherPersistent2.buffer2
          && (buffer2 != null
              && !buffer2.equals(otherPersistent2.buffer2)
              && otherPersistent2.buffer2 != null)) {
        System.out.println("buffer2 failed");
        return false;
      }

      return true;
    }
  }

  static class Persistent3 extends Persistent1 {

    Persistent3() {}

    Persistent3(
        int newInt,
        float newFloat,
        long newLong,
        boolean newBoolean,
        String newString,
        Date newDate,
        byte[] newBytes,
        Codable newCodable) {
      super(newInt, newFloat, newLong, newBoolean, newString, newDate, newBytes, newCodable);
    }
  }

  static class Delegate implements Decoder.Delegate {
    public Codable newInstance(String className)
        throws ClassNotFoundException, IllegalAccessException, InstantiationException {
      return (Codable) Class.forName(className).newInstance();
    }
  }
}