/*
 * See the dyninst/COPYRIGHT file for copyright information.
 *
 * We provide the Paradyn Tools (below described as "Paradyn")
 * on an AS IS basis, and do not warrant its validity or performance.
 * We reserve the right to update, modify, or discontinue this
 * software at any time.  We shall have no obligation to supply such
 * updates or modifications or any other form of support to you.
 *
 * By your use of Paradyn, you understand and agree that we (or any
 * other person or entity with proprietary rights in Paradyn) are
 * under no obligation to provide either maintenance services,
 * update services, notices of latent defects, or correction of
 * defects for Paradyn.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "symtab_comp.h"
#include "test_lib.h"

#include "Module.h"
#include "Symbol.h"
#include "Symtab.h"
#include "Type.h"
#include <array>
#include <functional>
#include <iostream>

using namespace Dyninst;
using namespace SymtabAPI;

class test_type_info_Mutator : public SymtabMutator {
   std::vector<Type *> *std_types;
   std::vector<Type *> *builtin_types;
   test_results_t verify_basic_type_lists();
   std::string execname;
   bool verify_type(Type *t);
   bool verify_type_enum(typeEnum *t, std::vector<std::pair<std::string, int> > * = NULL);
   bool verify_type_pointer(typePointer *t, std::string * = NULL);
   bool verify_type_function(typeFunction *t);
   bool verify_type_subrange(typeSubrange *t);
   bool verify_type_array(typeArray *t, int * = NULL, int * = NULL, std::string * = NULL);
   bool verify_type_struct(typeStruct *t, 
             std::vector<std::pair<std::string, std::string> > * = NULL, 
             std::vector<std::pair<std::string, std::string> > * = NULL,
             std::vector<std::pair<std::string, std::string> > * = NULL);
   bool verify_type_union(typeUnion *t, 
		   std::vector<std::pair<std::string, std::string> > * = NULL, 
		   std::vector<std::pair<std::string, std::string> > * = NULL);
   bool verify_type_scalar(typeScalar *t);
   bool verify_type_typedef(typeTypedef *t, std::string * = NULL);
   bool verify_field(Field *f);
   bool verify_field_list(fieldListType *t, 
		   std::vector<std::pair<std::string, std::string> > * = NULL, 
         std::vector<std::pair<std::string, std::string> > * = NULL,
		   std::vector<std::pair<std::string, std::string> > * = NULL);

   bool got_type_enum;
   bool got_type_pointer;
   bool got_type_function;
   bool got_type_subrange;
   bool got_type_array;
   bool got_type_struct;
   bool got_type_union;
   bool got_type_scalar;
   bool got_type_typedef;

   supportedLanguages lang;
	public:
   test_type_info_Mutator() : 
	   std_types(NULL), 
	   builtin_types(NULL),
	   got_type_enum(false),
	   got_type_pointer(false),
	   got_type_function(false),
	   got_type_subrange(false),
	   got_type_array(false),
	   got_type_struct(false),
	   got_type_union(false),
	   got_type_scalar(false),
	   got_type_typedef(false),
	   lang(lang_Unknown)
   { }

   bool specific_type_tests();

   bool got_all_types()
   {
	   if (!got_type_enum)
	   {
		   logerror( "%s[%d]:  enum was missed\n", FILE__, __LINE__);
		   return false;
	   }

	   if (!got_type_pointer)
	   {
		   logerror( "%s[%d]:  pointer was missed\n", FILE__, __LINE__);
		   return false;
	   }

#if 0
	   //  I think typeFunction is c++ only
	   if (!got_type_function)
	   {
		   logerror( "%s[%d]:  function was missed\n", FILE__, __LINE__);
		   return false;
	   }
#endif

	   if (!got_type_subrange)
	   {
#if !defined(os_windows_test)
		   logerror( "%s[%d]:  subrange was missed\n", FILE__, __LINE__);
		   return false;
#endif
    }

    if (!got_type_array) {
      logerror("%s[%d]:  array was missed\n", FILE__, __LINE__);
      return false;
    }

    if (!got_type_struct) {
      logerror("%s[%d]:  struct was missed\n", FILE__, __LINE__);
      return false;
    }

    if (!got_type_union) {
      logerror("%s[%d]:  union was missed\n", FILE__, __LINE__);
      return false;
    }

    if (!got_type_scalar) {
      logerror("%s[%d]:  scalar was missed\n", FILE__, __LINE__);
      return false;
    }

    if (!got_type_typedef) {
#if !defined(os_windows_test)
      logerror("%s[%d]:  typedef was missed\n", FILE__, __LINE__);
      return false;
#endif
    }

    return true;
  }
  virtual test_results_t executeTest();
};

extern "C" DLLEXPORT TestMutator *test_type_info_factory() {
  return new test_type_info_Mutator();
}

bool test_type_info_Mutator::verify_type_enum(
    typeEnum *t, std::vector<std::pair<std::string, int>> *vals) {
  got_type_enum = true;
  std::string &tn = t->getName();

  auto constants = t->getConstants();

  if (!constants.size()) {
    logerror("%s[%d]: empty enum %s\n", FILE__, __LINE__, tn.c_str());
    return false;
  }

  for (unsigned int i = 0; i < constants.size(); ++i) {
    if (constants[i].first.length() == 0) {
      logerror("%s[%d]:  enum %s has unnamed element\n", FILE__, __LINE__,
               tn.c_str());
      return false;
    }
  }

  if (vals) {
    std::vector<std::pair<std::string, int>> &expected_vals = *vals;

    if (expected_vals.size() != constants.size()) {
      logerror("%s[%d]:  differing sizes for values: %d vs %d\n", FILE__,
               __LINE__, expected_vals.size(), constants.size());
      return false;
    }

    for (unsigned int i = 0; i < expected_vals.size(); ++i) {
      std::string &tag1 = expected_vals[i].first;
      std::string &tag2 = constants[i].first;
      int &val1 = expected_vals[i].second;
      int &val2 = constants[i].second;

      if (tag1 != tag2) {
        logerror("%s[%d]:  enum elems[%d] differ: %s != %s\n", FILE__, __LINE__,
                 i, tag1.c_str(), tag2.c_str());
        return false;
      }

      if (val1 != val2) {
        logerror("%s[%d]:  enum elems[%d] differ: %d != %d\n", FILE__, __LINE__,
                 i, val1, val2);
        return false;
      }
    }
  }

  return true;
}

bool test_type_info_Mutator::verify_type_pointer(typePointer *t,
                                                 std::string *exp_base) {
  got_type_pointer = true;
  std::string &tn = t->getName();
  Type *c = t->getConstituentType();
  if (!c) {
    logerror("%s[%d]:  NULL constituent type for type %s!\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  if (exp_base) {
    if (c->getName() != *exp_base) {
      logerror("%s[%d]:  unexpected base type %s (not %s) for type %s\n",
               FILE__, __LINE__, c->getName().c_str(), exp_base->c_str(),
               tn.c_str());
      return false;
    }
  }
  return true;
}

bool test_type_info_Mutator::verify_type_function(typeFunction *t) {
  got_type_function = true;
  std::string &tn = t->getName();
  Type *retType = t->getReturnType();

  if (!retType) {
    logerror("%s[%d]:  func type %s has no return type\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  auto params = t->getParams();

  //  It is not an error to have zero params
  for (unsigned int i = 0; i < params.size(); ++i) {
    if (params[i] == NULL) {
      logerror("%s[%d]:  got NULL param type\n", FILE__, __LINE__);
      return false;
    }
  }

  return true;
}

bool test_type_info_Mutator::verify_type_subrange(typeSubrange *t) {
  got_type_subrange = true;
  std::string &tn = t->getName();

  if (t->getLow() > t->getHigh()) {
    logerror("%s[%d]:  bad range [%d--%d] for type %s!\n", FILE__, __LINE__,
             t->getLow(), t->getHigh(), tn.c_str());
    return false;
  }

  return true;
}

bool test_type_info_Mutator::verify_type_array(typeArray *t, int *exp_low, int *exp_hi, 
		std::string *base_type_name)
{
	got_type_array = true;
	std::string &tn = t->getName();

	//std::cerr << "verify_type_array for " << tn << std::endl;

	if (t->getLow() > t->getHigh())
	{
		//  special case -- if low bound is zero and
		//  highbound is -1, the array is not specified with a proper range, so
		//  ignore
		if (! (t->getLow() == 0L && t->getHigh() == -1L))
		{
			logerror( "%s[%d]:  bad ranges [%lu--%lu] for type %s!\n", 
					FILE__, __LINE__, t->getLow(), t->getHigh(), tn.c_str());
			return false;
		}
	}

	Type *b = t->getBaseType();
	if (!b)
	{
		logerror( "%s[%d]:  NULL base type for type %s!\n", 
				FILE__, __LINE__, tn.c_str());
		return false;
	}

	if (exp_low)
	{
		if (*exp_low != t->getLow())
		{
			logerror( "%s[%d]:  unexpected lowbound %d (not %d) for type %s!\n", 
					FILE__, __LINE__, t->getLow(), *exp_low, tn.c_str());
			return false;
		}
	}

	if (exp_hi)
	{
		if (*exp_hi != t->getHigh())
		{
			logerror( "%s[%d]:  unexpected hibound %d (not %d) for type %s!\n", 
					FILE__, __LINE__, t->getHigh(), *exp_hi, tn.c_str());
			return false;
		}
	}

	if (base_type_name)
	{
		if (*base_type_name != b->getName())
		{
			logerror( "%s[%d]:  unexpected basetype %s (not %s) for type %s!\n", 
					FILE__, __LINE__, b->getName().c_str(), base_type_name->c_str(), tn.c_str());
			return false;
		}
	}

	return true;
}

bool test_type_info_Mutator::verify_field(Field *f) {
  if (!f) {
    logerror("%s[%d]:  NULL field\n", FILE__, __LINE__);
    return false;
  }

  if (0 == f->getName().length()) {
    logerror("%s[%d]:  unnamed field\n", FILE__, __LINE__);
  }

  Type *ft = f->getType();
  if (NULL == ft) {
    logerror("%s[%d]:  field %s has NULL type\n", FILE__, __LINE__,
             f->getName().c_str());
    return false;
  }
  return true;
}

bool test_type_info_Mutator::verify_field_list(
    fieldListType *t, std::vector<std::pair<std::string, std::string>> *comps,
    std::vector<std::pair<std::string, std::string>> *efields,
    std::vector<std::pair<std::string, std::string>> *afields) {
  std::string &tn = t->getName();

  auto components = t->getComponents();

  if (comps && !components) {
    logerror("%s[%d]:  no components for type %s\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  if (components) {
    for (unsigned int i = 0; i < components->size(); ++i) {
      Field *f = (*components)[i];
      if (!verify_field(f)) {
        logerror("%s[%d]:  verify field failed for type %s\n", FILE__, __LINE__,
                 tn.c_str());
        return false;
      }
    }
  }

  auto fields = t->getFields();

  if (efields && !fields) {
    logerror("%s[%d]:  no fields for type %s\n", FILE__, __LINE__, tn.c_str());
    return false;
  }

  if (fields) {
    for (unsigned int i = 0; i < fields->size(); ++i) {
      Field *f = (*fields)[i];
      if (!verify_field(f)) {
        logerror("%s[%d]:  verify field failed for type %s\n", FILE__, __LINE__,
                 tn.c_str());
        return false;
      }
    }

    if (efields) {
      std::vector<std::pair<std::string, std::string>> &expected_fields =
          *efields;

      if (efields->size() > fields->size()) {
        logerror("%s[%d]:  bad sizes for expected fields for type %s\n", FILE__,
                 __LINE__, tn.c_str());
        logerror("%s[%d]:  got %d, expected %d\n", FILE__, __LINE__,
                 fields->size(), efields->size());
        return false;
      }

      bool err = false;
      for (unsigned int i = 0; i < expected_fields.size(); ++i) {
        if (fields->size() <= i) {
          logerror("%s[%d]:  cannot get %dth elem of %d size vec\n", FILE__,
                   __LINE__, i, fields->size());
          return false;
        }

        Field *f1 = (*fields)[i];
        std::string fieldname = f1->getName();
        std::string fieldtypename =
            f1->getType() ? f1->getType()->getName() : "";
        Type *ft = f1->getType();

        std::string expected_fieldname = (expected_fields.size() > i)
                                             ? expected_fields[i].second
                                             : std::string("range_error");
        std::string expected_fieldtypename = (expected_fields.size() > i)
                                                 ? expected_fields[i].first
                                                 : std::string("range_error");
        std::string alternate_fieldname = (afields && afields->size() > i)
                                              ? (*afields)[i].second
                                              : "range_error";
        std::string alternate_fieldtypename = (afields && afields->size() > i)
                                                  ? (*afields)[i].first
                                                  : "range_error";

        if (fieldtypename != expected_fieldtypename &&
            fieldtypename != alternate_fieldtypename) {
          logerror("%s[%d]:  Field type '%s', not expected '%s'\n", FILE__,
                   __LINE__, fieldtypename.c_str(), expected_fieldname.c_str());
          err = true;
        }

        if (fieldname != expected_fieldname &&
            fieldname != alternate_fieldname) {
          logerror("%s[%d]:  Field type '%s' not expected '%s'\n", FILE__,
                   __LINE__, fieldname.c_str(), expected_fieldtypename.c_str());
          err = true;
        }
      }

      if (err)
        return false;
    }
  }

  return true;
}

bool test_type_info_Mutator::verify_type_struct(
    typeStruct *t, std::vector<std::pair<std::string, std::string>> *ecomps,
    std::vector<std::pair<std::string, std::string>> *efields,
    std::vector<std::pair<std::string, std::string>> *afields) {
  got_type_struct = true;
  std::string &tn = t->getName();

  if (!verify_field_list(t, ecomps, efields, afields)) {
    logerror("%s[%d]:  verify struct %s failing\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  return true;
}

bool test_type_info_Mutator::verify_type_union(
    typeUnion *t, std::vector<std::pair<std::string, std::string>> *ecomps,
    std::vector<std::pair<std::string, std::string>> *efields) {
  got_type_union = true;
  std::string &tn = t->getName();

  if (!verify_field_list(t, ecomps, efields)) {
    logerror("%s[%d]:  verify union %s failing\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  return true;
}

bool test_type_info_Mutator::verify_type_scalar(typeScalar *t) {
  got_type_scalar = true;
  std::string &tn = t->getName();

  //  uh... nothing to do here....  (maybe check sizes??)

  return true;
}

bool test_type_info_Mutator::verify_type_typedef(typeTypedef *t,
                                                 std::string *tn_constituent) {
  got_type_typedef = true;
  std::string &tn = t->getName();

  Type *c = t->getConstituentType();
  if (!c) {
    logerror("%s[%d]:  NULL constituent type for type %s!\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  if (tn_constituent) {
    if (c->getName() != *tn_constituent) {
      logerror(
          "%s[%d]:  unexpected constituent type '%s' (not %s) for type %s!\n",
          FILE__, __LINE__, c->getName().c_str(), tn_constituent->c_str(),
          tn.c_str());
      return false;
    }
  }

  return true;
}

bool test_type_info_Mutator::verify_type(Type *t) {
  assert(t);
  std::string &tn = t->getName();

  if (!t->getID()) {
    logerror("%s[%d]:  type %s with zero id\n", FILE__, __LINE__, tn.c_str());
    return false;
  }

  if (0 == tn.length()) {
    logerror("%s[%d]:  unnamed %s type\n", FILE__, __LINE__,
             dataClass2Str(t->getDataClass()));
  }

  dataClass dc = t->getDataClass();

  if (dc == dataUnknownType) {
    logerror("%s[%d]:  type %s has bad data class\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  if (dc == dataNullType) {
    logerror("%s[%d]:  type %s has bad data class\n", FILE__, __LINE__,
             tn.c_str());
    return false;
  }

  if (t->getEnumType())
    return verify_type_enum(t->getEnumType());
  else if (t->getPointerType())
    return verify_type_pointer(t->getPointerType());
  else if (t->getFunctionType())
    return verify_type_function(t->getFunctionType());
  else if (t->getSubrangeType())
    return verify_type_subrange(t->getSubrangeType());
  else if (t->getArrayType())
    return verify_type_array(t->getArrayType());
  else if (t->getStructType())
    return verify_type_struct(t->getStructType());
  else if (t->getUnionType())
    return verify_type_union(t->getUnionType());
  else if (t->getScalarType())
    return verify_type_scalar(t->getScalarType());
  else if (t->getTypedefType())
    return verify_type_typedef(t->getTypedefType());
  else if (t->getCommonType()) {
    // common blocks are fortran only
    // we don't test that here yet
    logerror("%s[%d]:  weird, got common type\n", FILE__, __LINE__);
    return true;
  } else if (t->getRefType()) {
    //  references are c++ only
    // we don't test that here yet
    logerror("%s[%d]:  weird, got reference type\n", FILE__, __LINE__);
    return true;
  } else {
    logerror("%s[%d]: uknown type type for %s!\n", FILE__, __LINE__,
             tn.c_str());
  }
  return false;
}

bool test_type_info_Mutator::specific_type_tests() {
  Type *t = NULL;

  std::string tname = "enum1";
  if (!symtab->findType(t, tname) || (NULL == t)) {
    logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  typeEnum *te = t->getEnumType();
  if (!te) {
    logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  std::vector<std::pair<std::string, int>> expected_vals;
  expected_vals.push_back(
      std::pair<std::string, int>(std::string("ef1_1"), 20));
  expected_vals.push_back(
      std::pair<std::string, int>(std::string("ef1_2"), 40));
  expected_vals.push_back(
      std::pair<std::string, int>(std::string("ef1_3"), 60));
  expected_vals.push_back(
      std::pair<std::string, int>(std::string("ef1_4"), 80));
  if (!verify_type_enum(te, &expected_vals)) {
    logerror("%s[%d]:  could not find enum type %s\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  tname = "my_union";
  if (!symtab->findType(t, tname) || (NULL == t)) {
    logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  typeUnion *tu = t->getUnionType();
  if (!tu) {
    logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  std::vector<std::pair<std::string, std::string>> expected_union_fields;
  expected_union_fields.push_back(
      std::pair<std::string, std::string>("float", "my_float"));
  expected_union_fields.push_back(
      std::pair<std::string, std::string>("int", "my_int"));

  if (!verify_type_union(tu, NULL, &expected_union_fields)) {
    logerror("%s[%d]:  could not verify union\n", FILE__, __LINE__);
    return false;
  }

  tname = "mystruct";
  if (!symtab->findType(t, tname) || (NULL == t)) {
    logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  typeStruct *ts = t->getStructType();
  if (!ts) {
    logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
             tname.c_str());
    return false;
  }

  std::vector<std::pair<std::string, std::string>> expected_struct_fields;
  std::vector<std::pair<std::string, std::string>> alternate_struct_fields;
  expected_struct_fields.push_back(
      std::pair<std::string, std::string>("int", "elem1"));
  alternate_struct_fields.push_back(
      std::pair<std::string, std::string>("int", "elem1"));

  expected_struct_fields.push_back(
      std::pair<std::string, std::string>("double", "elem2"));
  alternate_struct_fields.push_back(
      std::pair<std::string, std::string>("double", "elem2"));
  expected_struct_fields.push_back(
      std::pair<std::string, std::string>("char", "elem3"));
  alternate_struct_fields.push_back(
      std::pair<std::string, std::string>("signed char", "elem3"));
  expected_struct_fields.push_back(
      std::pair<std::string, std::string>("float", "elem4"));
  alternate_struct_fields.push_back(
      std::pair<std::string, std::string>("float", "elem4"));

  if (!verify_type_struct(ts, NULL, &expected_struct_fields,
                          &alternate_struct_fields)) {
    logerror("[%s:%u] - Could not verify struct\n");
    return false;
  }
#if !defined(os_windows_test)
	tname = "int_alias_t";
	if (!symtab->findType(t, tname) || (NULL == t))
	{
		logerror( "%s[%d]:  could not find type %s\n", FILE__, __LINE__, tname.c_str());
		return false;
	}

	typeTypedef *ttd = t->getTypedefType();
	if (!ttd)
	{
		logerror( "%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__, tname.c_str());
		return false;
	}

	std::string expected_constituent_typename("int");
	if (!verify_type_typedef(ttd, &expected_constituent_typename)) 
		return false;

	tname = "int_array_t";
	if (!symtab->findType(t, tname) || (NULL == t))
	{
		logerror( "%s[%d]:  could not find type %s\n", FILE__, __LINE__, tname.c_str());
		return false;
	}

	Type *tc = NULL;
	typeTypedef *tt = t->getTypedefType();
	if (!tt)
	{
		//  Caveat:  Some compilers differ here in how they emit the stab
		//  for the typedef array...  while it would be nice to have a consistent representation
		//  but it would involve creating "fake" placeholder typedefs...  or just
		//  modifying the test to be OK with either scenario....
		tc = t->getArrayType();
		if (NULL == tc)
		{
			logerror( "%s[%d]:  %s: unexpected variety %s\n", 
					FILE__, __LINE__, tname.c_str(), t->specificType().c_str());
			return false;
		}
	}
	else
	{
		if (!verify_type_typedef(tt, NULL)) 
			return false;

		tc = tt->getConstituentType();
	}
	if (!tc)
	{
		logerror( "%s[%d]:  %s: no constituent type\n", FILE__, __LINE__, tname.c_str());
		return false;
	}
	//logerror( "%s[%d]:  typedef %s constituent typename: %s:%s/id %d, typedef id = %d\n", FILE__, __LINE__, tt->getName().c_str(), tc->specificType().c_str(), tc->getName().c_str(), tc->getID(), tt->getID());

	typeArray *ta = tc->getArrayType();
	if (!ta)
	{
		logerror( "%s[%d]:  %s: unexpected variety: %s--%s\n", FILE__, __LINE__, tname.c_str(), tc->specificType().c_str(), tc->getName().c_str());
		typeTypedef *ttd = tc->getTypedefType();
		if (ttd)
		{
			Type *ttd_c = ttd->getConstituentType();
			logerror( "%s[%d]:  typedef constituent %s--%s\n", FILE__, __LINE__, ttd_c->getName().c_str(), ttd_c->specificType().c_str());
					}
		return false;
	}

	std::string expected_array_base = "int";
	int expected_low = 0;
	int expected_hi = 255;
	if (!verify_type_array(ta, &expected_low, &expected_hi, &expected_array_base)) 
	{
		logerror( "%s[%d]: failed to verify typeArray\n", FILE__, __LINE__);
		return false;
	}

	tname = "my_intptr_t";
	if (!symtab->findType(t, tname) || (NULL == t))
	{
		logerror( "%s[%d]:  could not find type %s\n",
				FILE__, __LINE__, tname.c_str());
		return false;
	}

	tt = t->getTypedefType();
	if (!tt)
	{
		logerror( "%s[%d]:  %s: unexpected variety\n",
				FILE__, __LINE__, tname.c_str());
		return false;
	}

	if (!verify_type_typedef(tt, NULL))
		return false;

	tc = tt->getConstituentType();
	if (!tc)
	{
		logerror( "%s[%d]:  %s: no constituent type\n",
				FILE__, __LINE__, tname.c_str());
		return false;
	}

	typePointer *tp = tc->getPointerType();
	if (!tp)
	{
		logerror( "%s[%d]:  %s: unexpected variety: %s\n",
				FILE__, __LINE__, tname.c_str(), dataClass2Str(tc->getDataClass()));
		return false;
	}

	std::string expected_pointer_base = "int";
	if (!verify_type_pointer(tp, &expected_pointer_base))
		return false;
#endif

  /* ----  Check DWARF encodings for base types ---- */
  // NB: We are limited to just C types because the mutatee is written in C

  // Integral types
  {
    tname = "integral_t";
    if (!symtab->findType(t, tname) || !t) {
      logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    typeStruct *ts = t->getStructType();
    if (!ts) {
      logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    using prop_t = SymtabAPI::typeScalar::properties_t;
    constexpr auto expected_num_fields = 6UL;
    // clang-format off
    auto check = [ts]() {
      std::array<std::function<bool(prop_t const&)>, expected_num_fields> checks = {{
        [](prop_t const& p) { return p.is_integral && p.is_boolean; },
        [](prop_t const& p) { return p.is_integral && (p.is_signed_char || p.is_unsigned_char); },
        [](prop_t const& p) { return p.is_integral && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && !p.is_floating_point && !p.is_string; }
      }};
      // clang-format on
      auto const &fields = *ts->getFields();
      if (fields.size() != expected_num_fields)
        return false;

      for (size_t i = 0; i < expected_num_fields; i++) {
        auto const &p = fields[i]->getType()->getScalarType()->properties();
        if (!checks[i](p))
          return false;
      }
      return true;
    }();
    if (!check) {
      logerror("%s[%d]:  %s: Found incorrect encodings\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }
  }

  // Signed Integral types
  {
    tname = "signed_integral_t";
    if (!symtab->findType(t, tname) || !t) {
      logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    typeStruct *ts = t->getStructType();
    if (!ts) {
      logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    using prop_t = SymtabAPI::typeScalar::properties_t;
    constexpr auto expected_num_fields = 5UL;
    // clang-format off
    auto check = [ts]() {
      std::array<std::function<bool(prop_t const&)>, expected_num_fields> checks = {{
        [](prop_t const& p) { return p.is_integral && p.is_signed_char && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_signed && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_signed && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_signed && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_signed && !p.is_floating_point && !p.is_string; }
      }};
      // clang-format on
      auto const &fields = *ts->getFields();
      if (fields.size() != expected_num_fields)
        return false;

      for (size_t i = 0; i < expected_num_fields; i++) {
        auto const &p = fields[i]->getType()->getScalarType()->properties();
        if (!checks[i](p))
          return false;
      }
      return true;
    }();
    if (!check) {
      logerror("%s[%d]:  %s: Found incorrect encodings\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }
  }

  // Unsigned Integral types
  {
    tname = "unsigned_integral_t";
    if (!symtab->findType(t, tname) || !t) {
      logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    typeStruct *ts = t->getStructType();
    if (!ts) {
      logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    using prop_t = SymtabAPI::typeScalar::properties_t;
    constexpr auto expected_num_fields = 5UL;
    // clang-format off
    auto check = [ts]() {
      std::array<std::function<bool(prop_t const&)>, expected_num_fields> checks = {{
        [](prop_t const& p) { return p.is_integral && p.is_unsigned_char && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_unsigned && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_unsigned && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_unsigned && !p.is_floating_point && !p.is_string; },
        [](prop_t const& p) { return p.is_integral && p.is_unsigned && !p.is_floating_point && !p.is_string; }
      }};
      // clang-format on
      auto const &fields = *ts->getFields();
      if (fields.size() != expected_num_fields)
        return false;

      for (size_t i = 0; i < expected_num_fields; i++) {
        auto const &p = fields[i]->getType()->getScalarType()->properties();
        if (!checks[i](p))
          return false;
      }
      return true;
    }();
    if (!check) {
      logerror("%s[%d]:  %s: Found incorrect encodings\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }
  }

  // Floating point types
  {
    tname = "floating_point_t";
    if (!symtab->findType(t, tname) || !t) {
      logerror("%s[%d]:  could not find type %s\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    typeStruct *ts = t->getStructType();
    if (!ts) {
      logerror("%s[%d]:  %s: unexpected variety\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }

    using prop_t = SymtabAPI::typeScalar::properties_t;
    constexpr auto expected_num_fields = 6UL;
    // clang-format off
    auto check = [ts]() {
      std::array<std::function<bool(prop_t const&)>, expected_num_fields> checks = {{
        [](prop_t const& p) { return p.is_floating_point && !p.is_integral && !p.is_string; },
        [](prop_t const& p) { return p.is_floating_point && !p.is_integral && !p.is_string; },
        [](prop_t const& p) { return p.is_floating_point && !p.is_integral && !p.is_string; },
        [](prop_t const& p) { return p.is_floating_point && p.is_complex_float && !p.is_integral && !p.is_string; },
        [](prop_t const& p) { return p.is_floating_point && p.is_complex_float && !p.is_integral && !p.is_string; },
        [](prop_t const& p) { return p.is_floating_point && p.is_complex_float && !p.is_integral && !p.is_string; }
      }};
      // clang-format on
      auto const &fields = *ts->getFields();
      if (fields.size() != expected_num_fields)
        return false;

      for (size_t i = 0; i < expected_num_fields; i++) {
        auto const &p = fields[i]->getType()->getScalarType()->properties();
        if (!checks[i](p))
          return false;
      }
      return true;
    }();
    if (!check) {
      logerror("%s[%d]:  %s: Found incorrect encodings\n", FILE__, __LINE__,
               tname.c_str());
      return false;
    }
  }

  return true;
}

test_results_t test_type_info_Mutator::verify_basic_type_lists() {
  std_types = symtab->getAllstdTypes();
  builtin_types = symtab->getAllbuiltInTypes();

  if (!std_types || !std_types->size()) {
    logerror("[%s:%u] - Unable to find std types\n", __FILE__, __LINE__);
    return FAILED;
  }

  if (!builtin_types || !builtin_types->size()) {
    logerror("[%s:%u] - Unable to find std types\n", __FILE__, __LINE__);
    return FAILED;
  }

  for (unsigned int i = 0; i < std_types->size(); ++i) {
    Type *t = (*std_types)[i];
    if (!t) {
      logerror("%s[%d]:  NULL type returned to user\n", FILE__, __LINE__);
      return FAILED;
    }

    if (!verify_type(t)) {
      logerror("%s[%d]:  failing due to bad type\n", FILE__, __LINE__);
      return FAILED;
    }
  }

  for (unsigned int i = 0; i < builtin_types->size(); ++i) {
    Type *t = (*builtin_types)[i];
    if (!t) {
      logerror("%s[%d]:  NULL type returned to user\n", FILE__, __LINE__);
      return FAILED;
    }

    if (!verify_type(t)) {
      logerror("%s[%d]:  failing due to bad type\n", FILE__, __LINE__);
      return FAILED;
    }
  }

  std::vector<SymtabAPI::Module *> mods;
  bool result = symtab->getAllModules(mods);

  if (!result || !mods.size()) {
    logerror("%s[%d]: Unable to getAllModules\n", FILE__, __LINE__);
    return FAILED;
  }

  for (unsigned int i = 0; i < mods.size(); ++i) {
    std::vector<Type *> *modtypes = mods[i]->getAllTypes();

    if (!modtypes) {
      //  we try to look at all modules that have types
      //  but not all do
      //  Only fail if the module is one of ours

      if (mods[i]->fileName() == std::string("mutatee_util.c") ||
          (mods[i]->fileName() == std::string("solo_mutatee_boilerplate.c")) ||
          (mods[i]->fileName() == std::string("mutatee_driver.c"))) {
        logerror("%s[%d]:  module %s has no types\n", FILE__, __LINE__,
                 mods[i]->fileName().c_str());

        return FAILED;
      } else
        continue;
    }

    for (unsigned int j = 0; j < modtypes->size(); ++j) {
      Type *t = (*modtypes)[j];
      if (!t) {
        logerror("%s[%d]:  NULL type returned to user\n", FILE__, __LINE__);
        return FAILED;
      }

      if (!verify_type(t)) {
        logerror("%s[%d]:  failing due to bad type\n", FILE__, __LINE__);
        return FAILED;
      }
    }
  }

  if (!specific_type_tests()) {
    logerror("%s[%d]:  specific type test failed... \n", FILE__, __LINE__);
    return FAILED;
  }

  if (!got_all_types()) {
    logerror("%s[%d]:  did not test all types...  failing\n", FILE__, __LINE__);
    return FAILED;
  }

  return PASSED;
}

test_results_t test_type_info_Mutator::executeTest() {
  SymtabAPI::Module *mod = NULL;
  std::vector<SymtabAPI::Module *> mods;

  execname = symtab->name();

  if (!symtab->getAllModules(mods)) {
    logerror("%s[%d]:  failed to get all modules\n", FILE__, __LINE__);
    return FAILED;
  }

  for (unsigned int i = 0; i < mods.size(); ++i) {
    std::string mname = mods[i]->fileName();
    if (!strncmp("solo_mutatee", mname.c_str(), strlen("solo_mutatee")) ||
        !strncmp("test_type_info_mutatee", mname.c_str(),
                 strlen("test_type_info_mutatee"))) {
      if (mod)
        logerror("%s[%d]:  FIXME\n", FILE__, __LINE__);
      mod = mods[i];
    }
  }

  if (!mod) {
    logerror("%s[%d]:  failed to find module\n", FILE__, __LINE__);
    return FAILED;
  }

  lang = mod->language();
  test_results_t ret = verify_basic_type_lists();
  return ret;
}
