from __future__ import print_function

import os
import unittest
import shutil
import json
import multi_facility_process_helper
import multi_facility_process_helper_client
import globals
import toolconfig
import testUtils
import pushRelease
import envPathsParser
import cpuEnvPathsParser

class TestIOCPush(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 validateEnvPaths(self, facility, testPushPkgName, testPushVersionName):
        facilityConfig = toolconfig.getConfigForFacility(facility)
        oldFacilityPath = os.environ['PACKAGE_TOP']
        newFacilityPath = facilityConfig['SIOC'].get('packageFolder', '')
        iocTopFolder = os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName, testPushVersionName)
        for bootFolderName in ['iocBoot', 'cpuBoot']:
            bootFolder = os.path.join(iocTopFolder, bootFolderName)
            if globals.verbose:
                print("boot folder is {0}".format(bootFolder))
            for root, dirs, files in os.walk(bootFolder):
                for file in files:
                    if file == 'envPaths':
                        if globals.verbose:
                            print("Checking for env paths in {0}".format(os.path.join(root, file)))
                        envPaths = envPathsParser.parseAndReplaceEnvPathsFile(os.path.join(root, file), {})
                        self.assertTrue('TOP' in envPaths)
                        self.assertTrue(iocTopFolder in envPaths['TOP'], "Checking for " + iocTopFolder + " in " + os.path.join(root, file) + "envPaths - envPaths['TOP'] is " + envPaths['TOP'])
                    elif file == 'cpuEnv.sh':
                        if globals.verbose:
                            print("Checking for env paths in {0}".format(os.path.join(root, file)))
                        replaceDict = {'PACKAGE_SITE_TOP': newFacilityPath}
                        envPaths, pkgPaths = cpuEnvPathsParser.parseAndReplaceEnvPathsFile(os.path.join(root, file), replaceDict, 'PACKAGE_SITE_TOP')
                        self.assertTrue('TOP' in envPaths)
                        self.assertTrue(iocTopFolder in envPaths['TOP'], "Checking for " + iocTopFolder + " in " + os.path.join(root, file) + "envPaths - envPaths['TOP'] is " + envPaths['TOP'])
                        if newFacilityPath:
                            for name, path in envPaths.items():
                                if globals.verbose:
                                    print("Verifying that {0} is not in {1}".format(oldFacilityPath, path))
                                self.assertTrue(oldFacilityPath not in path)
                            for path in pkgPaths.values():
                                if globals.verbose:
                                    print("Verifying that {0} is in {1}".format(newFacilityPath, path))
                                self.assertTrue(newFacilityPath in path)
                        else:
                            if globals.verbose:
                                print("packageFolder not found in {0}".format(facility))
                                print("Ensuring package paths are unchanged")
                            for path in pkgPaths.values():
                                if globals.verbose:
                                    print("Verifying that {0} is in {1}".format(oldFacilityPath, path))
                                self.assertTrue(oldFacilityPath in path)
                    else:
                        print("Skipping checking for env paths in {0}".format(os.path.join(root, file)))


    def test_determineIOCSimplePush(self):
        '''Tests a simple, normal push for an IOC. We have a brand new version and everything is precreated.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V1"
        testUtils.createNewIOCRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.IOCWorkspaceFolder, testPushPkgName, testPushVersionName))
        args = []
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)
        testUtils.cd(startingDir)
        # Now look into iocTop to make sure the release made it
        for facility in toolconfig.getListOfFacilities():
            arg = [os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName)]
            lsresult = multi_facility_process_helper.ls(arg)
            if globals.verbose:
                print("lsresult is {0}".format(lsresult))
            file2details = {}
            for details in lsresult:
                file2details[details['name']] = details
            self.assertTrue(testPushVersionName in file2details)
            releaseitself = [os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName, testPushVersionName)]
            releaselsresult = multi_facility_process_helper.ls(releaseitself)
            if globals.verbose:
                print("releaselsresult is {0}".format(releaselsresult))
            releaseDetails = {}
            for details in releaselsresult:
                releaseDetails[details['name']] = details
            self.assertTrue('README' in releaseDetails)
            self.assertTrue('iocBoot' in releaseDetails)
            self.assertTrue(not releaseDetails['README']['isFolder'])
            self.assertTrue(releaseDetails['iocBoot']['isFolder'])
            self.assertTrue(not releaseDetails['README']['isLink'])
            self.assertTrue(not releaseDetails['iocBoot']['isLink'])
            self.validateEnvPaths(facility, testPushPkgName, testPushVersionName)


    def test_checkIOCPushFreshen(self):
        '''We expect an exception when we push the same version twice and we do not pass a freshen flag. If we pass the freshen flag this should succeed.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V1"
        testUtils.createNewIOCRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.IOCWorkspaceFolder, testPushPkgName, testPushVersionName))
        args = []
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)
        # Push again, we should get an exception
        self.assertRaises(Exception, pushRelease.execute(args))
        args.append('-f')
        pushRelease.execute(args)
        testUtils.cd(startingDir)
        # Now look into iocTop to make sure the release made it
        for facility in toolconfig.getListOfFacilities():
            arg = [os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName)]
            lsresult = multi_facility_process_helper.ls(arg)
            if globals.verbose:
                print("lsresult is {0}".format(lsresult))
            file2details = {}
            for details in lsresult:
                file2details[details['name']] = details
            self.assertTrue(testPushVersionName in file2details)
            releaseitself = [os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName, testPushVersionName)]
            releaselsresult = multi_facility_process_helper.ls(releaseitself)
            if globals.verbose:
                print("releaselsresult is {0}".format(releaselsresult))
            releaseDetails = {}
            for details in releaselsresult:
                releaseDetails[details['name']] = details
            self.assertTrue('README' in releaseDetails)
            self.assertTrue('iocBoot' in releaseDetails)
            self.assertTrue(not releaseDetails['README']['isFolder'])
            self.assertTrue(releaseDetails['iocBoot']['isFolder'])
            self.assertTrue(not releaseDetails['README']['isLink'])
            self.assertTrue(not releaseDetails['iocBoot']['isLink'])
            self.validateEnvPaths(facility, testPushPkgName, testPushVersionName)


class TestHLAPush(unittest.TestCase):

    testDataFolder = "/tmp"

    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 test_determineHLASimplePush(self):
        '''Tests a simple, normal push for an HLA. We have a brand new version and everything is precreated.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "testPkg"
        testPushVersionName = "testHLAPush_V1"
        testUtils.createNewHLARelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.HLAWorkspaceFolder, testPushPkgName, testPushVersionName))
        args = []
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)
        testUtils.cd(startingDir)
        # Now look into hlaTop to make sure the release made it
        for facility in toolconfig.getListOfFacilities():
            arg = [os.path.join(self.testDataFolder, testUtils.HLATestFolder, facility, 'physicsTop', 'release', testPushPkgName)]
            lsresult = multi_facility_process_helper.ls(arg)
            if globals.verbose:
                print("lsresult is {0}".format(lsresult))
            file2details = {}
            for details in lsresult:
                file2details[details['name']] = details
            self.assertTrue(testPushVersionName in file2details)
            releaseitself = [os.path.join(self.testDataFolder, testUtils.HLATestFolder, facility, 'physicsTop', 'release', testPushPkgName, testPushVersionName)]
            releaselsresult = multi_facility_process_helper.ls(releaseitself)
            if globals.verbose:
                print("releaselsresult is {0}".format(releaselsresult))
            releaseDetails = {}
            for details in releaselsresult:
                releaseDetails[details['name']] = details
            self.assertTrue('README' in releaseDetails)
            self.assertTrue(not releaseDetails['README']['isFolder'])
            self.assertTrue(not releaseDetails['README']['isLink'])

    def test_HLAPushFreshen(self):
        '''We expect an exception when we push the same version twice and we do not pass a freshen flag. If we pass the freshen flag this should succeed.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "testPkg"
        testPushVersionName = "testHLAPush_V1"
        testUtils.createNewHLARelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.HLAWorkspaceFolder, testPushPkgName, testPushVersionName))
        args = []
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)
        # Push again, we should get an exception
        self.assertRaises(Exception, pushRelease.execute(args))
        args.append('-f')
        pushRelease.execute(args)
        testUtils.cd(startingDir)
        # Now look into hlaTop to make sure the release made it
        for facility in toolconfig.getListOfFacilities():
            arg = [os.path.join(self.testDataFolder, testUtils.HLATestFolder, facility, 'physicsTop', 'release', testPushPkgName)]
            lsresult = multi_facility_process_helper.ls(arg)
            if globals.verbose:
                print("lsresult is {0}".format(lsresult))
            file2details = {}
            for details in lsresult:
                file2details[details['name']] = details
            self.assertTrue(testPushVersionName in file2details)
            releaseitself = [os.path.join(self.testDataFolder, testUtils.HLATestFolder, facility, 'physicsTop', 'release', testPushPkgName, testPushVersionName)]
            releaselsresult = multi_facility_process_helper.ls(releaseitself)
            if globals.verbose:
                print("releaselsresult is {0}".format(releaselsresult))
            releaseDetails = {}
            for details in releaselsresult:
                releaseDetails[details['name']] = details
            self.assertTrue('README' in releaseDetails)
            self.assertTrue(not releaseDetails['README']['isFolder'])
            self.assertTrue(not releaseDetails['README']['isLink'])


class TestPyDMPush(unittest.TestCase):

    testDataFolder = "/tmp"

    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 test_determinePyDMSimplePush(self):
        '''Tests a simple, normal push for a PyDM display. We have a brand new version and everything is precreated.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "pydm-testSubsys"
        testPushVersionName = "testPyDMPush_V1"
        testUtils.createNewPyDMRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.PyDMWorkspaceFolder, testPushPkgName, testPushVersionName))
        args = []
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)
        testUtils.cd(startingDir)
        # Now look into hlaTop to make sure the release made it
        for facility in toolconfig.getListOfFacilities():
            arg = [os.path.join(self.testDataFolder, testUtils.PyDMTestFolder, facility, 'pydmTop', 'release', testPushPkgName)]
            lsresult = multi_facility_process_helper.ls(arg)
            if globals.verbose:
                print("lsresult is {0}".format(lsresult))
            file2details = {}
            for details in lsresult:
                file2details[details['name']] = details
            self.assertTrue(testPushVersionName in file2details)
            releaseitself = [os.path.join(self.testDataFolder, testUtils.PyDMTestFolder, facility, 'pydmTop', 'release', testPushPkgName, testPushVersionName)]
            releaselsresult = multi_facility_process_helper.ls(releaseitself)
            if globals.verbose:
                print("releaselsresult is {0}".format(releaselsresult))
            releaseDetails = {}
            for details in releaselsresult:
                releaseDetails[details['name']] = details
            self.assertTrue('README' in releaseDetails)
            self.assertTrue(not releaseDetails['README']['isFolder'])
            self.assertTrue(not releaseDetails['README']['isLink'])

    def test_PyDMPushFreshen(self):
        '''We expect an exception when we push the same version twice and we do not pass a freshen flag. If we pass the freshen flag this should succeed.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "pydm-testSubsys"
        testPushVersionName = "testPyDMPush_V1"
        testUtils.createNewPyDMRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.PyDMWorkspaceFolder, testPushPkgName, testPushVersionName))
        args = []
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)
        # Push again, we should get an exception
        self.assertRaises(Exception, pushRelease.execute(args))
        args.append('-f')
        pushRelease.execute(args)
        testUtils.cd(startingDir)
        # Now look into hlaTop to make sure the release made it
        for facility in toolconfig.getListOfFacilities():
            arg = [os.path.join(self.testDataFolder, testUtils.PyDMTestFolder, facility, 'pydmTop', 'release', testPushPkgName)]
            lsresult = multi_facility_process_helper.ls(arg)
            if globals.verbose:
                print("lsresult is {0}".format(lsresult))
            file2details = {}
            for details in lsresult:
                file2details[details['name']] = details
            self.assertTrue(testPushVersionName in file2details)
            releaseitself = [os.path.join(self.testDataFolder, testUtils.PyDMTestFolder, facility, 'pydmTop', 'release', testPushPkgName, testPushVersionName)]
            releaselsresult = multi_facility_process_helper.ls(releaseitself)
            if globals.verbose:
                print("releaselsresult is {0}".format(releaselsresult))
            releaseDetails = {}
            for details in releaselsresult:
                releaseDetails[details['name']] = details
            self.assertTrue('README' in releaseDetails)
            self.assertTrue(not releaseDetails['README']['isFolder'])
            self.assertTrue(not releaseDetails['README']['isLink'])


class TestPushWithManifests(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 checkStdFiles(self, facility, testPushPkgName, testPushVersionName):
        '''Checks is the usual files made it'''
        arg = [os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName)]
        lsresult = multi_facility_process_helper.ls(arg)
        if globals.verbose:
            print("lsresult is {0}".format(lsresult))
        file2details = {}
        for details in lsresult:
            file2details[details['name']] = details
        self.assertTrue(testPushVersionName in file2details)
        releaseitself = [os.path.join(self.testDataFolder, testUtils.iocTestFolder, facility, 'iocTop', testPushPkgName, testPushVersionName)]
        releaselsresult = multi_facility_process_helper.ls(releaseitself)
        if globals.verbose:
            print("releaselsresult is {0}".format(releaselsresult))
        releaseDetails = {}
        for details in releaselsresult:
            releaseDetails[details['name']] = details
        self.assertTrue('README' in releaseDetails)
        self.assertTrue('iocBoot' in releaseDetails)
        self.assertTrue(not releaseDetails['README']['isFolder'])
        self.assertTrue(releaseDetails['iocBoot']['isFolder'])
        self.assertTrue(not releaseDetails['README']['isLink'])
        self.assertTrue(not releaseDetails['iocBoot']['isLink'])
        return releaseDetails

    def test_PushWithManifests(self):
        '''Adds rsync include and exclude files to the workspace and makes sure they respond appropriately.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V1"
        testUtils.createNewIOCRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.IOCWorkspaceFolder, testPushPkgName, testPushVersionName))

        args = ['--freshen']
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)

        for facility in toolconfig.getListOfFacilities():
            releaseDetails = self.checkStdFiles(facility, testPushPkgName, testPushVersionName)

        # Add a include file and check
        includeAll = "\n".join(['.cram/**', 'iocBoot/**', 'README', 'incextest*'])
        testUtils.touch(".cram/push_include", includeAll)

        args = ['--freshen']
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)

        for facility in toolconfig.getListOfFacilities():
            releaseDetails = self.checkStdFiles(facility, testPushPkgName, testPushVersionName)

        testUtils.touch("exall_incextest")
        exAll = "\n".join(['exall_incextest'])
        testUtils.touch(".cram/push_exclude", exAll)

        args = ['--freshen']
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)

        for facility in toolconfig.getListOfFacilities():
            releaseDetails = self.checkStdFiles(facility, testPushPkgName, testPushVersionName)
            self.assertTrue('exall_incextest' not in releaseDetails)

        testUtils.touch("ex_test1_incextest")
        exAll = "\n".join(['ex_test1_incextest'])
        testUtils.touch(".cram/test1_push_exclude", exAll)

        args = ['--freshen']
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)

        for facility in toolconfig.getListOfFacilities():
            releaseDetails = self.checkStdFiles(facility, testPushPkgName, testPushVersionName)
            self.assertTrue('incextest_exall' not in releaseDetails)
            if facility == 'test1':
                self.assertTrue('ex_test1_incextest' not in releaseDetails)
            else:
                self.assertTrue('ex_test1_incextest' in releaseDetails)

        testUtils.cd(startingDir)




    def test_PushWithManifestsExcludeAllIncludeSpecificFiles(self):
        '''Tests the use case where we exclude everything and include only specific files.'''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "testPkg"
        testPushVersionName = "testPush_V1"
        testUtils.createNewIOCRelease(testPushPkgName, testPushVersionName)
        testUtils.cd(os.path.join(self.testDataFolder, testUtils.IOCWorkspaceFolder, testPushPkgName, testPushVersionName))

        # Add a exclude all and check to make sure nothing made it.
        excludeAll = "\n".join(['*'])
        testUtils.touch(".cram/push_exclude", excludeAll)

        includeSpecific = "\n".join(['/*', '.cram/**', 'iocBoot/**', 'README', 'incextest*'])
        testUtils.touch(".cram/push_include", includeSpecific)

        args = ['--freshen']
        if globals.verbose:
            args.append('-v')
        pushRelease.execute(args)

        for facility in toolconfig.getListOfFacilities():
            releaseDetails = self.checkStdFiles(facility, testPushPkgName, testPushVersionName)
