Skip to content

Commit

Permalink
Provide tests for message flags, message age, and the Boolean Not ope…
Browse files Browse the repository at this point in the history
…rator.
  • Loading branch information
Thomi Richards committed Jul 5, 2015
1 parent f599b34 commit 32231f2
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 5 deletions.
2 changes: 0 additions & 2 deletions gmailfilter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@


98 changes: 98 additions & 0 deletions gmailfilter/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
"""

from datetime import (
datetime,
timedelta,
)
import operator
import unicodedata

import imapclient

from gmailfilter.messageutils import get_list_id


Expand Down Expand Up @@ -82,6 +88,17 @@ def match(self, message):
return any([t.match(message) for t in self._tests])


class Not(Test):

"""Invert the output of any single test."""

def __init__(self, test):
self._test = test

def match(self, message):
return not self._test.match(message)


class MatchesHeader(Test):

"""Check whether an email has a given header.
Expand Down Expand Up @@ -157,6 +174,87 @@ def match(self, message):
return get_list_id(message) == self._target_list


class HasFlag(Test):

"""Test for certain flags being set on a message.
This test can be given any string for a flag, although the following are
provided as a convenience:
HasFlag.ANSWERED
HasFlag.DELETED
HasFlag.DRAFT
HasFlag.FLAGGED
HasFlag.RECENT
HasFlag.SEEN
"""

ANSWERED = imapclient.ANSWERED
DELETED = imapclient.DELETED
DRAFT = imapclient.DRAFT
FLAGGED = imapclient.FLAGGED
RECENT = imapclient.RECENT
SEEN = imapclient.SEEN

def __init__(self, flag):
self.expected_flag = flag

def match(self, message):
return self.expected_flag in message.get_flags()


def IsAnswered():
return HasFlag(HasFlag.ANSWERED)


def IsDeleted():
return HasFlag(HasFlag.DELETED)


def IsDraft():
return HasFlag(HasFlag.DRAFT)


def IsFlagged():
return HasFlag(HasFlag.FLAGGED)


def IsRecent():
return HasFlag(HasFlag.RECENT)


def IsRead():
return HasFlag(HasFlag.SEEN)


# Provide a function with the same name as the imap flag name, even though
# it's bad english
IsSeen = IsRead


# TODO: Need a way to provide alternative values for the datetime.now() call
# here, otherwise it's hard to test.
class MessageOlderThan(Test):

"""Test that a message is older than a certain age.
Age is specified as a python datetime.timedelta object. For example:
>>> from datetime import timedelta
>>> MessageOlderThan(timedelta(days=12))
"""

def __init__(self, age):
if not isinstance(age, timedelta):
raise TypeError("'age' must be a datetime.timedelta object.")
self._age = age

def match(self, message):
return message.get_date() + self._age < datetime.now()


# def caseless_comparison(str1, str2, op):
# """Perform probably-correct caseless comparison between two strings.

Expand Down
22 changes: 19 additions & 3 deletions gmailfilter/tests/factory.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
"""Test factory for gmailfilter tests."""

import datetime

from gmailfilter._message import Message


class TestFactoryMixin(object):

"""A mixin class that generates test fake values."""

def get_email_message(self, headers=None, subject='Test Subject'):
def get_email_message(self, headers=None, subject='Test Subject',
flags=None, date=None):
"""Get an email message.
:param headers: If set, must be a dict or 2-tuple iteratble of key/value
pairs that will be set as the email message header values.
:param headers: If set, must be a dict or 2-tuple iteratble of
key/value pairs that will be set as the email message header
values.
"""
message = FakeMessage()
if headers:
message.headers = dict(headers)
message.headers['Subject'] = subject
if flags:
message.flags = flags
if date is not None:
message.date = date
return message


class FakeMessage(Message):

def __init__(self):
self.headers = {}
self.flags = ()
self.date = datetime.datetime.utcnow()

def get_headers(self):
return self.headers

def subject(self):
return self.get_headers()['Subject']

def get_flags(self):
return self.flags

def get_date(self):
return self.date
38 changes: 38 additions & 0 deletions gmailfilter/tests/test_tests.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import datetime
from unittest import TestCase

import imapclient

from gmailfilter.tests.factory import TestFactoryMixin
from gmailfilter.test import (
Test,
And,
Or,
Not,
MatchesHeader,
SubjectContains,
ListId,
HasFlag,
)
from gmailfilter._message import Message

Expand Down Expand Up @@ -69,6 +73,18 @@ def test_or_boolean_table(self):
Or(*operands).match(self.get_email_message())
)

def test_not_with_passing_test(self):
self.assertEqual(
False,
Not(AlwaysPassingTest()).match(self.get_email_message())
)

def test_not_with_failing_test(self):
self.assertEqual(
True,
Not(AlwaysFailingTest()).match(self.get_email_message())
)


class TestMatchesHeaderTests(TestCase, TestFactoryMixin):

Expand Down Expand Up @@ -139,3 +155,25 @@ def test_list_id_mismatch(self):
def test_non_list_message(self):
message = self.get_email_message()
self.assertFalse(ListId('something.else').match(message))


class FlagTests(TestCase, TestFactoryMixin):

def test_all_flag_matchers(self):
flags = (
imapclient.ANSWERED,
imapclient.DELETED,
imapclient.DRAFT,
imapclient.FLAGGED,
imapclient.RECENT,
imapclient.SEEN,
)
for flag in flags:
message = self.get_email_message(flags=(flag,))
self.assertTrue(HasFlag(flag).match(message))


class MessageAgeTests(TestCase, TestFactoryMixin):

def test_newer_message(self):
now = datetime.datetime(2015, 7, 5)

0 comments on commit 32231f2

Please sign in to comment.