TEST_F(UnitsFixture,QuantityConverter_OSQuantityVector) {
    // basic conversion
    OSQuantityVector testVec(createIPEnergy(),2u,100.0);
    Quantity testQ(100.0,createIPEnergy());

    OSQuantityVector resultVec = convert(testVec,UnitSystem(UnitSystem::Wh));
    OptionalQuantity resultQ = convert(testQ,UnitSystem(UnitSystem::Wh));
    ASSERT_EQ(2u,resultVec.size());
    ASSERT_TRUE(resultQ);
    EXPECT_EQ(resultQ.get(),resultVec.getQuantity(0));
    EXPECT_EQ(resultQ.get(),resultVec.getQuantity(1));
    EXPECT_EQ(resultQ->system(),resultVec.system());

    resultVec = convert(testVec,resultQ->units());
    resultQ = convert(testQ,resultQ->units());
    ASSERT_EQ(2u,resultVec.size());
    ASSERT_TRUE(resultQ);
    EXPECT_EQ(resultQ.get(),resultVec.getQuantity(0));
    EXPECT_EQ(resultQ.get(),resultVec.getQuantity(1));
    EXPECT_EQ(resultQ->system(),resultVec.system());

    // temperature conversion
    testVec = OSQuantityVector(createCelsiusTemperature(),2u,20.0);
    testQ = Quantity(20.0,createCelsiusTemperature());
    resultVec = convert(testVec,UnitSystem(UnitSystem::Fahrenheit));
    resultQ = convert(testQ,UnitSystem(UnitSystem::Fahrenheit));
    ASSERT_EQ(2u,resultVec.size());
    ASSERT_TRUE(resultQ);
    EXPECT_EQ(resultQ.get().units(),resultVec.units());
    // not sure why these aren't quite getting the precision we would like
    EXPECT_NEAR(resultQ.get().value(),resultVec.getQuantity(0).value(),1.0E-12);
    EXPECT_NEAR(resultQ.get().value(),resultVec.getQuantity(1).value(),1.0E-12);
    EXPECT_EQ(resultQ->system(),resultVec.system());
    EXPECT_TRUE(resultVec.isAbsolute());
    EXPECT_TRUE(resultQ->isAbsolute());

    testVec.setAsRelative();
    testQ.setAsRelative();
    resultVec = convert(testVec,UnitSystem(UnitSystem::Fahrenheit));
    resultQ = convert(testQ,UnitSystem(UnitSystem::Fahrenheit));
    ASSERT_EQ(2u,resultVec.size());
    ASSERT_TRUE(resultQ);
    EXPECT_EQ(resultQ.get().units(),resultVec.units());
    // not sure why these aren't quite getting the precision we would like
    EXPECT_NEAR(resultQ.get().value(),resultVec.getQuantity(0).value(),1.0E-12);
    EXPECT_NEAR(resultQ.get().value(),resultVec.getQuantity(1).value(),1.0E-12);
    EXPECT_EQ(resultQ->system(),resultVec.system());
    EXPECT_TRUE(resultVec.isRelative());
    EXPECT_TRUE(resultQ->isRelative());
}
TEST_F(IdfFixture, IdfObject_GetQuantity)
{
    std::string text = "Building,                !- Building \n\
                      Building,                !- Name \n\
                      30.,                     !- North Axis {deg} \n\
                      City,                    !- Terrain \n\
                      0.04,                    !- Loads Convergence Tolerance Value \n\
                      0.4,                     !- Temperature Convergence Tolerance Value {deltaC} \n\
                      FullExterior,            !- Solar Distribution \n\
                      25;                      !- Maximum Number of Warmup Days";

  // make an idf object
  OptionalIdfObject oObj = IdfObject::load(text);
  ASSERT_TRUE(oObj);

  // Test get.
  OSOptionalQuantity ooq = oObj->getQuantity (4);
  ASSERT_TRUE(ooq.isSet());
  Quantity q = ooq.get();
  EXPECT_TRUE(q.value() == 0.4);
  EXPECT_TRUE(q.system() == UnitSystem::SI);
  EXPECT_TRUE(q.standardUnitsString() == "K");

  // Test set.
  OptionalQuantity oq = convert(q,UnitSystem(UnitSystem::IP));
  ASSERT_TRUE(oq);
  EXPECT_TRUE(oq->system() == UnitSystem::IP);
  EXPECT_DOUBLE_EQ(0.72,oq->value());
  oq->setValue(1.5);

  EXPECT_TRUE(oObj->setQuantity(4, *oq));
  ooq = oObj->getQuantity(4);
  ASSERT_TRUE(ooq.isSet());
  q = ooq.get();
  EXPECT_DOUBLE_EQ(0.83333333333333333,q.value());
  EXPECT_TRUE(q.system() == UnitSystem::SI);
  EXPECT_TRUE(q.standardUnitsString() == "K");
}
TEST_F(IddFixture,IddFactory_Units) {
    std::vector<boost::regex> unsupported;
    unsupported.push_back(boost::regex("\\$"));
    unsupported.push_back(boost::regex("eV"));
    unsupported.push_back(boost::regex("hh:mm"));
    unsupported.push_back(boost::regex("percent"));
    unsupported.push_back(boost::regex("ppm"));

    IddObjectVector objects = IddFactory::instance().getObjects(IddFileType(IddFileType::WholeFactory));
    StringSet goodUnits;
    StringSet badUnits;
    for (const IddObject& object : objects) {
        IddFieldVector fields = object.nonextensibleFields();
        IddFieldVector temp = object.extensibleGroup();
        fields.insert(fields.end(),temp.begin(),temp.end());
        for (const IddField& field : fields) {
            OptionalString iddUnits = field.properties().units;
            OptionalUnit siUnit;
            if (iddUnits) {

                // check if already tested units
                if (goodUnits.find(*iddUnits) != goodUnits.end()) {
                    continue;
                }
                if (badUnits.find(*iddUnits) != badUnits.end()) {
                    continue;
                }

                // screen for unsupported units
                for (const boost::regex& re : unsupported) {
                    if (boost::regex_search(*iddUnits,re)) {
                        iddUnits = boost::none;
                        break;
                    }
                }
                if (!iddUnits) {
                    continue;
                }

                siUnit = field.getUnits(false);
                EXPECT_TRUE(siUnit || field.unitsBasedOnOtherField()) << object.name() << " field: " << field.name();
                if (siUnit) {
                    // could just return junk unit. if not junk, quantity will be convertible
                    // to UnitSystem::SI.
                    Quantity q(1.0,*siUnit);
                    OptionalQuantity testQ = convert(q,UnitSystem(UnitSystem::SI));
                    EXPECT_TRUE(testQ) << "Unable to convert unit '" << *iddUnits << "' to SI for field '"
                                       << field.name() << "' in IddObject '" << object.name() << "'.";
                    if (testQ) {
                        goodUnits.insert(*iddUnits);
                        EXPECT_TRUE(testQ->system() == UnitSystem::SI);
                    } else {
                        badUnits.insert(*iddUnits);
                        LOG(Debug,"Unable to convert unit '" << *iddUnits << "' to SI for field '"
                            << field.name() << "' in IddObject '" << object.name() << "'.");
                    }
                }
                else if (field.unitsBasedOnOtherField()) {
                    goodUnits.insert(*iddUnits);
                    continue;
                }
                else {
                    badUnits.insert(*iddUnits);
                    LOG(Debug,"Unable to instantiate unit '" << *iddUnits << "' for field '"
                        << field.name() << "' in IddObject '" << object.name() << "'.");
                    continue;
                }
                OptionalUnit ipUnit = field.getUnits(true);
                EXPECT_TRUE(ipUnit);
                if (ipUnit) {
                    // could just return junk unit. if not junk, quantity will be convertable
                    // to *siUnit or UnitSystem::SI.
                    Quantity q(1.0,*ipUnit);
                    OptionalQuantity testQ;
                    if (siUnit) {
                        testQ = convert(q,*siUnit);
                    }
                    else {
                        testQ = convert(q,UnitSystem(UnitSystem::SI));
                    }
                    EXPECT_TRUE(testQ);
                    if (testQ) {
                        goodUnits.insert(*iddUnits);
                    }
                    else {
                        badUnits.insert(ipUnit->standardString());
                        LOG(Debug,"Unable to convert unit " << *ipUnit << " to IDD/SI units " << *siUnit
                            << " for field '" << field.name() << "' in IddObject '" << object.name() << "'.");
                    }
                }
                else {
                    badUnits.insert(*iddUnits);
                    LOG(Debug,"Unable to instantiate ipUnit for field " << field.name() << " in IddObject "
                        << object.name() << ", which has units " << *siUnit << ".");
                }
            }
        }
    }
    LOG(Info,"IddUnitStrings not handled properly by the Factories and Converter:");
    for (const std::string& str : badUnits) {
        LOG(Info,"  " << str);
    }
    LOG(Info,"IddUnitStrings handled properly by the Factories and Converter:");
    for (const std::string& str : goodUnits) {
        LOG(Info,"  " << str);
    }
}