from unittest import TestCase, TestSuite, main, makeSuite

import moppy.searchreplace as sr
import moppy.mop as mop
import testfixtures as tf

def times3(someNum):
    return someNum * 3

class TestFixturesObjectDomain(mop.ObjectDomain):
    def __init__(self):
        mop.ObjectDomain.__init__(self)
        self.modules.add(tf)

class MixinClass1(object):
    def sayHi(self):
        print "hello from MixinClass1!"

class Test(TestCase):
    
        
    def testMixin1(self):
        selection = mop.SelectionCriteria()
        selection.AND(sr.ClassNameCriteria(r'\bmoppy.tests.testfixtures.A\b'))
        selection = mop.Selection(selection)
        mso = mop.MetaStructuralObject()
        mso.addSelection(selection)
        #as = mop.MetaObjectSystem()
        as = mop.MetaObjectSystem(TestFixturesObjectDomain())
        as.addMetaStructuralObject(mso)
        mso.addMixin(MixinClass1)
        #should just be one
        assert(len(selection.matches) == 1)
        #method sayHi should now be in tf.A class objects
        #if not, we'll get an exception
        a = tf.A()
        self.assertNotEquals(0,hasattr(a, 'sayHi'))
        a.sayHi()
        #now remove it
        as.removeMetaStructuralObject(mso)
        self.assertEquals(0,hasattr(a, 'sayHi'))
        
    def testMetaObjectSystem1(self):
        selection = mop.SelectionCriteria()
        selection.AND(sr.ClassMemberCriteria("moppy.tests.testfixtures.A"))
        selection.AND(sr.MethodNameCriteria(r'\binstMeth\b'))
        selection = mop.Selection(selection)
        mco = mop.MetaCallableObject()
        mco.addSelection(selection)
        #as = mop.MetaObjectSystem()
        as = mop.MetaObjectSystem(TestFixturesObjectDomain())
        as.addMetaCallableObject(mco)
        #should just be one
        assert(len(selection.matches) == 1)
        as.removeMetaCallableObject(mco)
        
    def testMetaObjectSystem2(self):
        selection = mop.SelectionCriteria()
        selection.AND(sr.ClassMemberCriteria("moppy.tests.testfixtures.A"))
        selection.AND(sr.MethodNameCriteria("instMeth"))
        selection = mop.Selection(selection)
        mco = mop.MetaCallableObject()
        mco.addSelection(selection)
        #as = mop.MetaObjectSystem()
        as = mop.MetaObjectSystem(TestFixturesObjectDomain())
        as.addMetaCallableObject(mco)
        #should be two. Note difference between last
        #method name criteria. This one will match ANY
        #method in class testfixtures.A starting with 'instMeth'
        assert(len(selection.matches) == 2)
        as.removeMetaCallableObject(mco)
        
    def testMetaObjectSystem3(self):
        selection = mop.SelectionCriteria()
        selection.AND(sr.ClassMemberCriteria("moppy.tests.testfixtures.A"))
        selection.AND(sr.MethodNameCriteria(r'\btimes2\b'))
        selection = mop.Selection(selection)
        hook = tf.Times2Retval()
        mco = mop.MetaCallableObject()
        mco.addSelection(selection)
        as = mop.MetaObjectSystem(TestFixturesObjectDomain())
        #as = mop.MetaObjectSystem()
        as.addMetaCallableObject(mco)
        mco.addHook(hook)
        #should be 1
        assert(len(selection.matches) == 1)
        tfa = tf.A()
        retVal = tfa.times2(4)
        #the hook will double the return value (8),
        #so it should be 16
        assert(retVal == 16)
        mco.removeHook(hook)
        retVal = tfa.times2(4)
        assert(retVal == 8)
        as.removeMetaCallableObject(mco)
        
    def testMetaObjectSystem4(self):
        selection = mop.SelectionCriteria()
        selection.AND(sr.ModuleMemberCriteria("moppy.tests.testfixtures"))
        selection.AND(sr.FunctionNameCriteria(r'\btimes2\b'))
        selection = mop.Selection(selection)
        hook = tf.Times2Retval()
        mco = mop.MetaCallableObject()
        mco.addSelection(selection)
        as = mop.MetaObjectSystem(TestFixturesObjectDomain())
        #as = mop.MetaObjectSystem()
        as.addMetaCallableObject(mco)
        mco.addHook(hook)
        #should be 1
        assert(len(selection.matches) == 1)
        retVal = tf.times2(4)
        #the hook will double the return value (8),
        #so it should be 16
        assert(retVal == 16)

        mco.removeHook(hook)
        retVal = tf.times2(4)
        assert(retVal == 8)
        as.removeMetaCallableObject(mco)
        
    def testMetaObjectSystem5(self):
        selection = mop.SelectionCriteria()
        selection.AND(sr.ModuleMemberCriteria("moppy.tests.testfixtures"))
        selection.AND(sr.FunctionNameCriteria(r'\btimes2\b'))
        selection = mop.Selection(selection)
        hook = tf.Times2Retval()
        hook2 = tf.Times2Retval()
        mco = mop.MetaCallableObject()
        mco.addSelection(selection)
        as = mop.MetaObjectSystem(TestFixturesObjectDomain())
        #as = mop.MetaObjectSystem()
        as.addMetaCallableObject(mco)
        mco.addHook(hook)
        mco.addHook(hook2)
        #should be 1
        assert(len(selection.matches) == 1)
        retVal = tf.times2(4)
        #the hooks will twice double the return value (8),
        #so it should be 32
        assert(retVal == 32)
        
        #remove one hook, should have one more
        mco.removeHook(hook)
        retVal = tf.times2(4)
        assert(retVal == 16)

        mco.removeHook(hook2)
        retVal = tf.times2(4)
        assert(retVal == 8)
        as.removeMetaCallableObject(mco)



def testSuite():
    return makeSuite(Test)


if __name__=='__main__':
    main(defaultTest='testSuite')