TEST_F(AnalysisFixture, Problem_Constructors) {
  VariableVector variables;
  MeasureVector measures;
  runmanager::Workflow workflow;

  // almost-default constructor
  Problem problem("Problem",variables,workflow);
  EXPECT_EQ(0,problem.numVariables());
  OptionalInt combinatorialSize = problem.combinatorialSize(true);
  ASSERT_TRUE(combinatorialSize);
  EXPECT_EQ(0,*combinatorialSize);

  // variables with consistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  openstudio::path rubyScriptPath = toPath(rubyLibDir()) /
                                    toPath("openstudio/runmanager/rubyscripts/PerturbObject.rb");
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Variable 1",measures));
  measures.clear();
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 2",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 3",measures));
  problem = Problem("Problem",variables,workflow);
  EXPECT_EQ(3,problem.numVariables());
  EXPECT_EQ(6,problem.combinatorialSize(true).get());

  // variables with inconistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::OSM,
                                           FileReferenceType::OSM));
  variables.push_back(MeasureGroup("Variable 1",measures));
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 2",measures));
  EXPECT_THROW(Problem("Problem",variables,workflow),std::exception);

  // variables and non-null workflow with consistent file types
  variables.clear();
  measures.clear();
  measures.push_back(NullMeasure());
  measures.push_back(RubyMeasure(rubyScriptPath,
                                           FileReferenceType::IDF,
                                           FileReferenceType::IDF));
  variables.push_back(MeasureGroup("Variable 1",measures));
  workflow = runmanager::Workflow();
  workflow.addJob(openstudio::runmanager::JobType::EnergyPlus);
  problem = Problem("Problem",variables,workflow);
  problem.setDisplayName("Display Name");
  problem.setDescription("long winded description");
  EXPECT_EQ(1,problem.numVariables());
  EXPECT_EQ(2,problem.combinatorialSize(true).get());

  // deserialization
  Problem problemCopy(problem.uuid(),
                      problem.versionUUID(),
                      problem.name(),
                      problem.displayName(),
                      problem.description(),
                      problem.workflow(),
                      problem.responses());
  EXPECT_FALSE(problem == problemCopy); // different impls
  EXPECT_TRUE(problem.uuid() == problemCopy.uuid());
  EXPECT_TRUE(problem.versionUUID() == problemCopy.versionUUID());
  EXPECT_EQ(problem.name(),problemCopy.name());
  EXPECT_EQ(problem.displayName(),problemCopy.displayName());
  EXPECT_EQ(problem.description(),problemCopy.description());
  EXPECT_TRUE(problem.workflow() == problemCopy.workflow());

  // variables and non-null workflow with inconsistent file types
  workflow = runmanager::Workflow();
  workflow.addJob(openstudio::runmanager::JobType::ModelToIdf);
  workflow.addJob(openstudio::runmanager::JobType::EnergyPlus);
  EXPECT_THROW(Problem("Problem",variables,workflow),std::exception);
}
TEST_F(AnalysisFixture, Problem_FileTypesAreCompatible_NewMeasureGroup) {
  // ok to insert null-only discrete variable anywhere in the chain
  // (between null-model, model-model, idf-idf, idf-workflow)
  VariableVector variables;
  // 0
  variables.push_back(
        MeasureGroup("Null Variable 1",
                     MeasureVector(1u,NullMeasure())));
  // 1
  variables.push_back(
        MeasureGroup("Model Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("modelUserScript.rb"),
                                                                        FileReferenceType::OSM,
                                                                        FileReferenceType::OSM,
                                                                        true))));
  // 2
  variables.push_back(
        MeasureGroup("Translation Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("translationUserScript.rb"),
                                                                        FileReferenceType::OSM,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  // 3
  variables.push_back(
        MeasureGroup("Workspace Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  runmanager::Workflow workflow;
  workflow.addJob(runmanager::JobType::EnergyPlus);
  Problem problem("Problem 1",variables,workflow);
  MeasureGroup newVar("New Discrete Variable",
                          MeasureVector(1u,NullMeasure()));
  EXPECT_TRUE(problem.insert(4,newVar.clone().cast<InputVariable>()));
  ASSERT_EQ(5,problem.numVariables());
  EXPECT_EQ("New Discrete Variable",problem.variables()[4].name());
  EXPECT_TRUE(problem.insert(3,newVar.clone().cast<InputVariable>()));
  EXPECT_EQ(6,problem.numVariables());
  ASSERT_GE(problem.numVariables(),5);
  EXPECT_EQ("Translation Variable 1",problem.variables()[2].name());
  EXPECT_EQ("New Discrete Variable",problem.variables()[3].name());
  EXPECT_EQ("Workspace Variable 1",problem.variables()[4].name());
  EXPECT_TRUE(problem.insert(2,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(1,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(0,newVar.clone().cast<InputVariable>()));
  EXPECT_EQ(9,problem.numVariables());
  variables = castVector<Variable>(problem.variables());
  for (int i = 0, n = problem.numVariables(); i < n; ++i) {
    if (i % 2 == 0) {
      EXPECT_EQ("New Discrete Variable",variables[i].name());
    }
    else {
      EXPECT_NE("New Discrete Variable",variables[i].name());
    }
  }

  // (between null-idf, idf-idf, idf-workflow)
  variables.clear();
  // 0
  variables.push_back(
        MeasureGroup("Null Variable 1",
                         MeasureVector(1u,NullMeasure())));
  // 1
  variables.push_back(
        MeasureGroup("Workspace Variable 1",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  // 2
  variables.push_back(
        MeasureGroup("Workspace Variable 2",
                         MeasureVector(1u,RubyMeasure(toPath("workspaceUserScript.rb"),
                                                                        FileReferenceType::IDF,
                                                                        FileReferenceType::IDF,
                                                                        true))));
  problem = Problem("Problem 2",variables,workflow);
  EXPECT_TRUE(problem.insert(3,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(2,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(1,newVar.clone().cast<InputVariable>()));
  EXPECT_TRUE(problem.insert(0,newVar.clone().cast<InputVariable>()));
  EXPECT_EQ(7,problem.numVariables());
  variables = castVector<Variable>(problem.variables());
  for (int i = 0, n = problem.numVariables(); i < n; ++i) {
    if (i % 2 == 0) {
      EXPECT_EQ("New Discrete Variable",variables[i].name());
    }
    else {
      EXPECT_NE("New Discrete Variable",variables[i].name());
    }
  }

  // expected behavior for then adding first measure
  // test with BCLMeasure first. verify with RubyMeasure.
  openstudio::path dir = resourcesPath() / toPath("/utilities/BCL/Measures/v2/SetWindowToWallRatioByFacade");
  BCLMeasure bclMeasure = BCLMeasure::load(dir).get();
  EXPECT_EQ(FileReferenceType(FileReferenceType::OSM),bclMeasure.inputFileType());
  EXPECT_EQ(FileReferenceType(FileReferenceType::OSM),bclMeasure.outputFileType());
  RubyMeasure measure(bclMeasure);

  // have idf-only problem with two null variables at the top
  // try to add measures to first variable
  MeasureGroup firstVariable = problem.variables()[0].cast<MeasureGroup>();
  ASSERT_TRUE(firstVariable.parent());
  WorkflowStep step0 = problem.workflow()[0];
  EXPECT_TRUE(firstVariable.parent().get() == step0);
  EXPECT_EQ(1u,firstVariable.numMeasures(false));
  EXPECT_FALSE(problem.fileTypesAreCompatible(firstVariable,
                                              measure.inputFileType(),
                                              measure.outputFileType()));
  ASSERT_TRUE(firstVariable.parent());
  EXPECT_TRUE(firstVariable.parent().get() == problem.workflow()[0]);
  EXPECT_TRUE(problem.workflow()[0] == step0);
  EXPECT_FALSE(firstVariable.push(measure));
  EXPECT_EQ(1u,firstVariable.numMeasures(false));

  // make second variable a translation variable
  MeasureGroup secondVariable = problem.variables()[1].cast<MeasureGroup>();
  EXPECT_FALSE(secondVariable.push(RubyMeasure(toPath("myTranslationScript.rb"),
                                                    FileReferenceType::OSM,
                                                    FileReferenceType::IDF)));
  secondVariable.erase(secondVariable.measures(false)[0]);
  EXPECT_TRUE(secondVariable.push(RubyMeasure(toPath("myTranslationScript.rb"),
                                                   FileReferenceType::OSM,
                                                   FileReferenceType::IDF)));

  // now should be able to add OSM measure
  EXPECT_TRUE(problem.fileTypesAreCompatible(firstVariable,
                                             measure.inputFileType(),
                                             measure.outputFileType()));
  EXPECT_TRUE(firstVariable.push(measure));
  EXPECT_EQ(2u,firstVariable.numMeasures(false));
}