# Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Helpers for tests for implementations of ``IStateChange``.
"""

__all__ = [
    "make_comparison_tests", "make_istatechange_tests",
]

from zope.interface.verify import verifyObject

from zope.interface import implementer

from eliot import Logger, start_action

from pyrsistent import PRecord, field
from characteristic import attributes

from twisted.trial.unittest import SynchronousTestCase
from twisted.internet.defer import succeed

from .. import IStateChange


def make_comparison_tests(klass, kwargs1, kwargs2):
    """
    Create tests to verify a class provides standard ``==`` and ``!=``
    behavior.

    :param klass: Class that implements ``IStateChange``.
    :param kwargs1: Keyword arguments to ``klass``.
    :param kwargs2: Keyword arguments to ``klass`` that create different change
        than ``kwargs1``.

    :return: ``SynchronousTestCase`` subclass named
        ``<klassname>ComparisonTests``.
    """
    class Tests(SynchronousTestCase):
        def test_equality(self):
            """
            Instances with the same arguments are equal.
            """
            self.assertTrue(klass(**kwargs1) == klass(**kwargs1))
            self.assertFalse(klass(**kwargs1) == klass(**kwargs2))

        def test_notequality(self):
            """
            Instance with different arguments are not equal.
            """
            self.assertTrue(klass(**kwargs1) != klass(**kwargs2))
            self.assertFalse(klass(**kwargs1) != klass(**kwargs1))
    Tests.__name__ = klass.__name__ + "ComparisonTests"
    return Tests


def make_istatechange_tests(klass, kwargs1, kwargs2):
    """
    Create tests to verify a class provides ``IStateChange``.

    :param klass: Class that implements ``IStateChange``.
    :param kwargs1: Keyword arguments to ``klass``.
    :param kwargs2: Keyword arguments to ``klass`` that create different
        change than ``kwargs1``.

    :return: ``SynchronousTestCase`` subclass named
        ``<klassname>IStateChangeTests``.
    """
    class Tests(make_comparison_tests(klass, kwargs1, kwargs2)):
        def test_interface(self):
            """
            The class implements ``IStateChange``.
            """
            self.assertTrue(verifyObject(IStateChange, klass(**kwargs1)))
    Tests.__name__ = klass.__name__ + "IStateChangeTests"
    return Tests


@implementer(IStateChange)
class DummyStateChange(PRecord):
    """
    A do-nothing implementation of ``IStateChange``.
    """
    value = field()

    @property
    def eliot_action(self):
        return start_action(Logger(), u"flocker:tests:dummy_state_change")

    def run(self, deployer):
        return succeed(None)


@implementer(IStateChange)
@attributes(["value"])
class RunSpyStateChange(object):
    """
    An implementation of ``IStateChange`` that records its runs.
    """
    @property
    def eliot_action(self):
        return start_action(Logger(), u"flocker:tests:run_spy_state_change")

    def run(self, deployer):
        self.value += 1
        return succeed(None)

@@ -775,32 +775,125 @@
 ts to %60%60klass%60%60.
+  Either a %60%60dict%60%60 or a%0A        no-argument callable which returns keyword arguments to use.
 %0A    :param kwar
@@ -974,32 +974,125 @@
 han %60%60kwargs1%60%60.
+  Either a %60%60dict%60%60 or a no-argument callable which%0A        returns keyword arguments to use.
 %0A%0A    :return: %60
@@ -1129,32 +1129,37 @@
 s named%0A        
+     
 %60%60%3Cklassname%3ECom
@@ -1174,32 +1174,164 @@
 ests%60%60.%0A    %22%22%22%0A
+    def instance(kwargs):%0A        if isinstance(kwargs, dict):%0A            return klass(**kwargs)%0A        return klass(**kwargs())%0A%0A
     class Tests(
@@ -1498,32 +1498,33 @@
 .assertTrue(
-klass(**
+instance(
 kwargs1) == 
@@ -1519,32 +1519,33 @@
 kwargs1) == 
-klass(**
+instance(
 kwargs1))%0A  
@@ -1567,32 +1567,33 @@
 assertFalse(
-klass(**
+instance(
 kwargs1) == 
@@ -1592,24 +1592,25 @@
 gs1) == 
-klass(**
+instance(
 kwargs2)
@@ -1769,24 +1769,25 @@
 ertTrue(
-klass(**
+instance(
 kwargs1)
@@ -1786,32 +1786,33 @@
 kwargs1) != 
-klass(**
+instance(
 kwargs2))%0A  
@@ -1838,24 +1838,25 @@
 rtFalse(
-klass(**
+instance(
 kwargs1)
@@ -1859,24 +1859,25 @@
 gs1) != 
-klass(**
+instance(
 kwargs1)

Make `make_comparison_tests` a little more powerful.