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 lintRelease


class TestLint(unittest.TestCase):
    '''Testing for positives/negatives for each of the lint checks.
    '''

    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 test_Lint(self):
        '''These are the current lint checks we test.
        1) We look at all dbLoadRecords statements and make sure that the file exists and is contained within the software package.
        2) We look at all cd statements and make sure that the folder we are changing to exists and is contained within the software package.
        3) We process all include statements that look like so "< iocBoot/${IOC}/areaDetector.cfg" and make sure make sure that the file exists and is contained within the software package.
        '''
        startingDir = os.path.realpath(os.curdir)
        testPushPkgName = "test1_specific_package"
        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')

        lintStates = lintRelease.lintRelease(args)
        self.assertTrue(lintStates)
        lintStatesDict = {}
        for (iocFolder, lintState) in lintStates:
            lintStatesDict[iocFolder] = lintState
        
        for ioc in ["sioc-test1-001", "sioc-test1-002", "sioc-test1-003", "sioc-test1-004", "sioc-test1-005", "sioc-test2-001", "sioc-test2-002", "sioc-test2-003", "sioc-test2-004", "sioc-test2-005"]:
            self.assertTrue(os.path.join('iocBoot', ioc) in lintStatesDict)
            lintErrorsDict = {}
            lintLineNums = set()
            lintState = lintStatesDict[os.path.join('iocBoot', ioc)]
            for error in lintState.errors:
                # Form a dict using filename + line + first two words of error msg as a proxy for error type.
                lintErrorsDict[error['srcFile'] + ':' + str(error['srcLine']) + ' '.join(error['msg'].split(' ')[0:2])] = error['msg']
                lintLineNums.add(error['srcLine'])
            if globals.verbose:
                print sorted(lintErrorsDict.items())
            #iocBoot/sioc-test1-002/st.cmd:26 Missing file db/test.db in ${TOP}; perhaps you missed adding a DB_INSTALLS += db/test.db to your Makefile
            #iocBoot/sioc-test1-002/st.cmd:27 Add DB_INSTALLS += ${MOTOR}/motorApp/Db/asyn_motor.db to your Makefile and change this to dbLoadRecords("db/asyn_motor.db")
            #iocBoot/sioc-test1-002/st.cmd:28 Missing file db/my_nonexistent_test.db in ${TOP}; perhaps you missed adding a DB_INSTALLS += db/my_nonexistent_test.db to your Makefile
            #iocBoot/sioc-test1-002/st.cmd:29 Add DB_INSTALLS += ${MOTOR}/motorApp/Db/nonexistent_test.db to your Makefile and change this to dbLoadRecords("db/nonexistent_test.db")
            #iocBoot/sioc-test1-002/st.cmd:30 Comment out the 'cd' to an external folder ${MOTOR}
            #iocBoot/sioc-test1-002/st.cmd:31 Add DB_INSTALLS += motorApp/Db/asyn_motor.db to your Makefile and change this to dbLoadRecords("db/asyn_motor.db")
            #iocBoot/sioc-test1-002/st.cmd:32 Add DB_INSTALLS += motorApp/Db/nonexistent_test.db to your Makefile and change this to dbLoadRecords("db/nonexistent_test.db")
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '26' + 'Missing file' in lintErrorsDict)
            self.assertTrue('Missing file ' + os.path.join(lintState.topDir, 'db', 'test.db') in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '26' + 'Missing file'])
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '27' + 'Add DB_INSTALLS' in lintErrorsDict)
            self.assertTrue('${MOTOR}/motorApp/Db/asyn_motor.db' in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '27' + 'Add DB_INSTALLS'])
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '28' + 'Missing file' in lintErrorsDict)
            self.assertTrue('db/my_nonexistent_test.db in ' + lintState.topDir in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '28' + 'Missing file'])
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '29' + 'Add DB_INSTALLS' in lintErrorsDict)
            self.assertTrue('${MOTOR}/motorApp/Db/nonexistent_test.db' in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '29' + 'Add DB_INSTALLS'])
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '30' + 'Comment out' in lintErrorsDict)
            self.assertTrue(' to an external folder ' in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '30' + 'Comment out'])
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '31' + 'Add DB_INSTALLS' in lintErrorsDict)
            self.assertTrue('motorApp/Db/asyn_motor.db' in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '31' + 'Add DB_INSTALLS'])
            self.assertTrue(os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '32' + 'Add DB_INSTALLS' in lintErrorsDict)
            self.assertTrue('motorApp/Db/nonexistent_test.db' in lintErrorsDict[os.path.join('iocBoot', ioc, 'st.cmd') + ':' + '32' + 'Add DB_INSTALLS'])

            # We only get these errors and nothing else.            
            self.assertTrue(not(lintLineNums - set([26, 27, 28, 29, 30, 31, 32])))
            
        # This needs to be the last line        
        testUtils.cd(startingDir)
