Home | History | Annotate | Download | only in c
      1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 
     16 #include "tensorflow/c/c_api.h"
     17 
     18 #include "tensorflow/c/c_test_util.h"
     19 #include "tensorflow/core/framework/function.pb.h"
     20 #include "tensorflow/core/framework/op_def.pb.h"
     21 #include "tensorflow/core/lib/core/status.h"
     22 #include "tensorflow/core/lib/hash/hash.h"
     23 #include "tensorflow/core/lib/strings/str_util.h"
     24 #include "tensorflow/core/lib/strings/strcat.h"
     25 #include "tensorflow/core/platform/logging.h"
     26 #include "tensorflow/core/platform/test.h"
     27 
     28 namespace tensorflow {
     29 namespace {
     30 
     31 // Specification for expected input/output and its type.
     32 // DataType value of DT_INVALID signifies that we don't want to
     33 // check the data type.
     34 typedef std::pair<string, DataType> IOSpec;
     35 
     36 std::vector<IOSpec> M(const std::initializer_list<string>& names) {
     37   std::vector<IOSpec> v;
     38   for (const string& name : names) {
     39     v.push_back(IOSpec(name, DT_INVALID));
     40   }
     41   return v;
     42 }
     43 
     44 // Specification for an expected edge.
     45 // src is either:
     46 // - input name (as it appears in FunctionDef)
     47 // - name of output tensor (in nested "add:z:0" format)
     48 // dst is either:
     49 // - output name (as it appears in FunctionDef)
     50 // - <name_of_node>:<index_of_this_input_into_node> (this looks the same as
     51 //      output tensor naming, but it the index is actually an input index)
     52 struct EdgeSpec : public std::pair<string, string> {
     53   typedef std::pair<string, string> Base;
     54 
     55   // Inherit the set of constructors
     56   using Base::pair;
     57 
     58   string ToString() const { return strings::StrCat(first, "->", second); }
     59 };
     60 
     61 class CApiFunctionTest : public ::testing::Test {
     62  protected:
     63   CApiFunctionTest()
     64       : s_(TF_NewStatus()),
     65         func_graph_(TF_NewGraph()),
     66         host_graph_(TF_NewGraph()),
     67         func_(nullptr) {}
     68 
     69   void SetUp() override {}
     70 
     71   ~CApiFunctionTest() override {
     72     TF_DeleteFunction(func_);
     73     TF_DeleteGraph(host_graph_);
     74     TF_DeleteGraph(func_graph_);
     75     TF_DeleteStatus(s_);
     76   }
     77 
     78   void Run(const std::vector<std::pair<TF_Operation*, TF_Tensor*>>& inputs,
     79            TF_Operation* output, int32_t expected_result) {
     80     Run(inputs, {{output, 0}}, {expected_result});
     81   }
     82 
     83   // Run the host graph, which now contains a function and check that
     84   // outputs are as expected.
     85   // 'T' stands for 'tensor' since the outputs are tensors, not scalars.
     86   void RunT(const std::vector<std::pair<TF_Operation*, TF_Tensor*>>& inputs,
     87             std::initializer_list<TF_Output> outputs,
     88             const std::vector<std::vector<int32_t>>& expected_results) {
     89     // Create a session for this graph
     90     CSession csession(host_graph_, s_);
     91     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
     92 
     93     // Run
     94     csession.SetInputs(inputs);
     95     csession.SetOutputs(outputs);
     96     csession.Run(s_);
     97     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
     98 
     99     // Check results
    100     for (int i = 0; i < expected_results.size(); ++i) {
    101       TF_Tensor* out = csession.output_tensor(i);
    102       ASSERT_TRUE(out != nullptr);
    103       EXPECT_EQ(TF_INT32, TF_TensorType(out));
    104       EXPECT_EQ(1, TF_NumDims(out));
    105       CompareInt32Tensor(expected_results[i], out);
    106     }
    107   }
    108 
    109   // Run the host graph, which now contains a function and check that
    110   // outputs are as expected.
    111   void Run(const std::vector<std::pair<TF_Operation*, TF_Tensor*>>& inputs,
    112            std::initializer_list<TF_Output> outputs,
    113            const std::vector<int32_t>& expected_results) {
    114     // Create a session for this graph.
    115     CSession csession(host_graph_, s_);
    116     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    117 
    118     csession.SetInputs(inputs);
    119     csession.SetOutputs(outputs);
    120     csession.Run(s_);
    121     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    122 
    123     for (int i = 0; i < expected_results.size(); ++i) {
    124       TF_Tensor* out = csession.output_tensor(i);
    125       ASSERT_TRUE(out != nullptr);
    126       EXPECT_EQ(TF_INT32, TF_TensorType(out));
    127       EXPECT_EQ(0, TF_NumDims(out));  // scalar
    128       ASSERT_EQ(sizeof(int32_t), TF_TensorByteSize(out));
    129       int32_t* output_contents = static_cast<int32_t*>(TF_TensorData(out));
    130       EXPECT_EQ(expected_results[i], *output_contents);
    131     }
    132   }
    133 
    134   void CompareInt32Tensor(const std::vector<int32_t>& expected, TF_Tensor* t) {
    135     int32_t* data = static_cast<int32_t*>(TF_TensorData(t));
    136     size_t size = TF_TensorByteSize(t);
    137     ASSERT_EQ(expected.size() * sizeof(int32_t), size);
    138     for (int i = 0; i < expected.size(); ++i) {
    139       ASSERT_EQ(expected[i], data[i]) << "Different data at index " << i;
    140     }
    141   }
    142 
    143   std::vector<TF_Output> ToOutput(const std::vector<TF_Operation*> ops) {
    144     std::vector<TF_Output> out;
    145     for (auto op : ops) {
    146       out.push_back({op, 0});
    147     }
    148     return out;
    149   }
    150 
    151   void Define(int num_opers, const std::vector<TF_Operation*>& opers,
    152               const std::vector<TF_Operation*>& inputs,
    153               const std::vector<TF_Operation*>& outputs,
    154               const std::vector<string>& output_names,
    155               bool expect_failure = false) {
    156     DefineT(num_opers, opers, ToOutput(inputs), ToOutput(outputs), output_names,
    157             expect_failure);
    158   }
    159 
    160   // Caller must delete[] the returned value
    161   static const char** ToArray(const std::vector<string>& strs) {
    162     const char** ptr = nullptr;
    163     if (!strs.empty()) {
    164       ptr = new const char*[strs.size()];
    165       for (size_t i = 0; i < strs.size(); ++i) {
    166         ptr[i] = strs[i].c_str();
    167       }
    168     }
    169     return ptr;
    170   }
    171 
    172   // An explicit `num_opers` is needed so that we can distinguish between the
    173   // case of no operations specified (-1) and the case of an empty set of
    174   // operations specified (0).
    175   void DefineT(int num_opers, const std::vector<TF_Operation*>& opers,
    176                const std::vector<TF_Output>& inputs,
    177                const std::vector<TF_Output>& outputs,
    178                const std::vector<string>& output_names,
    179                bool expect_failure = false) {
    180     ASSERT_EQ(func_, nullptr);
    181     const char** output_names_ptr = ToArray(output_names);
    182     func_ = TF_GraphToFunction(func_graph_, func_name_, false, num_opers,
    183                                num_opers == -1 ? nullptr : opers.data(),
    184                                inputs.size(), inputs.data(), outputs.size(),
    185                                outputs.data(), output_names_ptr,
    186                                /*opts=*/nullptr, /*description=*/nullptr, s_);
    187     delete[] output_names_ptr;
    188     if (expect_failure) {
    189       ASSERT_EQ(func_, nullptr);
    190       return;
    191     }
    192 
    193     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    194     ASSERT_NE(func_, nullptr);
    195     TF_GraphCopyFunction(host_graph_, func_, nullptr, s_);
    196     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    197   }
    198 
    199   TF_Operation* Use(const std::vector<TF_Operation*>& inputs) {
    200     return UseT(ToOutput(inputs));
    201   }
    202 
    203   TF_Operation* UseT(const std::vector<TF_Output>& inputs) {
    204     TF_Operation* op;
    205     UseHelper(inputs, &op);
    206     return op;
    207   }
    208 
    209   // All the *Helper methods are used as a workaround for the restrictions that
    210   // one cannot call ASSERT_* methods in non-void-returning functions (when
    211   // exceptions are disabled during compilation)
    212   void UseHelper(const std::vector<TF_Output>& inputs, TF_Operation** op) {
    213     TF_OperationDescription* desc =
    214         TF_NewOperation(host_graph_, func_name_, func_node_name_);
    215     for (auto input : inputs) {
    216       TF_AddInput(desc, input);
    217     }
    218     // Set device to CPU because some ops inside the function might not be
    219     // available on GPU.
    220     TF_SetDevice(desc, "/cpu:0");
    221     *op = TF_FinishOperation(desc, s_);
    222     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    223     ASSERT_NE(*op, nullptr);
    224   }
    225 
    226   FunctionDef fdef() {
    227     tensorflow::FunctionDef fdef;
    228     EXPECT_TRUE(GetFunctionDef(func_, &fdef));
    229     return fdef;
    230   }
    231 
    232   // logging utility
    233   template <class Container>
    234   string ToString(const Container& v) {
    235     std::stringstream ss;
    236     ss << "{";
    237     size_t i = 0;
    238     for (const auto& e : v) {
    239       if (i != 0) {
    240         ss << ", ";
    241       }
    242       ss << e.ToString();
    243       ++i;
    244     }
    245     ss << "}";
    246     return ss.str();
    247   }
    248 
    249   void VerifyFDefNodes(const tensorflow::FunctionDef& fdef,
    250                        const std::unordered_set<string>& nodes) {
    251     ASSERT_EQ(nodes.size(), fdef.node_def_size())
    252         << "Got unexpected number of nodes. Expected: ["
    253         << str_util::Join(nodes, ", ")
    254         << "] Actual nodes in fdef: " << fdef.DebugString();
    255     for (const NodeDef& node_def : fdef.node_def()) {
    256       ASSERT_TRUE(nodes.find(node_def.name()) != nodes.end())
    257           << "Got unexpected node: " << node_def.name()
    258           << " in fdef: " << fdef.DebugString();
    259     }
    260   }
    261 
    262   void VerifyFDefInputs(const tensorflow::FunctionDef& fdef,
    263                         const std::vector<IOSpec>& inputs) {
    264     const OpDef& signature = fdef.signature();
    265     ASSERT_EQ(inputs.size(), signature.input_arg_size());
    266     for (int i = 0; i < inputs.size(); ++i) {
    267       const OpDef::ArgDef& arg = signature.input_arg(i);
    268       const IOSpec& in = inputs[i];
    269       if (in.second != DT_INVALID) {
    270         ASSERT_EQ(arg.type(), in.second)
    271             << "Got unexpected type for input " << i
    272             << ". fdef: " << fdef.DebugString();
    273       }
    274       ASSERT_EQ(arg.name(), in.first) << "Got unexpected name for input " << i
    275                                       << ". fdef: " << fdef.DebugString();
    276     }
    277   }
    278 
    279   void VerifyFDefOutputs(const tensorflow::FunctionDef& fdef,
    280                          const std::vector<IOSpec>& outputs) {
    281     const OpDef& signature = fdef.signature();
    282     ASSERT_EQ(outputs.size(), signature.output_arg_size());
    283     for (int i = 0; i < outputs.size(); ++i) {
    284       const OpDef::ArgDef& arg = signature.output_arg(i);
    285       const IOSpec& out = outputs[i];
    286       if (out.second != DT_INVALID) {
    287         ASSERT_EQ(arg.type(), out.second)
    288             << "Got unexpected type for output " << i
    289             << ". fdef: " << fdef.DebugString();
    290       }
    291       ASSERT_EQ(arg.name(), out.first) << "Got unexpected name for output " << i
    292                                        << ". fdef: " << fdef.DebugString();
    293     }
    294   }
    295 
    296   void VerifyFDefEdges(
    297       const tensorflow::FunctionDef& fdef,
    298       const std::vector<EdgeSpec>& e_edges,  // expected edges
    299       const std::vector<EdgeSpec>& c_edges,  // expected ctrl edges
    300       bool is_exact_edges = true) {
    301     // Build a set of edges from fdef
    302     std::set<EdgeSpec> a_edges;  // actual edges
    303     // Get edges from inputs to body nodes and between body nodes
    304     for (const NodeDef& node_def : fdef.node_def()) {
    305       for (int i = 0; i < node_def.input_size(); ++i) {
    306         const string& in = node_def.input(i);
    307         const auto& v =
    308             a_edges.insert({in, strings::StrCat(node_def.name(), ":", i)});
    309         ASSERT_TRUE(v.second) << "Duplicate edge " << in << " -> "
    310                               << strings::StrCat(node_def.name(), ":", i)
    311                               << ". fdef: " << fdef.DebugString();
    312       }
    313     }
    314     // Get edges from body nodes to outputs and from inputs to outputs
    315     for (const OpDef::ArgDef& arg : fdef.signature().output_arg()) {
    316       const auto& iter = fdef.ret().find(arg.name());
    317       if (iter != fdef.ret().end()) {
    318         const auto& v = a_edges.insert({iter->second, arg.name()});
    319         ASSERT_TRUE(v.second) << "Duplicate edge " << iter->second << " -> "
    320                               << arg.name() << ". fdef: " << fdef.DebugString();
    321       } else {
    322         const auto& v = a_edges.insert({arg.name(), arg.name()});
    323         ASSERT_TRUE(v.second) << "Duplicate edge " << arg.name() << " -> "
    324                               << arg.name() << ". fdef: " << fdef.DebugString();
    325       }
    326     }
    327 
    328     // Verify edges
    329     for (const EdgeSpec& e : e_edges) {
    330       ASSERT_TRUE(a_edges.find(e) != a_edges.end())
    331           << "Failed to find expected edge " << e.ToString()
    332           << " in fdef: " << fdef.DebugString();
    333     }
    334     for (const EdgeSpec& e : c_edges) {
    335       ASSERT_TRUE(a_edges.find(e) != a_edges.end())
    336           << "Failed to find expected control edge " << e.ToString()
    337           << " in fdef: " << fdef.DebugString();
    338     }
    339 
    340     // If caller specified all edges, check that we have seen all
    341     if (is_exact_edges) {
    342       ASSERT_EQ(e_edges.size() + c_edges.size(), a_edges.size())
    343           << "Expected edges: " << ToString(e_edges)
    344           << " Expected Control edges: " << ToString(c_edges)
    345           << " Actual edges: " << ToString(a_edges)
    346           << " in fdef: " << fdef.DebugString();
    347     }
    348   }
    349 
    350   void VerifyFDef(const std::unordered_set<string>& nodes,
    351                   const std::vector<IOSpec>& inputs,
    352                   const std::vector<IOSpec>& outputs,
    353                   const std::vector<EdgeSpec>& e_edges,  // expected edges
    354                   const std::vector<EdgeSpec>& c_edges,  // expected ctrl edges
    355                   bool is_exact_edges = true) {
    356     tensorflow::FunctionDef fdef;
    357     ASSERT_TRUE(GetFunctionDef(func_, &fdef));
    358     VerifyFDefNodes(fdef, nodes);
    359     VerifyFDefInputs(fdef, inputs);
    360     VerifyFDefOutputs(fdef, outputs);
    361     VerifyFDefEdges(fdef, e_edges, c_edges, is_exact_edges);
    362   }
    363 
    364   // Serialize func_ to fdef and import it back
    365   void Reincarnate() {
    366     // func_ -> fdef
    367     tensorflow::FunctionDef fdef;
    368     ASSERT_TRUE(GetFunctionDef(func_, &fdef));
    369     TF_DeleteFunction(func_);
    370 
    371     // fdef -> func_
    372     string buf;
    373     ASSERT_TRUE(fdef.SerializeToString(&buf));
    374     func_ = TF_FunctionImportFunctionDef(buf.data(), buf.size(), s_);
    375     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    376   }
    377 
    378   void GetAttr(const char* attr_name, AttrValue* out_attr) {
    379     TF_Buffer* attr_buf = TF_NewBuffer();
    380     TF_FunctionGetAttrValueProto(func_, attr_name, attr_buf, s_);
    381     ASSERT_TRUE(out_attr->ParseFromArray(attr_buf->data, attr_buf->length));
    382     TF_DeleteBuffer(attr_buf);
    383   }
    384 
    385   const char* func_name_ = "MyFunc";
    386   const char* func_node_name_ = "MyFunc_0";
    387   TF_Status* s_;
    388   TF_Graph* func_graph_;
    389   TF_Graph* host_graph_;
    390   TF_Function* func_;
    391 
    392   // Workaround for not being able to initialize empty map using {}
    393   std::unordered_set<string> empty_;
    394 };
    395 
    396 TEST_F(CApiFunctionTest, OneOp_ZeroInputs_OneOutput) {
    397   /*
    398    *                constant
    399    *                   |
    400    *                   v
    401    */
    402   // Define
    403   TF_Operation* c = ScalarConst(10, func_graph_, s_, "scalar10");
    404   Define(-1, {}, {}, {c}, {});
    405 
    406   // Use, run, and verify
    407   TF_Operation* func_op = Use({});
    408   Run({}, func_op, 10);
    409   VerifyFDef({"scalar10_0"}, {}, {{"scalar10", DT_INT32}},
    410              {{"scalar10_0:output:0", "scalar10"}}, {});
    411 }
    412 
    413 TEST_F(CApiFunctionTest, OneOp_OneInput_OneOutput) {
    414   /*
    415    *                   |
    416    *                   v
    417    *                 negate
    418    *                   |
    419    *                   v
    420    */
    421   // Define
    422   TF_Operation* feed = Placeholder(func_graph_, s_);
    423   TF_Operation* neg = Neg(feed, func_graph_, s_);
    424   Define(-1, {}, {feed}, {neg}, {});
    425 
    426   // Use, run, and verify
    427   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    428   TF_Operation* func_op = Use({func_feed});
    429   Run({{func_feed, Int32Tensor(3)}}, func_op, -3);
    430   VerifyFDef({"neg_0"}, {{"feed", DT_INT32}}, {{"neg", DT_INT32}},
    431              {{"feed", "neg_0:0"}, {"neg_0:y:0", "neg"}}, {});
    432 }
    433 
    434 TEST_F(CApiFunctionTest, OneOutput_OutputNames) {
    435   /*
    436    *                   |
    437    *                   v
    438    *                 negate
    439    *                   |
    440    *                   v
    441    */
    442   // Define
    443   TF_Operation* feed = Placeholder(func_graph_, s_);
    444   TF_Operation* neg = Neg(feed, func_graph_, s_);
    445   Define(-1, {}, {feed}, {neg}, {"negated_num"});
    446 
    447   // Use, run, and verify
    448   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    449   TF_Operation* func_op = Use({func_feed});
    450   Run({{func_feed, Int32Tensor(3)}}, func_op, -3);
    451   VerifyFDef({"neg"}, {{"feed", DT_INT32}}, {{"negated_num", DT_INT32}},
    452              {{"feed", "neg:0"}, {"neg:y:0", "negated_num"}}, {});
    453 }
    454 
    455 TEST_F(CApiFunctionTest, OutputNames_SameNameAsInput) {
    456   /*
    457    *                   |
    458    *                   v
    459    *                 negate
    460    *                   |
    461    *                   v
    462    */
    463   // Define
    464   TF_Operation* feed = Placeholder(func_graph_, s_, "negation");
    465   TF_Operation* neg = Neg(feed, func_graph_, s_, "neg");
    466   Define(-1, {}, {feed}, {neg}, {"negation"});
    467 
    468   // Use, run, and verify
    469   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    470   TF_Operation* func_op = Use({func_feed});
    471   Run({{func_feed, Int32Tensor(3)}}, func_op, -3);
    472   VerifyFDef({"neg"}, {{"negation_0", DT_INT32}}, {{"negation", DT_INT32}},
    473              {{"negation_0", "neg:0"}, {"neg:y:0", "negation"}}, {});
    474 }
    475 
    476 TEST_F(CApiFunctionTest, ZeroOps_Identity) {
    477   /*
    478    *                   |
    479    *                   |
    480    *                   |
    481    *                   v
    482    */
    483   // Define
    484   TF_Operation* feed = Placeholder(func_graph_, s_);
    485   Define(-1, {}, {feed}, {feed}, {});
    486 
    487   // Use, run, and verify
    488   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    489   TF_Operation* func_op = Use({func_feed});
    490   Run({{func_feed, Int32Tensor(3)}}, func_op, 3);
    491   VerifyFDef(empty_, {{"feed_0", DT_INT32}}, {{"feed", DT_INT32}},
    492              {{"feed_0", "feed"}}, {});
    493 }
    494 
    495 TEST_F(CApiFunctionTest, ZeroOps_Permutation) {
    496   /*
    497    *                   |   |
    498    *                   \  /
    499    *                    \/
    500    *                    x
    501    *                   /\
    502    *                  /  \
    503    *                 |   |
    504    *                 v   v
    505    */
    506   // Define
    507   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    508   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    509   Define(-1, {}, {feed1, feed2}, {feed2, feed1}, {});
    510 
    511   // Use, run, and verify
    512   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    513   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    514   TF_Operation* func_op = Use({two, func_feed});
    515   Run({{func_feed, Int32Tensor(3)}}, {{func_op, 0}, {func_op, 1}}, {3, 2});
    516   VerifyFDef(empty_, M({{"feed1_0"}, {"feed2_0"}}), M({{"feed2"}, {"feed1"}}),
    517              {{"feed1_0", "feed1"}, {"feed2_0", "feed2"}}, {});
    518 }
    519 
    520 TEST_F(CApiFunctionTest, ZeroOps_Permutation_OutputNames) {
    521   /*
    522    *                   |   |
    523    *                   \  /
    524    *                    \/
    525    *                    x
    526    *                   /\
    527    *                  /  \
    528    *                 |   |
    529    *                 v   v
    530    */
    531   // Define
    532   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    533   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    534   Define(-1, {}, {feed1, feed2}, {feed2, feed1}, {"first", "second"});
    535 
    536   // Use, run, and verify
    537   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    538   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    539   TF_Operation* func_op = Use({two, func_feed});
    540   Run({{func_feed, Int32Tensor(3)}}, {{func_op, 0}, {func_op, 1}}, {3, 2});
    541   VerifyFDef(empty_, M({{"feed1"}, {"feed2"}}), M({{"first"}, {"second"}}),
    542              {{"feed1", "second"}, {"feed2", "first"}}, {});
    543 }
    544 
    545 TEST_F(CApiFunctionTest, OneOp_TwoInputs_OneOutput) {
    546   /*
    547    *                  |  |
    548    *                  v  v
    549    *                  add
    550    *                   |
    551    *                   v
    552    */
    553   // Define
    554   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    555   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    556   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
    557   Define(-1, {}, {feed1, feed2}, {add}, {});
    558 
    559   // Use, run, and verify
    560   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    561   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    562   TF_Operation* func_op = Use({two, func_feed});
    563   Run({{func_feed, Int32Tensor(3)}}, func_op, 2 + 3);
    564   VerifyFDef(
    565       {"add_0"}, M({{"feed1"}, {"feed2"}}), M({{"add"}}),
    566       {{"feed1", "add_0:0"}, {"feed2", "add_0:1"}, {"add_0:sum:0", "add"}}, {});
    567 }
    568 
    569 TEST_F(CApiFunctionTest, OneOp_TwoInputs_ZeroOutputs) {
    570   /*
    571    *                  |  |
    572    *                  v  v
    573    *                  add
    574    *
    575    *            (output ignored)
    576    */
    577   // Define
    578   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    579   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    580   Add(feed1, feed2, func_graph_, s_);
    581   Define(-1, {}, {feed1, feed2}, {}, {});
    582 
    583   // Use, run, and verify
    584   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    585   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    586   Use({two, func_feed});
    587   VerifyFDef({"add"}, M({{"feed1"}, {"feed2"}}), {},
    588              {{"feed1", "add:0"}, {"feed2", "add:1"}}, {});
    589 }
    590 
    591 TEST_F(CApiFunctionTest, TwoOps_ThreeInputs_OneOutput) {
    592   /*
    593    *                  |  |   |
    594    *                  v  v   /
    595    *                  add1  /
    596    *                   |   |
    597    *                   v   v
    598    *                   add2
    599    *                    |
    600    *                    v
    601    */
    602   // Define
    603   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    604   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    605   TF_Operation* feed3 = Placeholder(func_graph_, s_, "feed3");
    606   TF_Operation* add1 = Add(feed1, feed2, func_graph_, s_, "add1");
    607   TF_Operation* add2 = Add(add1, feed3, func_graph_, s_, "add2");
    608   Define(-1, {}, {feed1, feed2, feed3}, {add2}, {});
    609 
    610   // Use, run, and verify
    611   TF_Operation* two = ScalarConst(2, host_graph_, s_, "two");
    612   TF_Operation* ten = ScalarConst(10, host_graph_, s_, "ten");
    613   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    614   TF_Operation* func_op = Use({two, ten, func_feed});
    615   Run({{func_feed, Int32Tensor(3)}}, func_op, 2 + 10 + 3);
    616   VerifyFDef({"add1", "add2_0"}, M({{"feed1"}, {"feed2"}, {"feed3"}}),
    617              M({{"add2"}}),
    618              {{"feed1", "add1:0"},
    619               {"feed2", "add1:1"},
    620               {"add1:sum:0", "add2_0:0"},
    621               {"feed3", "add2_0:1"},
    622               {"add2_0:sum:0", "add2"}},
    623              {});
    624 }
    625 
    626 TEST_F(CApiFunctionTest, OneOp_TwoInputs_TwoDuplicateOutputs) {
    627   /*
    628    *                  |  |
    629    *                  v  v
    630    *                  add
    631    *                   |
    632    *                 +-+-+
    633    *                 |   |
    634    *                 v   v
    635    */
    636   // Define
    637   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    638   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    639   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
    640   Define(-1, {}, {feed1, feed2}, {add, add}, {});
    641 
    642   // Use, run, and verify
    643   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    644   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    645   TF_Operation* func_op = Use({two, func_feed});
    646   Run({{func_feed, Int32Tensor(3)}}, {{func_op, 0}, {func_op, 1}}, {5, 5});
    647   VerifyFDef({"add_1"}, M({{"feed1"}, {"feed2"}}), M({{"add"}, {"add_0"}}),
    648              {{"feed1", "add_1:0"},
    649               {"feed2", "add_1:1"},
    650               {"add_1:sum:0", "add"},
    651               {"add_1:sum:0", "add_0"}},
    652              {});
    653 }
    654 
    655 TEST_F(CApiFunctionTest, TwoDuplicateOutputs_OutputNames) {
    656   /*
    657    *                  |  |
    658    *                  v  v
    659    *                  add
    660    *                   |
    661    *                 +-+-+
    662    *                 |   |
    663    *                 v   v
    664    */
    665   // Define
    666   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    667   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    668   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
    669   Define(-1, {}, {feed1, feed2}, {add, add}, {"out1", "out2"});
    670 
    671   // Use, run, and verify
    672   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    673   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    674   TF_Operation* func_op = Use({two, func_feed});
    675   Run({{func_feed, Int32Tensor(3)}}, {{func_op, 0}, {func_op, 1}}, {5, 5});
    676   VerifyFDef({"add"}, M({{"feed1"}, {"feed2"}}), M({{"out1"}, {"out2"}}),
    677              {{"feed1", "add:0"},
    678               {"feed2", "add:1"},
    679               {"add:sum:0", "out1"},
    680               {"add:sum:0", "out2"}},
    681              {});
    682 }
    683 
    684 TEST_F(CApiFunctionTest, TwoOps_ThreeInputs_TwoOutputs) {
    685   /*
    686    *                  |  |  |
    687    *                  v  v  /
    688    *                  add  /
    689    *                   |  |
    690    *                 +-+  |
    691    *                 | |  |
    692    *                 | v  v
    693    *                 | add
    694    *                 |  |
    695    *                 v  v
    696    */
    697   // Define
    698   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    699   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    700   TF_Operation* feed3 = Placeholder(func_graph_, s_, "feed3");
    701   TF_Operation* add1 = Add(feed1, feed2, func_graph_, s_, "add1");
    702   TF_Operation* add2 = Add(add1, feed3, func_graph_, s_, "add2");
    703   Define(-1, {}, {feed1, feed2, feed3}, {add1, add2}, {});
    704 
    705   // Use, run, and verify
    706   TF_Operation* two = ScalarConst(2, host_graph_, s_, "two");
    707   TF_Operation* ten = ScalarConst(10, host_graph_, s_, "ten");
    708   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    709   TF_Operation* func_op = Use({two, ten, func_feed});
    710   Run({{func_feed, Int32Tensor(3)}}, {{func_op, 0}, {func_op, 1}}, {12, 15});
    711   VerifyFDef({"add1_0", "add2_0"}, M({{"feed1"}, {"feed2"}, {"feed3"}}),
    712              M({{"add1"}, {"add2"}}),
    713              {{"feed1", "add1_0:0"},
    714               {"feed2", "add1_0:1"},
    715               {"add1_0:sum:0", "add2_0:0"},
    716               {"feed3", "add2_0:1"},
    717               {"add1_0:sum:0", "add1"},
    718               {"add2_0:sum:0", "add2"}},
    719              {});
    720 }
    721 
    722 TEST_F(CApiFunctionTest, FromSubsetOfOps) {
    723   /*
    724    *                  |  |  |
    725    *                  v  v  /
    726    *                  add  /
    727    *                   |  |
    728    *               +---+--+---+
    729    *  Ops used     |   |  |   |
    730    *  for func     |   v  v   |
    731    *     |         |   add    |
    732    *     +-------> |    |     |
    733    *               |    v     |
    734    *               |          |
    735    *               +----------+
    736    */
    737   // Define
    738   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    739   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    740   TF_Operation* feed3 = Placeholder(func_graph_, s_, "feed3");
    741   TF_Operation* add1 = Add(feed1, feed2, func_graph_, s_, "add1");
    742   TF_Operation* add2 = Add(add1, feed3, func_graph_, s_, "add2");
    743   Define(1, {add2}, {add1, feed3}, {add2}, {});
    744 
    745   // Use, run, and verify
    746   TF_Operation* two = ScalarConst(2, host_graph_, s_, "two");
    747   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    748   TF_Operation* func_op = Use({two, func_feed});
    749   Run({{func_feed, Int32Tensor(3)}}, func_op, 2 + 3);
    750   VerifyFDef(
    751       {"add2_0"}, M({{"add1"}, {"feed3"}}), M({{"add2"}}),
    752       {{"add1", "add2_0:0"}, {"feed3", "add2_0:1"}, {"add2_0:sum:0", "add2"}},
    753       {});
    754 }
    755 
    756 TEST_F(CApiFunctionTest, UsingOneOutputOfSplit) {
    757   /*
    758    *                      feed
    759    *                       |
    760    *             +---------+---+
    761    *             | const0  |   |
    762    *             |    |    |   |
    763    *             |    v    /   |
    764    *             |    split    |
    765    *             |   |  |  |   |
    766    *             |   v  |  v   |
    767    *             |      |      |
    768    *             +------+------+
    769    *                    |
    770    *                    v
    771    *
    772    *  Only the second output from split is used as function output
    773    */
    774   // Define
    775   TF_Operation* feed = Placeholder(func_graph_, s_);
    776   TF_Operation* split = Split3(feed, func_graph_, s_);
    777   DefineT(-1, {}, {{feed, 0}}, {{split, 1}}, {});
    778 
    779   // Use, run, and verify
    780   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    781   TF_Operation* func_op = Use({func_feed});
    782   RunT({{func_feed, Int32Tensor({1, 2, 3, 4, 5, 6})}}, {{func_op, 0}},
    783        {{3, 4}});
    784   VerifyFDef({"split3_const0", "split3_0"}, M({{"feed"}}), M({{"split3"}}),
    785              {{"split3_const0:output:0", "split3_0:0"},
    786               {"feed", "split3_0:1"},
    787               {"split3_0:output:1", "split3"}},
    788              {});
    789 }
    790 
    791 TEST_F(CApiFunctionTest, UsingTwoOutputsOfSplit) {
    792   /*
    793    *                      feed
    794    *                       |
    795    *             +---------+---+
    796    *             | const0  |   |
    797    *             |    |    |   |
    798    *             |    v    /   |
    799    *             |    split    |
    800    *             |   |  |  |   |
    801    *             |   |  v  |   |
    802    *             |   |     |   |
    803    *             +---+-----+---+
    804    *                 |     |
    805    *                 v     v
    806    *
    807    *  Second output from split is not used as function output
    808    */
    809   // Define
    810   TF_Operation* feed = Placeholder(func_graph_, s_);
    811   TF_Operation* split = Split3(feed, func_graph_, s_);
    812   DefineT(-1, {}, {{feed, 0}}, {{split, 0}, {split, 2}}, {});
    813 
    814   // Use, run, and verify
    815   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    816   TF_Operation* func_op = Use({func_feed});
    817   RunT({{func_feed, Int32Tensor({1, 2, 3, 4, 5, 6})}},
    818        {{func_op, 0}, {func_op, 1}}, {{1, 2}, {5, 6}});
    819   VerifyFDef({"split3_const0", "split3_1"}, M({{"feed"}}),
    820              M({{"split3"}, {"split3_0"}}),
    821              {{"split3_const0:output:0", "split3_1:0"},
    822               {"feed", "split3_1:1"},
    823               {"split3_1:output:0", "split3"},
    824               {"split3_1:output:2", "split3_0"}},
    825              {});
    826 }
    827 
    828 TEST_F(CApiFunctionTest, UsingTwoOutputsOfSplitAsInputs) {
    829   /*
    830    *                    |
    831    *                    v
    832    *                  split
    833    *                 |  |  |
    834    *                 |  v  |
    835    *                 |     |
    836    *             +---+-----+---+
    837    *             |   |     |   |
    838    *             |   v     v   |
    839    *             |     add     |
    840    *             |      |      |
    841    *             |      |      |
    842    *             +------+------+
    843    *                    |
    844    *                    v
    845    */
    846   // Define
    847   TF_Operation* feed = Placeholder(func_graph_, s_);
    848   TF_Operation* split = Split3(feed, func_graph_, s_);
    849   TF_Operation* add = Add({split, 0}, {split, 2}, func_graph_, s_);
    850   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    851   DefineT(1, {add}, {{split, 0}, {split, 2}}, {{add, 0}}, {});
    852 
    853   // Use, run, and verify
    854   TF_Operation* two = ScalarConst(2, host_graph_, s_, "two");
    855   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    856   TF_Operation* func_op = Use({two, func_feed});
    857   Run({{func_feed, Int32Tensor(3)}}, func_op, 2 + 3);
    858   VerifyFDef(
    859       {"add_0"}, M({{"split3"}, {"split3_0"}}), M({{"add"}}),
    860       {{"split3", "add_0:0"}, {"split3_0", "add_0:1"}, {"add_0:sum:0", "add"}},
    861       {});
    862 }
    863 
    864 TEST_F(CApiFunctionTest, NodesUsedInInputsMustHaveSingleOutput) {
    865   /*
    866    *                    |
    867    *                    v
    868    *                  split
    869    *                 |  |  |
    870    *                 |  v  |
    871    *                 |     |
    872    *       input --->|     |<--- input
    873    *                 |     |
    874    *                 v     v
    875    *                   add
    876    *                    |
    877    *                    |
    878    *                    v
    879    */
    880   // Define
    881   TF_Tensor* tensor_123 = Int32Tensor({1, 2, 3});
    882   TF_Operation* c = Const(tensor_123, func_graph_, s_, "const_array");
    883   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    884   TF_Operation* split = Split3(c, func_graph_, s_);
    885   TF_Operation* add = Add({split, 0}, {split, 2}, func_graph_, s_);
    886   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    887   DefineT(-1, {}, {{split, 0}, {split, 2}}, {{add, 0}}, {}, true);
    888   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
    889   EXPECT_EQ(string("When `num_opers` is set to -1, nodes referenced in "
    890                    "`inputs` must have a single output. Node split3 has "
    891                    "3 outputs. Encountered while creating function 'MyFunc'"),
    892             string(TF_Message(s_)));
    893 
    894   TF_DeleteTensor(tensor_123);
    895 }
    896 
    897 TEST_F(CApiFunctionTest, FunctionWithWhileLoop) {
    898   // Inputs to the while loop and the function as a whole
    899   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    900   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    901 
    902   // Outputs of the while loop corresponding to the two inputs above
    903   // The first one will the function's output
    904   std::vector<TF_Output> outputs;
    905 
    906   // Add while loop to func_graph_
    907   {
    908     // The inputs to the while loop
    909     std::vector<TF_Output> inputs = {{feed1, 0}, {feed2, 0}};
    910     std::unique_ptr<TF_WhileParams> params(new TF_WhileParams(
    911         TF_NewWhile(func_graph_, &inputs[0], inputs.size(), s_)));
    912     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    913     params->name = "test_loop";
    914 
    915     // Initialize outputs so we can easily detect errors/bugs
    916     outputs.resize(2, {nullptr, -1});
    917 
    918     // Create loop: while (input1 < input2) input1 += input2 + 1
    919     TF_Operation* less_than = LessThan(
    920         params->cond_inputs[0], params->cond_inputs[1], params->cond_graph, s_);
    921     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    922     params->cond_output = {less_than, 0};
    923 
    924     TF_Operation* add1 = Add(params->body_inputs[0], params->body_inputs[1],
    925                              params->body_graph, s_, "add1");
    926     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    927     TF_Operation* one = ScalarConst(1, params->body_graph, s_);
    928     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    929     TF_Operation* add2 = Add(add1, one, params->body_graph, s_, "add2");
    930     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    931     params->body_outputs[0] = {add2, 0};
    932     params->body_outputs[1] = params->body_inputs[1];
    933 
    934     // Finalize while loop
    935     TF_FinishWhile(params.get(), s_, &outputs[0]);
    936     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    937   }
    938 
    939   // Define function, use it in graph, and run
    940   DefineT(-1, {}, {{feed1, 0}, {feed2, 0}}, {outputs[0]}, {});
    941   TF_Operation* five = ScalarConst(5, host_graph_, s_, "five");
    942   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    943   TF_Operation* func_op = Use({func_feed, five});
    944   Run({{func_feed, Int32Tensor(2)}}, func_op, 2 /*+=*/ + 5 + 1);
    945 
    946   // Verify input, output, and subset of edges in fdef.
    947   // The subset of edges we verify is a chain between feed1 and output to
    948   // make sure that the correct output is picked.
    949   tensorflow::FunctionDef fdef;
    950   ASSERT_TRUE(GetFunctionDef(func_, &fdef));
    951   VerifyFDefInputs(fdef, M({{"feed1"}, {"feed2"}}));
    952   VerifyFDefOutputs(fdef, M({{"test_loop_exit"}}));
    953   VerifyFDefEdges(fdef,
    954                   {{"feed1", "test_loop/Enter:0"},
    955                    {"test_loop/Enter:output:0", "test_loop/Merge:0"},
    956                    {"test_loop/Merge:output:0", "test_loop/Switch:0"},
    957                    {"test_loop/Switch:output_false:0", "test_loop/Exit:0"},
    958                    {"test_loop/Exit:output:0", "test_loop_exit"}},
    959                   {}, false);
    960 }
    961 
    962 TEST_F(CApiFunctionTest, ControlDependency) {
    963   /*
    964    *                  |  |    scalar
    965    *                  |  |    .
    966    *                  v  v   . <---- control dependency
    967    *                  add < -
    968    *                   |
    969    *                   v
    970    */
    971   // Define
    972   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
    973   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
    974   TF_Operation* five = ScalarConst(5, func_graph_, s_);
    975   TF_Operation* add =
    976       AddWithCtrlDependency(feed1, feed2, func_graph_, five, s_);
    977   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
    978   Define(-1, {}, {feed1, feed2}, {add}, {});
    979 
    980   // Use, run, and verify
    981   TF_Operation* two = ScalarConst(2, host_graph_, s_);
    982   TF_Operation* func_feed = Placeholder(host_graph_, s_);
    983   TF_Operation* func_op = Use({two, func_feed});
    984   Run({{func_feed, Int32Tensor(3)}}, func_op, 2 + 3);
    985   VerifyFDef(
    986       {"add_0", "scalar"}, M({{"feed1"}, {"feed2"}}), M({{"add"}}),
    987       {{"feed1", "add_0:0"}, {"feed2", "add_0:1"}, {"add_0:sum:0", "add"}},
    988       {{"^scalar", "add_0:2"}});
    989 }
    990 
    991 TEST_F(CApiFunctionTest, ControlDependencyOutsideOfBody) {
    992   /*
    993    *                  |  |    scalar
    994    *                  |  |    .
    995    *                  v  v   . <---- control dependency
    996    *                  add < -
    997    *                   |
    998    *                   v
    999    */
   1000   // Define
   1001   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1002   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1003   TF_Operation* five = ScalarConst(5, func_graph_, s_);
   1004   TF_Operation* add =
   1005       AddWithCtrlDependency(feed1, feed2, func_graph_, five, s_);
   1006   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1007   Define(1, {add}, {feed1, feed2}, {add}, {}, true);
   1008   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1009   EXPECT_EQ(string("The source of control edge [id=3 scalar:-1 -> add:-1] "
   1010                    "is not in the body. Encountered while creating "
   1011                    "function 'MyFunc'"),
   1012             string(TF_Message(s_)));
   1013 }
   1014 
   1015 TEST_F(CApiFunctionTest, ControlDependencyOutsideOfBody_FromInputNode) {
   1016   /*
   1017    *                  |  |.
   1018    *                  |  |  .
   1019    *                  |  |   .
   1020    *                  v  v   . <---- control dependency
   1021    *                  add < -
   1022    *                   |
   1023    *                   v
   1024    */
   1025   // Define
   1026   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1027   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1028   TF_Operation* add =
   1029       AddWithCtrlDependency(feed1, feed2, func_graph_, feed1, s_);
   1030   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1031   Define(-1, {}, {feed1, feed2}, {add}, {});
   1032 
   1033   // Use, run, and verify
   1034   TF_Operation* two = ScalarConst(2, host_graph_, s_);
   1035   TF_Operation* func_feed = Placeholder(host_graph_, s_);
   1036   TF_Operation* func_op = Use({two, func_feed});
   1037   Run({{func_feed, Int32Tensor(3)}}, func_op, 2 + 3);
   1038   VerifyFDef(
   1039       {"add_0"}, M({{"feed1"}, {"feed2"}}), M({{"add"}}),
   1040       {{"feed1", "add_0:0"}, {"feed2", "add_0:1"}, {"add_0:sum:0", "add"}},
   1041       {{"^feed1", "add_0:2"}});
   1042 }
   1043 
   1044 TEST_F(CApiFunctionTest, DuplicateInputsAreNotAllowed) {
   1045   /*
   1046    *                  feed
   1047    *                   |
   1048    *                  +++
   1049    *                  | |
   1050    *              +---+-+---+
   1051    *              |   | |   |
   1052    *              |   v v   |
   1053    *              |   add   |
   1054    *              |    |    |
   1055    *              |    |    |
   1056    *              +----+----+
   1057    *                   |
   1058    *                   v
   1059    */
   1060   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1061   TF_Operation* add = Add(feed1, feed1, func_graph_, s_);
   1062   Define(-1, {}, {feed1, feed1}, {add}, {}, true);
   1063   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1064   EXPECT_EQ(
   1065       string("TF_Output feed1:0 appears more than once in the input list"),
   1066       string(TF_Message(s_)));
   1067 }
   1068 
   1069 TEST_F(CApiFunctionTest, DuplicateOutputNamesAreNotAllowed) {
   1070   /*
   1071    *                  |  |  |
   1072    *                  v  v  /
   1073    *                  add  /
   1074    *                   |  |
   1075    *                 +-+  |
   1076    *                 | |  |
   1077    *                 | v  v
   1078    *                 | add
   1079    *                 |  |
   1080    *                 v  v
   1081    */
   1082   // Define
   1083   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1084   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1085   TF_Operation* feed3 = Placeholder(func_graph_, s_, "feed3");
   1086   TF_Operation* add1 = Add(feed1, feed2, func_graph_, s_, "add1");
   1087   TF_Operation* add2 = Add(add1, feed3, func_graph_, s_, "add2");
   1088   Define(-1, {}, {feed1, feed2, feed3}, {add1, add2}, {"my_out", "my_out"},
   1089          true);
   1090   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1091   EXPECT_EQ(string("Cannot have duplicate output names. Name 'my_out' "
   1092                    "appears more than once in 'output_names' array."),
   1093             string(TF_Message(s_)));
   1094 }
   1095 
   1096 TEST_F(CApiFunctionTest, InvalidInputTensor_HighIndex) {
   1097   /*
   1098    *                  |  |
   1099    *                  v  v
   1100    *                  add
   1101    *                   |
   1102    *                   v
   1103    */
   1104   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1105   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1106   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
   1107   DefineT(-1, {}, {{feed1, 0}, {feed2, 2}}, {{add, 0}}, {}, true);
   1108   EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s_));
   1109   EXPECT_EQ(string("Node 'feed2' (type: 'Placeholder', num of outputs: 1) does "
   1110                    "not have output 2\n\tEncountered while processing "
   1111                    "input 1 into function 'MyFunc'"),
   1112             string(TF_Message(s_)));
   1113 }
   1114 
   1115 TEST_F(CApiFunctionTest, InvalidInputTensor_BadNodePtr) {
   1116   /*
   1117    *                  |  |
   1118    *                  v  v
   1119    *                  add
   1120    *                   |
   1121    *                   v
   1122    */
   1123   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1124   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1125   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
   1126   DefineT(-1, {}, {{feed1, 0}, {nullptr, 0}}, {{add, 0}}, {}, true);
   1127   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1128   EXPECT_EQ(string("Node is null\n\tEncountered while processing input 1 "
   1129                    "into function 'MyFunc'"),
   1130             string(TF_Message(s_)));
   1131 }
   1132 
   1133 TEST_F(CApiFunctionTest, InvalidOutputTensor_HighIndex) {
   1134   /*
   1135    *                  |  |
   1136    *                  v  v
   1137    *                  add
   1138    *                   |
   1139    *                   v
   1140    */
   1141   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1142   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1143   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
   1144   DefineT(-1, {}, {{feed1, 0}, {feed2, 0}}, {{add, 3}}, {}, true);
   1145   EXPECT_EQ(TF_OUT_OF_RANGE, TF_GetCode(s_));
   1146   EXPECT_EQ(string("Node 'add' (type: 'AddN', num of outputs: 1) does "
   1147                    "not have output 3\n\tEncountered while processing "
   1148                    "output 0 from function 'MyFunc'"),
   1149             string(TF_Message(s_)));
   1150 }
   1151 
   1152 TEST_F(CApiFunctionTest, InvalidOutputTensor_BadNodePtr) {
   1153   /*
   1154    *                  |  |
   1155    *                  v  v
   1156    *                  add
   1157    *                   |
   1158    *                   v
   1159    */
   1160   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1161   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1162   Add(feed1, feed2, func_graph_, s_);
   1163   DefineT(-1, {}, {{feed1, 0}, {feed2, 0}}, {{nullptr, 3}}, {}, true);
   1164   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1165   EXPECT_EQ(string("Node is null\n\tEncountered while processing output 0 "
   1166                    "from function 'MyFunc'"),
   1167             string(TF_Message(s_)));
   1168 }
   1169 
   1170 TEST_F(CApiFunctionTest, NodeMissingInput) {
   1171   /*
   1172    *        input---> |  | <----missing input
   1173    *                  v  v
   1174    *        body----> add
   1175    *                   |
   1176    *                   v
   1177    */
   1178   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1179   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1180   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
   1181   DefineT(1, {add}, {{feed1, 0}}, {{add, 0}}, {}, true);
   1182   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1183   EXPECT_EQ(string("Input 1, 'feed2:0', of node 'add' in function 'MyFunc' "
   1184                    "is not available. You might need to include it in inputs "
   1185                    "or include its source node in the body"),
   1186             string(TF_Message(s_)));
   1187 }
   1188 
   1189 TEST_F(CApiFunctionTest, OutputOpNotInBody) {
   1190   /*
   1191    *                  |  |
   1192    *                  v  v
   1193    *                  add    scalar    (scalar not included in body)
   1194    *                   |       |
   1195    *                   v       v       (function has two outputs)
   1196    */
   1197   // Define
   1198   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1199   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1200   TF_Operation* scalar = ScalarConst(2, func_graph_, s_);
   1201   TF_Operation* add = Add(feed1, feed2, func_graph_, s_);
   1202   Define(1, {add}, {feed1, feed2}, {add, scalar}, {}, true);
   1203   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1204   EXPECT_EQ(string("TF_Output scalar:0 is neither in the function body nor "
   1205                    "among function inputs. Encountered while creating "
   1206                    "function 'MyFunc'"),
   1207             string(TF_Message(s_)));
   1208 }
   1209 
   1210 void DefineFunction(const char* name, TF_Function** func,
   1211                     const char* description = nullptr,
   1212                     bool append_hash = false) {
   1213   std::unique_ptr<TF_Graph, decltype(&TF_DeleteGraph)> func_graph(
   1214       TF_NewGraph(), TF_DeleteGraph);
   1215   std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> s(TF_NewStatus(),
   1216                                                            TF_DeleteStatus);
   1217 
   1218   TF_Operation* feed = Placeholder(func_graph.get(), s.get());
   1219   TF_Operation* neg = Neg(feed, func_graph.get(), s.get());
   1220 
   1221   TF_Output inputs[] = {{feed, 0}};
   1222   TF_Output outputs[] = {{neg, 0}};
   1223   *func = TF_GraphToFunction(func_graph.get(), name, append_hash, -1,
   1224                              /*opers=*/nullptr, 1, inputs, 1, outputs,
   1225                              /*output_names=*/nullptr,
   1226                              /*opts=*/nullptr, description, s.get());
   1227   ASSERT_EQ(TF_OK, TF_GetCode(s.get())) << TF_Message(s.get());
   1228   ASSERT_NE(*func, nullptr);
   1229 }
   1230 
   1231 TEST_F(CApiFunctionTest, SetGradientAndRun) {
   1232   // Define the function and its grad
   1233   DefineFunction(func_name_, &func_);
   1234   TF_Function* grad_func;
   1235   DefineFunction("MyGrad", &grad_func);
   1236 
   1237   // Add func and its gradient to host graph
   1238   TF_GraphCopyFunction(host_graph_, func_, grad_func, s_);
   1239   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1240 
   1241   // Verify that function and its grad are in host graph's GraphDef
   1242   GraphDef gdef;
   1243   GetGraphDef(host_graph_, &gdef);
   1244   std::vector<string> func_names = GetFuncNames(gdef);
   1245   ASSERT_EQ(2, func_names.size());
   1246   ASSERT_EQ(func_name_, func_names[0]);
   1247   ASSERT_EQ("MyGrad", func_names[1]);
   1248   std::vector<std::pair<string, string>> grads = GetGradDefs(gdef);
   1249   ASSERT_EQ(1, grads.size());
   1250   ASSERT_EQ(func_name_, grads[0].first);
   1251   ASSERT_EQ("MyGrad", grads[0].second);
   1252 
   1253   // These calls must be noops
   1254   TF_GraphCopyFunction(host_graph_, func_, grad_func, s_);
   1255   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1256   TF_GraphCopyFunction(host_graph_, func_, nullptr, s_);
   1257   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1258 
   1259   // Delete the gradient func.
   1260   // It is safe to delete after adding a copy to host graph.
   1261   TF_DeleteFunction(grad_func);
   1262 
   1263   // Check that GraphDef did not change
   1264   GraphDef gdef2;
   1265   GetGraphDef(host_graph_, &gdef2);
   1266   ASSERT_EQ(gdef.DebugString(), gdef2.DebugString());
   1267 
   1268   // Use and run func
   1269   TF_Operation* func_feed = Placeholder(host_graph_, s_);
   1270   TF_Operation* func_op = Use({func_feed});
   1271   Run({{func_feed, Int32Tensor(3)}}, func_op, -3);
   1272 }
   1273 
   1274 TEST_F(CApiFunctionTest, SameGradForTwoFunctions) {
   1275   // Define the functions
   1276   TF_Function* func1;
   1277   TF_Function* func2;
   1278   TF_Function* grad_func;
   1279   DefineFunction("FooFunc1", &func1);
   1280   DefineFunction("FooFunc2", &func2);
   1281   DefineFunction("MyGrad", &grad_func);
   1282 
   1283   // Make grad_func be a gradient of func1 and func2
   1284   TF_GraphCopyFunction(host_graph_, func1, grad_func, s_);
   1285   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1286   TF_GraphCopyFunction(host_graph_, func2, grad_func, s_);
   1287   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1288 
   1289   // Verify that functions and their gradients are in host graph's GraphDef
   1290   GraphDef gdef;
   1291   GetGraphDef(host_graph_, &gdef);
   1292   std::vector<std::pair<string, string>> grads = GetGradDefs(gdef);
   1293   ASSERT_EQ(2, grads.size());
   1294   ASSERT_EQ("FooFunc1", grads[0].first);
   1295   ASSERT_EQ("MyGrad", grads[0].second);
   1296   ASSERT_EQ("FooFunc2", grads[1].first);
   1297   ASSERT_EQ("MyGrad", grads[1].second);
   1298 
   1299   TF_DeleteFunction(func1);
   1300   TF_DeleteFunction(func2);
   1301   TF_DeleteFunction(grad_func);
   1302 }
   1303 
   1304 TEST_F(CApiFunctionTest, AddFunctionsThenMakeOneGradientOfAnother) {
   1305   // Define the functions
   1306   TF_Function* func;
   1307   TF_Function* grad_func;
   1308   DefineFunction("FooFunc", &func);
   1309   DefineFunction("MyGrad", &grad_func);
   1310 
   1311   // Add functions individually
   1312   TF_GraphCopyFunction(host_graph_, func, nullptr, s_);
   1313   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1314   TF_GraphCopyFunction(host_graph_, grad_func, nullptr, s_);
   1315   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1316 
   1317   // Check that functions are added but not linked
   1318   GraphDef gdef;
   1319   GetGraphDef(host_graph_, &gdef);
   1320   std::vector<string> func_names = GetFuncNames(gdef);
   1321   ASSERT_EQ(2, func_names.size());
   1322   ASSERT_EQ("FooFunc", func_names[0]);
   1323   ASSERT_EQ("MyGrad", func_names[1]);
   1324   ASSERT_EQ(0, GetGradDefs(gdef).size());
   1325 
   1326   // Make grad_func a gradient of func
   1327   TF_GraphCopyFunction(host_graph_, func, grad_func, s_);
   1328   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1329 
   1330   // Verify that function and its grad are linked
   1331   gdef.Clear();
   1332   GetGraphDef(host_graph_, &gdef);
   1333   std::vector<std::pair<string, string>> grads = GetGradDefs(gdef);
   1334   ASSERT_EQ(1, grads.size());
   1335   ASSERT_EQ("FooFunc", grads[0].first);
   1336   ASSERT_EQ("MyGrad", grads[0].second);
   1337 
   1338   TF_DeleteFunction(func);
   1339   TF_DeleteFunction(grad_func);
   1340 }
   1341 
   1342 TEST_F(CApiFunctionTest, GradientErrorCases) {
   1343   // Define the function
   1344   DefineFunction(func_name_, &func_);
   1345   TF_Function* grad_func1;
   1346   TF_Function* grad_func2;
   1347   DefineFunction("MyGrad1", &grad_func1);
   1348   DefineFunction("MyGrad2", &grad_func2);
   1349 
   1350   // func cannot be null
   1351   TF_GraphCopyFunction(host_graph_, nullptr, func_, s_);
   1352   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1353   EXPECT_EQ(string("'func' argument to TF_GraphCopyFunction cannot be null"),
   1354             string(TF_Message(s_)));
   1355 
   1356   // Cannot change gradient
   1357   TF_GraphCopyFunction(host_graph_, func_, grad_func1, s_);
   1358   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1359   TF_GraphCopyFunction(host_graph_, func_, grad_func2, s_);
   1360   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1361   EXPECT_EQ(string("Cannot assign gradient function 'MyGrad2' to 'MyFunc' "
   1362                    "because it already has gradient function 'MyGrad1'"),
   1363             string(TF_Message(s_)));
   1364 
   1365   TF_DeleteFunction(grad_func1);
   1366   TF_DeleteFunction(grad_func2);
   1367 }
   1368 
   1369 TEST_F(CApiFunctionTest, ImportFunctionDef) {
   1370   /*
   1371    * Using a fairly complex function with output names
   1372    *
   1373    *                  |  |  |
   1374    *                  v  v  /
   1375    *                  add  /
   1376    *                   |  |
   1377    *            +------+  |
   1378    *            |      |  |
   1379    *            |      v  v
   1380    *            |      add
   1381    *            |       |
   1382    *            v       v
   1383    *    internal_out  final_out
   1384    */
   1385   // Define
   1386   TF_Operation* feed1 = Placeholder(func_graph_, s_, "feed1");
   1387   TF_Operation* feed2 = Placeholder(func_graph_, s_, "feed2");
   1388   TF_Operation* feed3 = Placeholder(func_graph_, s_, "feed3");
   1389   TF_Operation* add1 = Add(feed1, feed2, func_graph_, s_, "add1");
   1390   TF_Operation* add2 = Add(add1, feed3, func_graph_, s_, "add2");
   1391   Define(-1, {}, {feed1, feed2, feed3}, {add1, add2},
   1392          {"internal_out", "final_out"});
   1393 
   1394   // Save func_ to FunctionDef and import it back
   1395   Reincarnate();
   1396 
   1397   // Use, run, and verify
   1398   TF_Operation* two = ScalarConst(2, host_graph_, s_, "two");
   1399   TF_Operation* ten = ScalarConst(10, host_graph_, s_, "ten");
   1400   TF_Operation* func_feed = Placeholder(host_graph_, s_);
   1401   TF_Operation* func_op = Use({two, ten, func_feed});
   1402   Run({{func_feed, Int32Tensor(3)}}, {{func_op, 0}, {func_op, 1}}, {12, 15});
   1403   VerifyFDef({"add1", "add2"}, M({{"feed1"}, {"feed2"}, {"feed3"}}),
   1404              M({{"internal_out"}, {"final_out"}}),
   1405              {{"feed1", "add1:0"},
   1406               {"feed2", "add1:1"},
   1407               {"add1:sum:0", "add2:0"},
   1408               {"feed3", "add2:1"},
   1409               {"add1:sum:0", "internal_out"},
   1410               {"add2:sum:0", "final_out"}},
   1411              {});
   1412 }
   1413 
   1414 TEST_F(CApiFunctionTest, ImportFunctionDef_InvalidProto) {
   1415   // Invalid protobuf data (protos cannot start with 4 bytes of zeros)
   1416   char proto[] = {0x0, 0x0, 0x0, 0x0};
   1417   func_ = TF_FunctionImportFunctionDef(proto, 4, s_);
   1418   EXPECT_TRUE(func_ == nullptr);
   1419   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1420   EXPECT_EQ(string("Invalid FunctionDef given to TF_FunctionImportFunctionDef"),
   1421             string(TF_Message(s_)));
   1422 }
   1423 
   1424 TEST_F(CApiFunctionTest, Attribute) {
   1425   DefineFunction(func_name_, &func_);
   1426 
   1427   // Get non existent attribute
   1428   TF_Buffer* attr_buf = TF_NewBuffer();
   1429   TF_FunctionGetAttrValueProto(func_, "foo_attr", attr_buf, s_);
   1430   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_));
   1431   EXPECT_EQ(string("Function 'MyFunc' has no attr named 'foo_attr'."),
   1432             string(TF_Message(s_)));
   1433   TF_DeleteBuffer(attr_buf);
   1434 
   1435   // Set attr
   1436   tensorflow::AttrValue attr;
   1437   attr.set_s("test_attr_value");
   1438   string bytes;
   1439   attr.SerializeToString(&bytes);
   1440   TF_FunctionSetAttrValueProto(func_, "test_attr_name", bytes.data(),
   1441                                bytes.size(), s_);
   1442   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1443 
   1444   // Get attr
   1445   AttrValue read_attr;
   1446   GetAttr("test_attr_name", &read_attr);
   1447   ASSERT_EQ(attr.DebugString(), read_attr.DebugString());
   1448 
   1449   // Retrieve the same attr after save/restore
   1450   Reincarnate();
   1451   AttrValue read_attr2;
   1452   GetAttr("test_attr_name", &read_attr2);
   1453   ASSERT_EQ(attr.DebugString(), read_attr2.DebugString());
   1454 }
   1455 
   1456 TEST_F(CApiFunctionTest, Description) {
   1457   DefineFunction(func_name_, &func_, "Return something");
   1458   tensorflow::FunctionDef fdef;
   1459   ASSERT_TRUE(GetFunctionDef(func_, &fdef));
   1460   ASSERT_EQ(string("Return something"), fdef.signature().description());
   1461 }
   1462 
   1463 TEST_F(CApiFunctionTest, Name) {
   1464   DefineFunction("long_func_name", &func_, "Return something",
   1465                  /*append_hash=*/false);
   1466   tensorflow::FunctionDef fdef;
   1467   ASSERT_TRUE(GetFunctionDef(func_, &fdef));
   1468   ASSERT_EQ(string("long_func_name"), fdef.signature().name());
   1469 }
   1470 
   1471 TEST_F(CApiFunctionTest, AppendHash) {
   1472   DefineFunction("func_name_base", &func_, "Return something",
   1473                  /*append_hash=*/true);
   1474   tensorflow::FunctionDef fdef;
   1475   ASSERT_TRUE(GetFunctionDef(func_, &fdef));
   1476 #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
   1477   ASSERT_EQ(string("func_name_base_ZpgUD4x8oqk"), fdef.signature().name());
   1478 #else
   1479   ASSERT_EQ(string("func_name_base_qaJ8jA8UmGY"), fdef.signature().name());
   1480 #endif
   1481 }
   1482 
   1483 TEST_F(CApiFunctionTest, GetOpDef) {
   1484   DefineFunction(func_name_, &func_);
   1485   TF_GraphCopyFunction(host_graph_, func_, nullptr, s_);
   1486   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1487 
   1488   // Test we can retrieve function OpDef from graph
   1489   TF_Buffer* buffer = TF_NewBuffer();
   1490   TF_GraphGetOpDef(host_graph_, func_name_, buffer, s_);
   1491   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1492 
   1493   // Sanity check returned OpDef
   1494   string data(static_cast<const char*>(buffer->data), buffer->length);
   1495   OpDef op_def;
   1496   op_def.ParseFromString(data);
   1497   EXPECT_EQ(op_def.name(), func_name_);
   1498   EXPECT_EQ(op_def.input_arg_size(), 1);
   1499   EXPECT_EQ(op_def.output_arg_size(), 1);
   1500   EXPECT_FALSE(op_def.is_stateful());
   1501 
   1502   TF_DeleteBuffer(buffer);
   1503 }
   1504 
   1505 void DefineStatefulFunction(const char* name, TF_Function** func) {
   1506   std::unique_ptr<TF_Graph, decltype(&TF_DeleteGraph)> func_graph(
   1507       TF_NewGraph(), TF_DeleteGraph);
   1508   std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> s(TF_NewStatus(),
   1509                                                            TF_DeleteStatus);
   1510 
   1511   TF_Tensor* tensor_shape = Int32Tensor({37, 1});
   1512   TF_Operation* shape = Const(tensor_shape, func_graph.get(), s.get(), "shape");
   1513   TF_Operation* random =
   1514       RandomUniform(shape, TF_FLOAT, func_graph.get(), s.get());
   1515 
   1516   TF_Output inputs[] = {};
   1517   TF_Output outputs[] = {{random, 0}};
   1518   *func = TF_GraphToFunction(func_graph.get(), name, /*append_hash=*/false, -1,
   1519                              /*opers=*/nullptr, 0, inputs, 1, outputs,
   1520                              /*output_names=*/nullptr,
   1521                              /*opts=*/nullptr, "", s.get());
   1522   ASSERT_EQ(TF_OK, TF_GetCode(s.get())) << TF_Message(s.get());
   1523   ASSERT_NE(*func, nullptr);
   1524   TF_DeleteTensor(tensor_shape);
   1525 }
   1526 
   1527 TEST_F(CApiFunctionTest, StatefulOpDef) {
   1528   DefineStatefulFunction(func_name_, &func_);
   1529   TF_GraphCopyFunction(host_graph_, func_, nullptr, s_);
   1530   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1531 
   1532   // Test we can retrieve function OpDef from graph
   1533   TF_Buffer* buffer = TF_NewBuffer();
   1534   TF_GraphGetOpDef(host_graph_, func_name_, buffer, s_);
   1535   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1536 
   1537   // Sanity check returned OpDef
   1538   string data(static_cast<const char*>(buffer->data), buffer->length);
   1539   OpDef op_def;
   1540   op_def.ParseFromString(data);
   1541   EXPECT_EQ(op_def.name(), func_name_);
   1542   EXPECT_EQ(op_def.input_arg_size(), 0);
   1543   EXPECT_EQ(op_def.output_arg_size(), 1);
   1544   EXPECT_TRUE(op_def.is_stateful());
   1545 
   1546   TF_DeleteBuffer(buffer);
   1547 }
   1548 
   1549 void AssertEqual(TF_Function* f1, TF_Function* f2) {
   1550   string s1, s2;
   1551   tensorflow::FunctionDef fdef1, fdef2;
   1552   ASSERT_TRUE(GetFunctionDef(f1, &fdef1));
   1553   ASSERT_TRUE(GetFunctionDef(f2, &fdef2));
   1554   SerializeToStringDeterministic(fdef1, &s1);
   1555   SerializeToStringDeterministic(fdef2, &s2);
   1556   ASSERT_EQ(s1, s2);
   1557 }
   1558 
   1559 string GetName(TF_Function* func) {
   1560   tensorflow::FunctionDef fdef;
   1561   GetFunctionDef(func, &fdef);
   1562   return fdef.signature().name();
   1563 }
   1564 
   1565 TEST_F(CApiFunctionTest, GetFunctionsFromGraph) {
   1566   TF_Function* funcs[2];
   1567 
   1568   // Get functions from empty graph
   1569   EXPECT_EQ(TF_GraphNumFunctions(host_graph_), 0);
   1570   TF_GraphGetFunctions(host_graph_, nullptr, 0, s_);
   1571   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1572 
   1573   // Define a function and add it to host_graph_
   1574   TF_Function* func0;
   1575   DefineFunction("FooFunc0", &func0);
   1576   TF_GraphCopyFunction(host_graph_, func0, nullptr, s_);
   1577   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1578 
   1579   // Get this function from host_graph_
   1580   EXPECT_EQ(TF_GraphNumFunctions(host_graph_), 1);
   1581   EXPECT_EQ(TF_GraphGetFunctions(host_graph_, funcs, 0, s_), 0);
   1582   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1583   EXPECT_EQ(TF_GraphGetFunctions(host_graph_, funcs, 1, s_), 1);
   1584   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1585   AssertEqual(func0, funcs[0]);
   1586   TF_DeleteFunction(funcs[0]);
   1587   EXPECT_EQ(TF_GraphGetFunctions(host_graph_, funcs, 2, s_), 1);
   1588   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1589   AssertEqual(func0, funcs[0]);
   1590   TF_DeleteFunction(funcs[0]);
   1591 
   1592   // Define a second function
   1593   TF_Function* func1;
   1594   DefineFunction("FooFunc1", &func1);
   1595   TF_GraphCopyFunction(host_graph_, func1, nullptr, s_);
   1596   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1597 
   1598   // Get both function from host_graph_
   1599   EXPECT_EQ(TF_GraphNumFunctions(host_graph_), 2);
   1600   EXPECT_EQ(TF_GraphGetFunctions(host_graph_, funcs, 0, s_), 0);
   1601   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1602   EXPECT_EQ(TF_GraphGetFunctions(host_graph_, funcs, 2, s_), 2);
   1603   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1604   if (GetName(funcs[0]) == GetName(func0)) {
   1605     AssertEqual(func0, funcs[0]);
   1606     AssertEqual(func1, funcs[1]);
   1607   } else {
   1608     AssertEqual(func0, funcs[1]);
   1609     AssertEqual(func1, funcs[0]);
   1610   }
   1611 
   1612   TF_DeleteFunction(funcs[0]);
   1613   TF_DeleteFunction(funcs[1]);
   1614 
   1615   TF_DeleteFunction(func0);
   1616   TF_DeleteFunction(func1);
   1617 }
   1618 
   1619 }  // namespace
   1620 }  // namespace tensorflow
   1621