def run_test(f, ignores=[], raise_errors=True):
    """
    Runs the test required for the test file.
     
    Params
    ======
    f: str
        The name of the test file.
    ignores: list of int
        The index of the case to ignore
    """
    # Ensure that #ignores# is a list
    if type(ignores) == type(1):
        ignores = [ignores]

    #  Extract arguments
    cases = get_test_vals(f)

    # Loop through args and ans
    for cx, c in enumerate(cases):
        # If cx is in #ignores#, we ingore it
        if cx in ignores:
            continue

        # Unpack arguments and answers
        args = c['-arg']
        ans = c['-ans']

        # Repackage into string
        args = '\n'.join(args)
        ans = '\n'.join(ans)

        # Instantiate fnc arguments
        fnc.set_inputs(args)

        # Get start time
        start_time = datetime.now()

        # Run function
        try:

            try:
                fo(TIME_LIMIT, fnc.main, (args, ))

                # fnc.main(args)

            except FunctionTimedOut as e:
                raise e

            except Exception as e:
                raise e

            # Check answers
            assert "\n".join(fnc.fptr.get_answers()) == ans

        except Exception as e:
            c['error'] = e

        except FunctionTimedOut as e:
            c['error'] = e

        # Get time taken
        c['time_taken'] = datetime.now() - start_time

    # Give conclusions about each test case
    # Loop through cases to check for errors
    for cx, c in enumerate(cases):
        # Check for ignores
        if cx in ignores:
            continue

        print(f"\ntest case {str(cx)}: {str(c['time_taken'])}")

    for c in cases:
        # Only print errors if requested
        if raise_errors:

            try:
                raise c['error']

            except KeyError as e:
                # Suggests there are no errors
                continue
def run_test(f, dcstr=[], dcix=[], raise_errors=True):
    """
    Runs the test required for all cases within the folder. Any cases within dcstr and dcix are ignored.

    Params
    ======
    f: str
        The name of the test folder.
    dcstr: []
        Array of strings. 

        If the element of dcstr is an exact match with any of the case file names, the case is ignored.
    dcix: []
        Array of integers.

        Less reliable 'ignore' method. This ignores the 0-indexed element of the collected cases. 
    raise_errors: bool
        Whether any errors gathered while testing the cases should be returned. If false, only whether a case succeeded or failed is returned.
    """
    # === Ensure that dcstr and dcix are lists ===
    # Check if dcstr is a list
    if type(dcstr) == type([]):

        # If it is a list, we identify if all elements are of type 'str'

        # Filter out all non-string types
        other_types = [type(x) for x in dcstr]
        other_types = list(set(other_types))
        other_types = list(filter(lambda x: x != type(""), other_types))

        # If there are non-string types, raise an exception
        if other_types:

            raise Exception(f"dcstr must be a list of strings. Elements of type {other_types} found.")

    # If it's not a list, check if it's a string
    elif type(dcstr) == type(""):

        # Set it to a list of string
        dcstr = [dcstr] 

    # If it is neither a string or a list, we reject it. 
    else: 

        raise Exception(f"dcstr must be a string or a list of strings, not a {str(type(dcstr))}.")

    # We do the same check for dcix
    if type(dcix) == type([]):

        # If it is a list, we identify if all elements are of type 'str'

        # Filter out all non-string types
        other_types = [type(x) for x in dcix]
        other_types = list(set(other_types))
        other_types = list(filter(lambda x: x != type(1), other_types))

        # If there are non-string types, raise an exception
        if other_types:

            raise Exception(f"dcstr must be a list of integers. Elements of type {other_types} found.")

    # If it's not a list, check if it's a string
    elif type(dcix) == type(1):

        # Set it to a list of string
        dcix = [dcix] 

    # If it is neither a string or a list, we reject it. 
    else: 

        raise Exception(f"dcix must be an integer or a list of integers, not a {str(type(dcix))}.")

    # === Get cases from the folder ===
    cases = None

    try: 
        # Obtain cases 
        cases = get_cases(f)
    
    except AssertionError:
        raise Exception(f"The path '{f}' is not a valid folder.")

    # Ensure there are cases to run through
    if not cases:
        raise Exception(f"There are no test cases in '{f}'.")

    # === Loop through each case ===
    for cx, c in enumerate(cases): 
        
        # If cx is in dcix, ignore this case
        # If the name of the case is in dcstr, we ignore the case
        if cx in dcix or c["filename"] in dcstr: 
            continue 

        # Print out test case
        print(f"({f}) test case {cx} '{c['filename']}': ", end="")

        # Instantiate fnc arguments
        fnc.set_inputs(c["inputs"])
        
        # Get start time
        start_time = datetime.now()

        # Run function
        try: 
            fto(
                TIME_LIMIT,
                fnc.main,
                (c["inputs"], )
            )

        # If the function times out, add it as an error
        except FunctionTimedOut as e:
            c["errors"] = e 

        # For any other exception, we also add it as an error
        # The reason we separate FunctionTimedOut from Exception is because FunctionTimedOut is not considered an Exception by the program
        except Exception as e:
            c["errors"] = e

        # Here, we check if there are errors
        if "errors" in c.keys():

            # If there are errors, print the error out
            print(c["errors"])

        else:

            # If there are no exceptions, we check that the answer is correct
            try: 

                assert "\n".join(fnc.fptr.get_answers()) == c["outputs"]

                print("Success")

            except Exception as e:
                
                # There are, as of now, no errors
                # We set the errors to the assertion error
                c["errors"] = e
                
                # Print the error
                print(e)

    # Finally, we raise all errors so py.test recognises that this test case failed
    for c in cases:
        if "errors" in c.keys():
            raise c["errors"]