import unittest
from gradescope_utils.autograder_utils.decorators import number, weight
import subprocess

class TestDiff(unittest.TestCase):
    def setUp(self):
        pass 

    # Associated test number within Gradescope
    @number("0")
    # Associated point value within Gradescope
    @weight(20)
    def test_Compile(self):
        # Title used by Gradescope 
        """Clean Compile"""

        # Create a subprocess to run the students make file to ensure it compiles
        test = subprocess.Popen(["make"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output = test.stderr.read().strip().decode('utf-8')
        test.kill()

        # Standard unit test case with an associated error message
        self.assertTrue(output == "", msg=output)
        test.terminate()

    # Associated test number within Gradescope
    @number("1")
    # Associated point value within Gradescope
    @weight(35)
    def test_Output(self):
        # Title used by Gradescope 
        """Check program output"""

        # Create a subprocess to run the students code to obtain an output
        test = subprocess.Popen(["./broken.out"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output = test.stdout.read().strip().decode('utf-8')
        test.kill()
        
        # Boolean to hold value for test pass/fail (updated procedurally)
        correct = True
        
        # Array of expected lines of output, line by line in expected order
        expected = ['table[0]: 5', 'table[1]: 10', 'table[2]: 12', 'table[3]: 14', 'table[4]: 16', 'table[5]: 18', 'table[6]: 20', 'table[7]: 22', 'table[8]: 24', 'table[9]: 26', 'The number 11 is not in the table.']
        # Concatenated string of expected output, for display as unformatted expected output in error messages
        expectedUnformatted = ''
        for line in expected:
            expectedUnformatted = expectedUnformatted + line + '\n'
        
        # Create array of lines of submission output by splitting at new lines
        linesPrinted = output.split("\n")
        # Strip all lines containing nothing but the new line character
        linesPrinted = [line for line in linesPrinted if line != '\n']
        
        # Check for correct number of lines in output (to help narrow down test failures for students)
        if len(linesPrinted) < len(expected):
            # If too few lines, set test failure status and update message appropriately
            correct = False
            msg = ['Your program\'s output has fewer lines than expected. Check the lab instructions or copy your program\'s output and the expected output into https://diffchecker.com.', f'Your output (unformatted): {output}', f'Expected output (unformatted): {expectedUnformatted}']
        elif len(linesPrinted) > len(expected):
            # If too many lines, set test failure status and update message appropriately
            correct = False
            msg = ['Your program\'s output has more lines than expected. Check the lab instructions or copy your program\'s output and the expected output into https://diffchecker.com.', f'Your output (unformatted): {output}', f'Expected output (unformatted): {expectedUnformatted}']
        else:
            # If correct number of lines, check content of each line
            currentLine = 0
            for line in linesPrinted:
                # Compare each line of expected array to current line from output
                if expected[currentLine] not in line:
                    # When output does not match expected, set test failure status
                    correct = False
                currentLine += 1
            # Update message appropriately
            msg = ['Your program\'s output does not match the expected output. Check the lab instructions or copy your program\'s output and the expected output into https://diffchecker.com.', f'Your output (unformatted): {output}', f'Expected output (unformatted): {expectedUnformatted}']
        
        # Pass test pass/fail status and message to test run output
        self.assertTrue(correct, msg)
        test.terminate()