Takes a parsed JSON dict representing a set of tests in the auto-threaded
        format and validates it, descending recursively if necessary.

def validate_auto_threaded_json(pjson):
        """Takes a parsed JSON dict representing a set of tests in the auto-threaded
        format and validates it, descending recursively if necessary."""
        # pjson should be a dict or unicode
        if not type(pjson) is dict:
            raise ParseError("Expected a JSON object/dictionary or a string representing a test. Instead got %s." 
                             % pjson)

        # Make sure that the 'tests' key is present 
        try:
            tests = pjson["tests"]
        except KeyError:
            raise KeyError("Test object requires 'tests' attribute.\nObject: %s" % 
                           json.dumps(pjson))

        # Verify that tests is a list
        if type(tests) is not list:
            raise ParseError(
                "Expected 'tests' to be a list but got object of type %s.\nObject: %s" %
                (type(tests), json.dumps(tests))
            )

        # Verify that 'options' is a dict if present
        options = pjson.get('options', {})
        if type(options) is not dict:
            raise ParseError('"options" attribute must be a dictionary')

        # Verify that 'environments' is a list of dicts if present
        environments = pjson.get('environments', [])
        is_list_of_dicts = lambda ls: len(ls) == len(filter(lambda d: type(d) is dict, ls))
        if type(environments) is not list or not is_list_of_dicts(environments):
            raise ParseError('"environments" attribute must be a list of dictionaries. Instead got %s.' % environments)

        for test in tests:
            if type(test) is unicode:
                # Base case for recursion
                pass
            else:
                # Recur
                ConfigurationValidator.validate_auto_threaded_json(test)