'''
Testing for revert - for IOCs and HLA apps
Revert relies on hist, runReleaseScript and upgradeRelease so some of the testing of the subfunctions is done there.
This tests the revert itself.
'''
from __future__ import print_function

import os
import unittest
import shutil
import subprocess
import json
import multi_facility_process_helper
import multi_facility_process_helper_client
import globals
import toolconfig
import testUtils
import pushRelease
import upgradeRelease
import showHistory
import revertRelease
from six import iteritems

startingDir = os.path.realpath(os.curdir)


class TestIOCRevert(unittest.TestCase):
 
    testDataFolder = "/tmp"
 
    def setUp(self):
        self.testDataFolder = testUtils.getTestFolder()
        testUtils.createStdIOCFolders()
  
         
    def tearDown(self):
        shutil.rmtree(os.path.join(self.testDataFolder, testUtils.iocTestFolder))
        shutil.rmtree(os.path.join(self.testDataFolder, testUtils.IOCWorkspaceFolder))
 
    def getIOCVersionsInAllFacilities(self):
        '''Get the versions of all IOCs in all facilities as a dict
        The  master release for each facility is added as a facility_name_MASTER key
        IOCs themselves are tuples - version and whether they point to the master release or not'''
        iocVersions = {}
        for facility in toolconfig.getListOfFacilities():
            arg = {}
            arg['name'] = 'testPkg'
            arg['releaseFolder'] = os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, "iocTop")
            arg['linkFolder'] = os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, "iocCommon")
            ret = multi_facility_process_helper.determineIOCVersions(arg)
            iocVersions[facility + "_MASTER"] = (ret['master'], True)
            for ioc in ret['iocs']:
                iocVersions[ioc['ioc']] = (ioc['release'], ioc['isMaster'])
        return iocVersions
 
    def makeSureIOCVersionsAreTheSame(self, iocVersionsBefore, iocVersionsAfter, upgradedIOCs):
        '''Make sure that the versions before and after are the same with the exceptions of the IOCs in the upgradedIOCs list.'''
        for (iocName, versionMasterTuple) in iteritems(iocVersionsBefore):
            self.assertTrue(iocName in iocVersionsAfter)
            if iocName not in upgradedIOCs:
                if globals.verbose:
                    print('{0} {1} {2}'.format(iocName, versionMasterTuple, iocVersionsAfter[iocName]))
                self.assertTrue(versionMasterTuple == iocVersionsAfter[iocName], msg="Failure in " + iocName +  str(versionMasterTuple) +  str(iocVersionsAfter[iocName]))
        for (iocName, versionMasterTuple) in iteritems(iocVersionsAfter):
            self.assertTrue(iocName in iocVersionsBefore)
            if iocName not in upgradedIOCs:
                if globals.verbose:
                    print('{0} {1} {2}'.format(iocName, versionMasterTuple, iocVersionsBefore[iocName]))
                self.assertTrue(versionMasterTuple == iocVersionsBefore[iocName], msg="Failure in " + iocName +  str(versionMasterTuple) +  str(iocVersionsAfter[iocName]))

    def createPushAndUpgrade(self, testPushVersionName, testupgradeargs):
        ''' Creates, pushes and upgrade the specified IOCs to this release'''
        testPushPkgName = "testPkg"
        testUtils.createNewIOCRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.IOCWorkspaceFolder, testPushPkgName, testPushVersionName))
        pushargs = []
        if globals.verbose:
            pushargs.append('-v')
        pushRelease.execute(pushargs)

        upgradeargs = []
        if globals.verbose:
            upgradeargs.append('-v')
        upgradeargs.extend(testupgradeargs)
        upgradeRelease.execute(upgradeargs)
        
    def revert(self, testrevertargs):
        revertargs = []
        if globals.verbose:
            revertargs.append('-v')
        revertargs.extend(testrevertargs)
        revertRelease.execute(revertargs)
        testUtils.cd(startingDir)

    def getHist(self):
        ''' Gets the history for this package'''
        ret = {}
        for facility in toolconfig.getListOfFacilities():
            arg = {}
            arg['name'] = 'testPkg'
            arg['type'] = 'SIOC'
            arg['releaseFolder'] = toolconfig.getConfigForFacility(facility)['SIOC']['releaseFolder']
            ret[facility] = multi_facility_process_helper.hist(arg)
        return ret


    def testOneIOCInOneFacility(self):
        '''Test upgrading one IOC in one facility. We also test that the remaining IOCs are unaffected'''
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V9"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001', '--facility', 'test1', testPushVersionName])
        
        iocVersionsBefore = self.getIOCVersionsInAllFacilities()

        testPushVersionName = "testPush_V10"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001', '--facility', 'test1', testPushVersionName])

        self.revert(['--ioc', 'sioc-test1-001', '--facility', 'test1'])
        iocVersionsAfter = self.getIOCVersionsInAllFacilities()

        self.makeSureIOCVersionsAreTheSame(iocVersionsBefore, iocVersionsAfter, [])
 
        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('sioc-test1-001' in hist['test1'])
        self.assertTrue(hist['test1']['sioc-test1-001'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-001'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][2]['release'] == 'testPush_V9')
        

    def testUpgradeTwoIOCsButRevertOnlyOne(self):
        '''Test upgrading two IOCs in one facility but revert only one'''
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V9"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001,sioc-test1-002', '--facility', 'test1', testPushVersionName])
        
        iocVersionsBefore = self.getIOCVersionsInAllFacilities()

        testPushVersionName = "testPush_V10"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001,sioc-test1-002', '--facility', 'test1', testPushVersionName])

        self.revert(['--ioc', 'sioc-test1-001', '--facility', 'test1'])
        iocVersionsAfter = self.getIOCVersionsInAllFacilities()

        self.makeSureIOCVersionsAreTheSame(iocVersionsBefore, iocVersionsAfter, ['sioc-test1-002'])
 
        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('sioc-test1-001' in hist['test1'])
        self.assertTrue('sioc-test1-002' in hist['test1'])
        self.assertTrue(hist['test1']['sioc-test1-001'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-001'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-002'][0]['release'] == 'testPush_V10')


    def testRevertTwoIOCsToDifferentVersions(self):
        '''Test reverting two IOCs in one facility but they each go back to different versions'''
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V9"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001,sioc-test1-002', '--facility', 'test1',
                                                        testPushVersionName])

        iocVersionsBefore = self.getIOCVersionsInAllFacilities()

        testPushVersionName = "testPush_V10"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001,sioc-test1-002', '--facility', 'test1',
                                                        testPushVersionName])

        testPushVersionName = "testPush_V11"
        self.createPushAndUpgrade(testPushVersionName,
                                  ['--ioc', 'sioc-test1-001', '--facility', 'test1', testPushVersionName])

        self.revert(['--ioc', 'sioc-test1-001,sioc-test1-002', '--facility', 'test1'])
        iocVersionsAfter = self.getIOCVersionsInAllFacilities()

        self.makeSureIOCVersionsAreTheSame(iocVersionsBefore, iocVersionsAfter, ['sioc-test1-001', 'sioc-test1-002'])

        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('sioc-test1-001' in hist['test1'])
        self.assertTrue('sioc-test1-002' in hist['test1'])
        self.assertTrue(hist['test1']['sioc-test1-001'][0]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][1]['release'] == 'testPush_V11')
        self.assertTrue(hist['test1']['sioc-test1-001'][2]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][3]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-002'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-002'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-002'][2]['release'] == 'testPush_V9')

        # Add a display hist here
        showHistory.execute(['--name', 'testPkg', '--type', 'SIOC'])


    def testRevertManyIOCsToDifferentVersions(self):
        '''Test reverting many IOCs in one facility but they each go back to different versions'''
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V9"
        self.createPushAndUpgrade(testPushVersionName,
                                  ['--ioc', 'sioc-test1-*', '--facility', 'test1', testPushVersionName])

        iocVersionsBefore = self.getIOCVersionsInAllFacilities()

        testPushVersionName = "testPush_V10"
        self.createPushAndUpgrade(testPushVersionName,
                                  ['--ioc', 'sioc-test1-*', '--facility', 'test1', testPushVersionName])

        testPushVersionName = "testPush_V11"
        self.createPushAndUpgrade(testPushVersionName,
                                  ['--ioc', 'sioc-test1-001', '--facility', 'test1', testPushVersionName])

        self.revert(['--ioc', 'sioc-test1-*', '--facility', 'test1'])
        iocVersionsAfter = self.getIOCVersionsInAllFacilities()

        self.makeSureIOCVersionsAreTheSame(iocVersionsBefore, iocVersionsAfter,
                                           ['sioc-test1-001', 'sioc-test1-002', 'sioc-test1-003', 'sioc-test1-004', 'sioc-test1-006'])

        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('sioc-test1-001' in hist['test1'])
        self.assertTrue('sioc-test1-002' in hist['test1'])
        self.assertTrue('sioc-test1-003' in hist['test1'])
        self.assertTrue('sioc-test1-004' in hist['test1'])
        self.assertTrue('sioc-test1-006' in hist['test1'])
        self.assertTrue(hist['test1']['sioc-test1-001'][0]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][1]['release'] == 'testPush_V11')
        self.assertTrue(hist['test1']['sioc-test1-001'][2]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][3]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-002'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-002'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-002'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-003'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-003'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-003'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-004'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-004'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-004'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-006'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-006'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-006'][2]['release'] == 'testPush_V9')

        # Add a display hist here
        showHistory.execute(['--name', 'testPkg', '--type', 'SIOC'])

        # We expect to get an exception when passing in IOCs with a wildcard but no facility
        self.assertRaises(Exception, self.revert, ['--ioc', 'sioc-test1-*'])

    def testRevertManyIOCsWithTwoPatternsToDifferentVersions(self):
        '''Test reverting many IOCs in one facility with two patterns but they each go back to different versions'''
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V9"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-*', '--facility', 'test1', testPushVersionName])

        iocVersionsBefore = self.getIOCVersionsInAllFacilities()

        testPushVersionName = "testPush_V10"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-*,sioc-*-004', '--facility', 'test1', testPushVersionName])

        testPushVersionName = "testPush_V11"
        self.createPushAndUpgrade(testPushVersionName, ['--ioc', 'sioc-test1-001', '--facility', 'test1', testPushVersionName])

        self.revert(['--ioc', 'sioc-test1-*,sioc-*-004', '--facility', 'test1'])
        iocVersionsAfter = self.getIOCVersionsInAllFacilities()

        self.makeSureIOCVersionsAreTheSame(iocVersionsBefore, iocVersionsAfter,
                                           ['sioc-test1-001', 'sioc-test1-002', 'sioc-test1-003', 'sioc-test1-004', 'sioc-test1-006'])

        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('sioc-test1-001' in hist['test1'])
        self.assertTrue('sioc-test1-002' in hist['test1'])
        self.assertTrue('sioc-test1-003' in hist['test1'])
        self.assertTrue('sioc-test1-004' in hist['test1'])
        self.assertTrue('sioc-test1-006' in hist['test1'])
        self.assertTrue(hist['test1']['sioc-test1-001'][0]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][1]['release'] == 'testPush_V11')
        self.assertTrue(hist['test1']['sioc-test1-001'][2]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-001'][3]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-002'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-002'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-002'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-003'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-003'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-003'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-004'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-004'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-004'][2]['release'] == 'testPush_V9')

        self.assertTrue(hist['test1']['sioc-test1-006'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['sioc-test1-006'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['sioc-test1-006'][2]['release'] == 'testPush_V9')

        # Add a display hist here
        showHistory.execute(['--name', 'testPkg', '--type', 'SIOC'])


class TestHLARevert(unittest.TestCase):
    testDataFolder = "/tmp"
    startingDir = os.path.realpath(os.curdir)

    def setUp(self):
        self.testDataFolder = testUtils.getTestFolder()
        testUtils.createStdHLAFolders()

    def tearDown(self):
        shutil.rmtree(os.path.join(self.testDataFolder, testUtils.HLATestFolder))
        shutil.rmtree(os.path.join(self.testDataFolder, testUtils.HLAWorkspaceFolder))

    def getHLAVersionsInAllFacilities(self):
        '''Get the versions of the HLA applications in all facilities as a dict'''
        HLAVersions = {}
        for facility in toolconfig.getListOfFacilities():
            arg = {}
            arg['name'] = 'testPkg'
            arg['releaseFolder'] = os.path.join(self.testDataFolder, testUtils.HLATestFolder, facility, "physicsTop",
                                                "release")
            arg['linkFolder'] = os.path.join(self.testDataFolder, testUtils.HLATestFolder, facility, "physicsTop")
            ret = multi_facility_process_helper.determineHLAVersion(arg)
            HLAVersions[facility] = ret
        return HLAVersions

    def getHist(self):
        ''' Gets the history for this package'''
        ret = {}
        for facility in toolconfig.getListOfFacilities():
            arg = {}
            arg['name'] = 'testPkg'
            arg['type'] = 'HLA'
            arg['releaseFolder'] = toolconfig.getConfigForFacility(facility)['HLA']['releaseFolder']
            ret[facility] = multi_facility_process_helper.hist(arg)
        return ret

    def createPushAndUpgrade(self, testPushVersionName, testupgradeargs):
        '''Create, push and upgrade a HLA application'''
        testPushPkgName = "testPkg"
        testUtils.createNewHLARelease(testPushPkgName, testPushVersionName)
        testUtils.cd(
            os.path.join(self.testDataFolder, testUtils.HLAWorkspaceFolder, testPushPkgName, testPushVersionName))
        pushargs = []
        if globals.verbose:
            pushargs.append('-v')
        pushRelease.execute(pushargs)

        upgradeargs = []
        if globals.verbose:
            upgradeargs.append('-v')
        upgradeargs.extend(testupgradeargs)
        upgradeRelease.execute(upgradeargs)

    def revert(self, testrevertargs):
        revertargs = []
        if globals.verbose:
            revertargs.append('-v')
        revertargs.extend(testrevertargs)
        revertRelease.execute(revertargs)
        testUtils.cd(startingDir)

    def testHLAInOneFacility(self):
        '''Test reverting HLA in one facility '''
        self.createPushAndUpgrade('testPush_V9', ['--facility', 'test1', 'testPush_V9'])

        HLAVersionsBefore = self.getHLAVersionsInAllFacilities()

        self.createPushAndUpgrade('testPush_V10', ['--facility', 'test1', 'testPush_V10'])
        self.revert(['--facility', 'test1'])

        HLAVersionsAfter = self.getHLAVersionsInAllFacilities()

        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('MASTER' in hist['test1'])
        self.assertTrue(hist['test1']['MASTER'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['MASTER'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['MASTER'][2]['release'] == 'testPush_V9')

        # Add a display hist here
        showHistory.execute(['--name', 'testPkg', '--type', 'SIOC'])


class TestPyDMRevert(unittest.TestCase):
 
    testDataFolder = "/tmp"
    startingDir = os.path.realpath(os.curdir)
 
    def setUp(self):
        self.testDataFolder = testUtils.getTestFolder()
        testUtils.createStdPyDMFolders()
  
         
    def tearDown(self):
        shutil.rmtree(os.path.join(self.testDataFolder, testUtils.PyDMTestFolder))
        shutil.rmtree(os.path.join(self.testDataFolder, testUtils.PyDMWorkspaceFolder))


    def getPyDMDisplayVersionsInAllFacilities(self):
        '''Get the versions of the PyDM display in all facilities as a dict'''
        PyDMDisplayVersions = {}
        for facility in toolconfig.getListOfFacilities():
            arg = {}
            arg['name'] = 'pydm-testSubsys'
            arg['releaseFolder'] = os.path.join(self.testDataFolder, testUtils.PyDMTestFolder, facility, "pydmTop", "release")
            arg['linkFolder'] = os.path.join(self.testDataFolder, testUtils.PyDMTestFolder, facility, "pydmTop")
            ret = multi_facility_process_helper.determinePyDMDisplayVersion(arg)
            PyDMDisplayVersions[facility] = ret
        return PyDMDisplayVersions

    def getHist(self):
        ''' Gets the history for this package'''
        ret = {}
        for facility in toolconfig.getListOfFacilities():
            arg = {}
            arg['name'] = 'pydm-testSubsys'
            arg['type'] = 'PyDM'
            arg['releaseFolder'] = toolconfig.getConfigForFacility(facility)['PyDM']['releaseFolder']
            ret[facility] = multi_facility_process_helper.hist(arg)
        return ret

    def createPushAndUpgrade(self, testPushVersionName, testupgradeargs):
        '''Create, push and upgrade a PyDM display'''
        testPushPkgName = "pydm-testSubsys"
        testUtils.createNewPyDMRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.PyDMWorkspaceFolder, testPushPkgName, testPushVersionName))
        pushargs = []
        if globals.verbose:
            pushargs.append('-v')
        pushRelease.execute(pushargs)
 
        upgradeargs = []
        if globals.verbose:
            upgradeargs.append('-v')
        upgradeargs.extend(testupgradeargs)
        upgradeRelease.execute(upgradeargs)
 
    def revert(self, testrevertargs):
        revertargs = []
        if globals.verbose:
            revertargs.append('-v')
        revertargs.extend(testrevertargs)
        revertRelease.execute(revertargs)
        testUtils.cd(startingDir)


    def testPyDMInOneFacility(self):
        '''Test reverting PyDM in one facility '''
        self.createPushAndUpgrade('testPush_V9', ['--facility', 'test1', 'testPush_V9'])

        PyDMDisplayVersionsBefore = self.getPyDMDisplayVersionsInAllFacilities()

        self.createPushAndUpgrade('testPush_V10', ['--facility', 'test1', 'testPush_V10'])
        self.revert(['--facility', 'test1'])

        PyDMDisplayVersionsAfter = self.getPyDMDisplayVersionsInAllFacilities()

        hist = self.getHist()
        if globals.verbose:
            print("History {0}".format(hist))
        self.assertTrue('MASTER' in hist['test1'])
        self.assertTrue(hist['test1']['MASTER'][0]['release'] == 'testPush_V9')
        self.assertTrue(hist['test1']['MASTER'][1]['release'] == 'testPush_V10')
        self.assertTrue(hist['test1']['MASTER'][2]['release'] == 'testPush_V9')
        
        # Add a display hist here
        showHistory.execute(['--name', 'pydm-testSubsys', '--type', 'SIOC'])


