// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "paddle/fluid/framework/ir/is_test_pass.h"

#include <gtest/gtest.h>
#ifdef _WIN32
#undef FALSE
#undef TRUE
#endif
namespace paddle {
namespace framework {
namespace ir {

enum class ISTEST_STATE { FALSE, TRUE, UNSET };

void SetOp(ProgramDesc* prog, const std::string& type, const std::string& name,
           const std::vector<std::string>& inputs,
           const std::vector<std::string>& outputs, bool use_mkldnn = false,
           ISTEST_STATE is_test = ISTEST_STATE::UNSET) {
  auto* op = prog->MutableBlock(0)->AppendOp();
  op->SetType(type);
  op->SetAttr("name", name);
  op->SetInput("X", inputs);
  op->SetOutput("Out", outputs);
  op->SetAttr("use_mkldnn", use_mkldnn);
  if (is_test == ISTEST_STATE::UNSET)
    op->MutableAttrMap()->erase("is_test");
  else if (is_test == ISTEST_STATE::FALSE)
    op->SetAttr("is_test", false);
  else
    op->SetAttr("is_test", true);
}

// a->pool2d->b
// b->relu->c
// c,weights1)->conv2d->d
//
// d->pool2d->e
// e->hard_sigmoid->f
// (f,weights2)->conv2d->g
//
// g->pool2d->h
// h->tanh->i
// (i,weights3)->conv2d->j
ProgramDesc BuildProgramDesc() {
  ProgramDesc prog;
  for (auto& v :
       std::vector<std::string>({"a", "b", "c", "d", "e", "f", "g", "h", "i",
                                 "j", "weights1", "weights2", "weights3"})) {
    auto* var = prog.MutableBlock(0)->Var(v);
    var->SetType(proto::VarType::SELECTED_ROWS);
    if (v == "weights1" || v == "weights2" || v == "weights3") {
      var->SetPersistable(true);
    }
  }

  SetOp(&prog, "pool2d", "pooling1", std::vector<std::string>({"a"}),
        std::vector<std::string>({"b"}), true, ISTEST_STATE::TRUE);
  SetOp(&prog, "relu", "activation1", std::vector<std::string>({"b"}),
        std::vector<std::string>({"c"}), true, ISTEST_STATE::TRUE);
  SetOp(&prog, "conv2d", "conv1", std::vector<std::string>({"c", "weights1"}),
        std::vector<std::string>({"d"}), true, ISTEST_STATE::TRUE);

  SetOp(&prog, "pool2d", "pooling2", std::vector<std::string>({"d"}),
        std::vector<std::string>({"e"}), false, ISTEST_STATE::FALSE);
  SetOp(&prog, "hard_sigmoid", "activation2", std::vector<std::string>({"e"}),
        std::vector<std::string>({"f"}), false, ISTEST_STATE::FALSE);
  SetOp(&prog, "conv2d", "conv2", std::vector<std::string>({"f", "weights2"}),
        std::vector<std::string>({"g"}), false, ISTEST_STATE::FALSE);

  SetOp(&prog, "pool2d", "pooling3", std::vector<std::string>({"g"}),
        std::vector<std::string>({"h"}), false, ISTEST_STATE::UNSET);
  SetOp(&prog, "tanh", "activation3", std::vector<std::string>({"h"}),
        std::vector<std::string>({"i"}), true, ISTEST_STATE::UNSET);
  SetOp(&prog, "conv2d", "conv3", std::vector<std::string>({"i", "weights3"}),
        std::vector<std::string>({"j"}), false, ISTEST_STATE::UNSET);

  return prog;
}

TEST(IsTestPass, basic) {
  auto prog = BuildProgramDesc();

  std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));

  auto pass = PassRegistry::Instance().Get("is_test_pass");

  graph.reset(pass->Apply(graph.release()));

  for (auto* node : graph->Nodes()) {
    if (node->IsOp()) {
      auto* op = node->Op();
      auto op_name = boost::get<std::string>(op->GetAttr("name"));
      if (op_name == "conv3") {
        ASSERT_FALSE(op->HasAttr("is_test"));
      } else {
        ASSERT_TRUE(op->HasAttr("is_test"));
        EXPECT_TRUE(boost::get<bool>(op->GetAttr("is_test")));
      }
    }
  }
}

}  // namespace ir
}  // namespace framework
}  // namespace paddle

USE_PASS(is_test_pass);
