import unittest
from gradescope_utils.autograder_utils.decorators import number, weight
import subprocess

def nonEmptyLine(s):
        return len(s.strip()) != 0
    
def stripstr(s):
    return s.strip()

def removeEmptyLines(text):
    lst = filter(nonEmptyLine, list(map(stripstr, text.split("\n"))))
    return "\n".join(list(lst))

class TestDiff(unittest.TestCase):
    maxDiff = None
    
    def setUp(self):
        pass 

    # Associated test number within Gradescope
    @number("0")
    # Associated point value within Gradescope
    @weight(5)
    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(10)
    def test_3Pizzas(self):
        # Title used by Gradescope 
        """Check that program prints correct output for 3 pizzas"""

        # Create a subprocess to run the students code to obtain an output
        cat = subprocess.Popen(["cat", "input/3.txt"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        test = subprocess.Popen(["./PA3.out"], stdin=cat.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        
        # Try to decode stdout
        try:
            output = test.stdout.read().strip().decode('utf-8')
            test.kill()
            
            # Open reference output and decode
            reference = open('reference/3.txt', 'rb').read().strip().decode('utf-8')
            
            # Remove empty lines from both output and reference
            output = removeEmptyLines(output)
            reference = removeEmptyLines(reference)
            
            # Check that output and reference are equal
            self.assertEqual(output, reference, msg='Program output does not match expected output')
        # Catch exception for decode error
        except (UnicodeDecodeError):
            test.kill()
            msg = 'Your program printed a character that the autograder cannot decode. Ensure your program prints valid characters.'
            # Use assertTrue to print regular message as test failure in Gradescope
            self.assertTrue(False, msg)
        
        test.terminate()
        
    # Associated test number within Gradescope
    @number("1")
    # Associated point value within Gradescope
    @weight(15)
    def test_5Pizzas(self):
        # Title used by Gradescope 
        """Check that program prints correct output for 5 pizzas"""

        # Create a subprocess to run the students code to obtain an output
        cat = subprocess.Popen(["cat", "input/5.txt"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        test = subprocess.Popen(["./PA3.out"], stdin=cat.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        
        # Try to decode stdout
        try:
            output = test.stdout.read().strip().decode('utf-8')
            test.kill()
            
            # Open reference output and decode
            reference = open('reference/5.txt', 'rb').read().strip().decode('utf-8')
            
            # Remove empty lines from both output and reference
            output = removeEmptyLines(output)
            reference = removeEmptyLines(reference)
            
            # Check that output and reference are equal
            self.assertEqual(output, reference, msg='Program output does not match expected output')
        # Catch exception for decode error
        except (UnicodeDecodeError):
            test.kill()
            msg = 'Your program printed a character that the autograder cannot decode. Ensure your program prints valid characters.'
            # Use assertTrue to print regular message as test failure in Gradescope
            self.assertTrue(False, msg)
        
        test.terminate()