Repository: camel Updated Branches: refs/heads/master f8c140f01 -> 5eed4d966
CAMEL-7873 Add capability to sort received messages Add copyright headers Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1149fae7 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1149fae7 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1149fae7 Branch: refs/heads/master Commit: 1149fae798de25ec43b330d41b11db85273c3765 Parents: f8c140f Author: Christian Amann <christian.am...@qaware.de> Authored: Wed Sep 24 13:15:43 2014 +0200 Committer: Willem Jiang <willem.ji...@gmail.com> Committed: Mon Oct 20 11:34:06 2014 +0800 ---------------------------------------------------------------------- components/camel-mail/pom.xml | 5 + .../camel/component/mail/MailConsumer.java | 86 +++++++-- .../camel/component/mail/MailEndpoint.java | 36 ++-- .../apache/camel/component/mail/MailSorter.java | 174 +++++++++++++++++++ .../camel/component/mail/MailSortTermTest.java | 121 +++++++++++++ .../camel/component/mail/MailSorterTest.java | 154 ++++++++++++++++ 6 files changed, 552 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-mail/pom.xml b/components/camel-mail/pom.xml index 50cc9fa..4888f4d 100644 --- a/components/camel-mail/pom.xml +++ b/components/camel-mail/pom.xml @@ -88,6 +88,11 @@ <artifactId>slf4j-log4j12</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java ---------------------------------------------------------------------- diff --git a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java index 065a4ef..29835aa 100644 --- a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java +++ b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java @@ -27,7 +27,11 @@ import javax.mail.Header; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Store; +import javax.mail.search.SearchTerm; +import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPStore; +import com.sun.mail.imap.SortTerm; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.impl.ScheduledBatchPollingConsumer; @@ -51,6 +55,11 @@ public class MailConsumer extends ScheduledBatchPollingConsumer { private Folder folder; private Store store; + /** + * Is true if server is an IMAP server and supports IMAP SORT extension. + */ + private boolean serverCanSort; + public MailConsumer(MailEndpoint endpoint, Processor processor, JavaMailSender sender) { super(endpoint, processor); this.sender = sender; @@ -103,18 +112,7 @@ public class MailConsumer extends ScheduledBatchPollingConsumer { try { int count = folder.getMessageCount(); if (count > 0) { - Message[] messages; - - if (getEndpoint().getSearchTerm() != null) { - // use custom search term - messages = folder.search(getEndpoint().getSearchTerm()); - } else if (getEndpoint().getConfiguration().isUnseen()) { - // only unseen messages - messages = folder.search(new SearchTermBuilder().unseen().build()); - } else { - // get all messages - messages = folder.getMessages(); - } + Message[] messages = retrieveMessages(); polledMessages = processBatch(CastUtils.cast(createExchanges(messages))); } else if (count == -1) { @@ -229,6 +227,51 @@ public class MailConsumer extends ScheduledBatchPollingConsumer { } } + /** + * @return Messages from input folder according to the search and sort criteria stored in the endpoint + * @throws MessagingException If message retrieval fails + */ + private Message[] retrieveMessages() throws MessagingException { + Message[] messages; + final SortTerm[] sortTerm = getEndpoint().getSortTerm(); + final SearchTerm searchTerm = computeSearchTerm(); + if (sortTerm != null && serverCanSort) { + final IMAPFolder imapFolder = (IMAPFolder) folder; + if (searchTerm != null) { + // Sort and search using server capability + messages = imapFolder.getSortedMessages(sortTerm, searchTerm); + } else { + // Only sort using server capability + messages = imapFolder.getSortedMessages(sortTerm); + } + } else { + if (searchTerm != null) { + // Only search + messages = folder.search(searchTerm); + } else { + // No search + messages = folder.getMessages(); + } + // Now we can sort (emulate email sort but restrict sort terms) + if (sortTerm != null) { + MailSorter.sortMessages(messages, sortTerm); + } + } + return messages; + } + + /** + * @return Search term from endpoint (including "seen" check) or null if there is no search term + */ + private SearchTerm computeSearchTerm() { + if (getEndpoint().getSearchTerm() != null) { + return getEndpoint().getSearchTerm(); + } else if (getEndpoint().getConfiguration().isUnseen()) { + return new SearchTermBuilder().unseen().build(); + } + return null; + } + protected Queue<Exchange> createExchanges(Message[] messages) throws MessagingException { Queue<Exchange> answer = new LinkedList<Exchange>(); @@ -423,6 +466,8 @@ public class MailConsumer extends ScheduledBatchPollingConsumer { } store = sender.getSession().getStore(config.getProtocol()); store.connect(config.getHost(), config.getPort(), config.getUsername(), config.getPassword()); + + serverCanSort = hasSortCapability(store); } if (folder == null) { @@ -436,6 +481,23 @@ public class MailConsumer extends ScheduledBatchPollingConsumer { } } + /** + * Check whether the email store has the sort capability or not. + * + * @param store Email store + * @return true if the store is an IMAP store and it has the store capability + * @throws MessagingException In case capability check fails + */ + private static boolean hasSortCapability(Store store) throws MessagingException { + if (store instanceof IMAPStore) { + IMAPStore imapStore = (IMAPStore) store; + if (imapStore.hasCapability("SORT*")) { + return true; + } + } + return false; + } + @Override public MailEndpoint getEndpoint() { return (MailEndpoint) super.getEndpoint(); http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java index e1345ac..a875612 100644 --- a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java +++ b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java @@ -16,14 +16,8 @@ */ package org.apache.camel.component.mail; -import javax.mail.Message; -import javax.mail.search.SearchTerm; - -import org.apache.camel.Consumer; -import org.apache.camel.Exchange; -import org.apache.camel.ExchangePattern; -import org.apache.camel.Processor; -import org.apache.camel.Producer; +import com.sun.mail.imap.SortTerm; +import org.apache.camel.*; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.DefaultHeaderFilterStrategy; import org.apache.camel.impl.ScheduledPollEndpoint; @@ -31,10 +25,11 @@ import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; +import javax.mail.Message; +import javax.mail.search.SearchTerm; + /** * Endpoint for Camel Mail. - * - * @version */ @UriEndpoint(scheme = "mail", consumerClass = MailConsumer.class) public class MailEndpoint extends ScheduledPollEndpoint { @@ -46,6 +41,7 @@ public class MailEndpoint extends ScheduledPollEndpoint { @UriParam private int maxMessagesPerPoll; private SearchTerm searchTerm; + private SortTerm[] sortTerm; public MailEndpoint() { } @@ -84,7 +80,7 @@ public class MailEndpoint extends ScheduledPollEndpoint { public Consumer createConsumer(Processor processor) throws Exception { if (configuration.getProtocol().startsWith("smtp")) { throw new IllegalArgumentException("Protocol " + configuration.getProtocol() - + " cannot be used for a MailConsumer. Please use another protocol such as pop3 or imap."); + + " cannot be used for a MailConsumer. Please use another protocol such as pop3 or imap."); } // must use java mail sender impl as we need to get hold of a mail session @@ -111,7 +107,7 @@ public class MailEndpoint extends ScheduledPollEndpoint { public boolean isSingleton() { return false; } - + @Override public Exchange createExchange(ExchangePattern pattern) { return createExchange(pattern, null); @@ -185,4 +181,20 @@ public class MailEndpoint extends ScheduledPollEndpoint { this.searchTerm = searchTerm; } + /** + * @return Sorting order for messages. Only natively supported for IMAP. Emulated to some degree when using POP3 + * or when IMAP server does not have the SORT capability. + * @see com.sun.mail.imap.SortTerm + */ + public SortTerm[] getSortTerm() { + return sortTerm == null ? null : sortTerm.clone(); + } + + /** + * @param sortTerm {@link #getSortTerm()} + */ + public void setSortTerm(SortTerm[] sortTerm) { + this.sortTerm = (sortTerm == null ? null : sortTerm.clone()); + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java ---------------------------------------------------------------------- diff --git a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java new file mode 100644 index 0000000..cdfb22c --- /dev/null +++ b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailSorter.java @@ -0,0 +1,174 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.mail; + +import com.sun.mail.imap.SortTerm; + +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; +import java.util.*; + +/** + * Utility class for sorting of mail messages + */ +public final class MailSorter { + /** + * No instances + */ + private MailSorter() { + } + + /** + * Sort the messages. This emulates sorting the messages on the server if the server doesn't have the sorting + * capability. See RFC 5256 + * Does not support complex sorting like in the RFC (with Base Subject or other similar stuff), just simple + * comparisons. + * + * @param messages Messages to sort. Are sorted in place + * @param sortTerm Sort term + */ + public static void sortMessages(Message[] messages, final SortTerm[] sortTerm) { + final List<SortTermWithDescending> sortTermsWithDescending = getSortTermsWithDescending(sortTerm); + sortMessages(messages, sortTermsWithDescending); + } + + /** + * Compute the potentially descendingd sort terms from the input list + * + * @param sortTerm Input list + * @return Sort terms list including if the respective sort should be sorted in descending order + */ + private static List<SortTermWithDescending> getSortTermsWithDescending(SortTerm[] sortTerm) { + // List of reversable sort terms. If the boolean is true the respective sort term is descendingd + final List<SortTermWithDescending> sortTermsWithDescending = new ArrayList<>(sortTerm.length); + // Descending next item in input because the last item was a "descending" + boolean descendingNext = false; + for (SortTerm term : sortTerm) { + if (term.equals(SortTerm.REVERSE)) { + if (descendingNext) { + throw new IllegalArgumentException("Double reverse in sort term is not allowed"); + } + descendingNext = true; + } else { + sortTermsWithDescending.add(new SortTermWithDescending(term, descendingNext)); + descendingNext = false; + } + } + return sortTermsWithDescending; + } + + /** + * Sort messages using the list of properties + * + * @param messages Messages to sort. Are sorted in place + * @param sortTermsWithDescending Sort terms list including if the respective sort should be sorted in descending order + */ + private static void sortMessages(Message[] messages, final List<SortTermWithDescending> sortTermsWithDescending) { + Arrays.sort(messages, new Comparator<Message>() { + @Override + public int compare(Message m1, Message m2) { + try { + for (SortTermWithDescending reversableTerm : sortTermsWithDescending) { + int comparison = compareMessageProperty(m1, m2, reversableTerm.getTerm()); + // Descending + if (reversableTerm.isDescending()) { + comparison = -comparison; + } + // Abort on first non-equal + if (comparison != 0) { + return comparison; + } + } + // Equal + return 0; + } catch (MessagingException e) { + throw new IllegalArgumentException(e); + } + } + }); + } + + /** + * Compare the value of the property of the two messages. + * + * @param msg1 Message 1 + * @param msg2 Message 2 + * @param property Property to compare + * @return msg1.property.compareTo(msg2.property) + * @throws javax.mail.MessagingException If message data could not be read. + */ + private static int compareMessageProperty(Message msg1, Message msg2, SortTerm property) throws MessagingException { + if (property.equals(SortTerm.TO)) { + InternetAddress addr1 = (InternetAddress) msg1.getRecipients(Message.RecipientType.TO)[0]; + InternetAddress addr2 = (InternetAddress) msg2.getRecipients(Message.RecipientType.TO)[0]; + return addr1.getAddress().compareTo(addr2.getAddress()); + } else if (property.equals(SortTerm.CC)) { + InternetAddress addr1 = (InternetAddress) msg1.getRecipients(Message.RecipientType.CC)[0]; + InternetAddress addr2 = (InternetAddress) msg2.getRecipients(Message.RecipientType.CC)[0]; + return addr1.getAddress().compareTo(addr2.getAddress()); + } else if (property.equals(SortTerm.FROM)) { + InternetAddress addr1 = (InternetAddress) msg1.getFrom()[0]; + InternetAddress addr2 = (InternetAddress) msg2.getFrom()[0]; + return addr1.getAddress().compareTo(addr2.getAddress()); + } else if (property.equals(SortTerm.ARRIVAL)) { + Date arr1 = msg1.getReceivedDate(); + Date arr2 = msg2.getReceivedDate(); + return arr1.compareTo(arr2); + } else if (property.equals(SortTerm.DATE)) { + Date sent1 = msg1.getSentDate(); + Date sent2 = msg2.getSentDate(); + return sent1.compareTo(sent2); + } else if (property.equals(SortTerm.SIZE)) { + int size1 = msg1.getSize(); + int size2 = msg2.getSize(); + return Integer.compare(size1, size2); + } else if (property.equals(SortTerm.SUBJECT)) { + String sub1 = msg1.getSubject(); + String sub2 = msg2.getSubject(); + return sub1.compareTo(sub2); + } + throw new IllegalArgumentException(String.format("Unknown sort term: %s", property.toString())); + } + + /** + * A sort term with a bit indicating if sorting should be descending for this term + */ + private static final class SortTermWithDescending { + private SortTerm term; + private boolean descending; + + private SortTermWithDescending(SortTerm term, boolean descending) { + this.term = term; + this.descending = descending; + } + + /** + * @return Actual search term + */ + public SortTerm getTerm() { + return term; + } + + /** + * @return true if sorting should be descending, false if it should be ascending + */ + public boolean isDescending() { + return descending; + } + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java ---------------------------------------------------------------------- diff --git a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java new file mode 100644 index 0000000..40fb2f5 --- /dev/null +++ b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSortTermTest.java @@ -0,0 +1,121 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.mail; + +import com.sun.mail.imap.SortTerm; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.JndiRegistry; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; +import org.jvnet.mock_javamail.Mailbox; + +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.Store; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.search.SearchTerm; + +import java.util.Date; + +import static org.apache.camel.component.mail.SearchTermBuilder.Op; + +/** + * This is a test that checks integration of the sort term in camel. The actual sorting logic is tested in the + * SortUtilTest. + */ +public class MailSortTermTest extends CamelTestSupport { + + @Override + public void setUp() throws Exception { + prepareMailbox(); + super.setUp(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndi = super.createRegistry(); + jndi.bind("sortAscendingDate", new SortTerm[]{SortTerm.DATE}); + jndi.bind("sortDescendingDate", new SortTerm[]{SortTerm.REVERSE, SortTerm.DATE}); + jndi.bind("searchTerm", new SearchTermBuilder().subject("Camel").build()); + return jndi; + } + + @Test + public void testSortTerm() throws Exception { + Mailbox mailbox = Mailbox.get("bill@localhost"); + assertEquals(3, mailbox.size()); + + // This one has search term *not* set + MockEndpoint mockAsc = getMockEndpoint("mock:resultAscending"); + mockAsc.expectedBodiesReceived("Earlier date", "Later date"); + + // This one has search term set + MockEndpoint mockDesc = getMockEndpoint("mock:resultDescending"); + mockDesc.expectedBodiesReceived("Even later date", "Later date", "Earlier date"); + + // This one has search term set + MockEndpoint mockDescImap = getMockEndpoint("mock:resultDescendingImap"); + mockDescImap.expectedBodiesReceived("Even later date", "Later date", "Earlier date"); + + assertMockEndpointsSatisfied(); + } + + private void prepareMailbox() throws Exception { + // connect to mailbox + Mailbox.clearAll(); + JavaMailSender sender = new DefaultJavaMailSender(); + Store store = sender.getSession().getStore("pop3"); + store.connect("localhost", 25, "bill", "secret"); + Folder folder = store.getFolder("INBOX"); + folder.open(Folder.READ_WRITE); + folder.expunge(); + + // inserts 3 messages, one with earlier, one with later sent date and one with invalid subject (not returned in search) + Message[] messages = new Message[3]; + messages[0] = new MimeMessage(sender.getSession()); + messages[0].setText("Earlier date"); + messages[0].setSentDate(new Date(10000)); + messages[0].setSubject("Camel"); + + messages[1] = new MimeMessage(sender.getSession()); + messages[1].setText("Later date"); + messages[1].setSentDate(new Date(20000)); + messages[1].setSubject("Camel"); + + messages[2] = new MimeMessage(sender.getSession()); + messages[2].setText("Even later date"); + messages[2].setSentDate(new Date(30000)); + messages[2].setSubject("Invalid"); + + folder.appendMessages(messages); + folder.close(true); + } + + protected RouteBuilder createRouteBuilder() throws Exception { + // Two tests also in conjunction with search since mail consumer does sort and search together + return new RouteBuilder() { + public void configure() throws Exception { + from("pop3://bill@localhost?password=secret&searchTerm=#searchTerm&sortTerm=#sortAscendingDate").to("mock:resultAscending"); + from("pop3://bill@localhost?password=secret&sortTerm=#sortDescendingDate").to("mock:resultDescending"); + from("imap://bill@localhost?password=secret&sortTerm=#sortDescendingDate").to("mock:resultDescendingImap"); + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/1149fae7/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java ---------------------------------------------------------------------- diff --git a/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java new file mode 100644 index 0000000..a2a0b52 --- /dev/null +++ b/components/camel-mail/src/test/java/org/apache/camel/component/mail/MailSorterTest.java @@ -0,0 +1,154 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.mail; + +import com.sun.mail.imap.SortTerm; +import org.apache.camel.component.mail.MailSorter; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.mail.Address; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; +import java.util.Date; + +import static org.mockito.Mockito.when; + + +/** + * Tests mail sort util + */ +public class MailSorterTest extends CamelTestSupport { + private final static Message[] messages = new Message[3]; + private final static Message tieBreaker; + + /** + * All possible sort terms + */ + private final static SortTerm[] possibleTerms = new SortTerm[]{ + SortTerm.ARRIVAL, + SortTerm.CC, + SortTerm.DATE, + SortTerm.FROM, + SortTerm.SIZE, + SortTerm.TO, + SortTerm.SUBJECT + }; + + static { + try { + messages[0] = createMessage("to1", "cc1", "from1", new Date(1), new Date(1001), 1, "subject1"); + messages[1] = createMessage("to2", "cc2", "from2", new Date(2), new Date(1002), 2, "subject2"); + messages[2] = createMessage("to3", "cc3", "from3", new Date(3), new Date(1003), 3, "subject3"); + // Message that creates a tie on all fields except for one + tieBreaker = createMessage("to3", "cc3", "from3", new Date(3), new Date(1003), 3, "subject0TieBreaker"); + } catch (MessagingException e) { + // Rethrow as unchecked. Can not occur anyways + throw new IllegalStateException(e); + } + } + + /** + * Create a new message with the specified data + */ + private static Message createMessage(String to, String cc, String from, Date received, Date sent, int size, String subject) + throws MessagingException { + final Message msg = Mockito.mock(Message.class); + when(msg.getFrom()).thenReturn(new Address[]{new InternetAddress(from)}); + when(msg.getRecipients(Message.RecipientType.TO)).thenReturn(new Address[]{new InternetAddress(to)}); + when(msg.getRecipients(Message.RecipientType.CC)).thenReturn(new Address[]{new InternetAddress(cc)}); + when(msg.getSentDate()).thenReturn(sent); + when(msg.getReceivedDate()).thenReturn(received); + when(msg.getSize()).thenReturn(size); + when(msg.getSubject()).thenReturn(subject); + return msg; + } + + @Test + public void testSortMessages() throws Exception { + Message[] expected = new Message[]{messages[0], messages[1], messages[2]}; + + // Sort using all the terms. Message order should be the same no matter what term is used + for (SortTerm term : possibleTerms) { + Message[] actual = messages.clone(); + MailSorter.sortMessages(actual, new SortTerm[]{term}); + try { + assertArrayEquals(actual, expected); + } catch (Exception ex) { + throw new Exception("Term: " + term.toString(), ex); + } + } + } + + @Test + public void testSortMessagesReverse() throws Exception { + Message[] expected = new Message[]{messages[2], messages[1], messages[0]}; + + // Sort using all the terms. Message order should be the same no matter what term is used + for (SortTerm term : possibleTerms) { + Message[] actual = messages.clone(); + MailSorter.sortMessages(actual, new SortTerm[]{SortTerm.REVERSE, term}); + try { + assertArrayEquals(actual, expected); + } catch (AssertionError ex) { + throw new AssertionError("Term: " + term.toString(), ex); + } + } + } + + @Test + public void testSortMessagesMulti() throws Exception { + Message[] expected = new Message[]{messages[0], messages[1], messages[2]}; + + // Sort using all the terms. Message order should be the same no matter what term is used. The second term + // should be ignored since it is already the decider. + for (SortTerm term1 : possibleTerms) { + for (SortTerm term2 : possibleTerms) { + Message[] actual = messages.clone(); + MailSorter.sortMessages(actual, new SortTerm[]{term1, SortTerm.REVERSE, term2}); + try { + assertArrayEquals(actual, expected); + } catch (AssertionError ex) { + throw new AssertionError(String.format("Terms: %s, %s", term1.toString(), term2.toString()), ex); + } + } + + } + } + + @Test + public void testSortMessagesWithTie() throws Exception { + Message[] given = new Message[]{messages[2], tieBreaker}; + + // Sort according to the whole list. Only the last element breaks the tie + Message[] actual1 = given.clone(); + MailSorter.sortMessages(actual1, possibleTerms); + assertArrayEquals(actual1, new Message[]{tieBreaker, messages[2]}); + + // now reverse the last element (the tie breaker) + SortTerm[] reversed = new SortTerm[possibleTerms.length + 1]; + System.arraycopy(possibleTerms, 0, reversed, 0, possibleTerms.length - 1); + reversed[reversed.length - 2] = SortTerm.REVERSE; + reversed[reversed.length - 1] = possibleTerms[possibleTerms.length - 1]; + // And check again + Message[] actual2 = given.clone(); + MailSorter.sortMessages(actual2, reversed); + assertArrayEquals(actual2, new Message[]{messages[2], tieBreaker}); + } +} \ No newline at end of file