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 <algorithm>
     19 #include <cstddef>
     20 #include <iterator>
     21 #include <memory>
     22 #include <vector>
     23 
     24 #include "tensorflow/c/c_test_util.h"
     25 #include "tensorflow/cc/saved_model/signature_constants.h"
     26 #include "tensorflow/cc/saved_model/tag_constants.h"
     27 #include "tensorflow/core/example/example.pb.h"
     28 #include "tensorflow/core/example/feature.pb.h"
     29 #include "tensorflow/core/framework/api_def.pb.h"
     30 #include "tensorflow/core/framework/common_shape_fns.h"
     31 #include "tensorflow/core/framework/graph.pb_text.h"
     32 #include "tensorflow/core/framework/kernel_def.pb.h"
     33 #include "tensorflow/core/framework/node_def.pb_text.h"
     34 #include "tensorflow/core/framework/node_def_util.h"
     35 #include "tensorflow/core/framework/op.h"
     36 #include "tensorflow/core/framework/op_def.pb.h"
     37 #include "tensorflow/core/framework/op_kernel.h"
     38 #include "tensorflow/core/framework/partial_tensor_shape.h"
     39 #include "tensorflow/core/framework/tensor.h"
     40 #include "tensorflow/core/framework/tensor_shape.pb.h"
     41 #include "tensorflow/core/framework/types.pb.h"
     42 #include "tensorflow/core/graph/tensor_id.h"
     43 #include "tensorflow/core/lib/core/error_codes.pb.h"
     44 #include "tensorflow/core/lib/core/status_test_util.h"
     45 #include "tensorflow/core/lib/io/path.h"
     46 #include "tensorflow/core/lib/strings/str_util.h"
     47 #include "tensorflow/core/lib/strings/strcat.h"
     48 #include "tensorflow/core/platform/test.h"
     49 #include "tensorflow/core/protobuf/meta_graph.pb.h"
     50 #include "tensorflow/core/util/equal_graph_def.h"
     51 
     52 namespace tensorflow {
     53 TF_Tensor* TF_TensorFromTensor(const Tensor& src, TF_Status* status);
     54 Status TF_TensorToTensor(const TF_Tensor* src, Tensor* dst);
     55 
     56 namespace {
     57 
     58 static void ExpectHasSubstr(StringPiece s, StringPiece expected) {
     59   EXPECT_TRUE(str_util::StrContains(s, expected))
     60       << "'" << s << "' does not contain '" << expected << "'";
     61 }
     62 
     63 // Returns the GPU device name if there is one (with arbitrary tie breaking if
     64 // there are more than one), or "" otherwise.
     65 string GPUDeviceName(TF_Session* session) {
     66   std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
     67       TF_NewStatus(), TF_DeleteStatus);
     68   TF_Status* s = status.get();
     69   std::unique_ptr<TF_DeviceList, decltype(&TF_DeleteDeviceList)> list(
     70       TF_SessionListDevices(session, s), TF_DeleteDeviceList);
     71   TF_DeviceList* device_list = list.get();
     72 
     73   CHECK_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
     74 
     75   const int num_devices = TF_DeviceListCount(device_list);
     76   LOG(INFO) << "There are " << num_devices << " devices.";
     77   for (int i = 0; i < num_devices; ++i) {
     78     const char* device_name = TF_DeviceListName(device_list, i, s);
     79     CHECK_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
     80     const char* device_type = TF_DeviceListType(device_list, i, s);
     81     CHECK_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
     82     LOG(INFO) << "Device " << i << " has name " << device_name << ", type "
     83               << device_type;
     84     if (string(device_type) == DEVICE_GPU) {
     85       return device_name;
     86     }
     87   }
     88   // No GPU device found.
     89   return "";
     90 }
     91 
     92 string GPUDeviceName() {
     93   std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
     94       TF_NewStatus(), TF_DeleteStatus);
     95   TF_Status* s = status.get();
     96   std::unique_ptr<TF_Graph, decltype(&TF_DeleteGraph)> graph(TF_NewGraph(),
     97                                                              TF_DeleteGraph);
     98 
     99   TF_SessionOptions* opts = TF_NewSessionOptions();
    100   TF_Session* sess = TF_NewSession(graph.get(), opts, s);
    101   TF_DeleteSessionOptions(opts);
    102 
    103   const string gpu_device_name = GPUDeviceName(sess);
    104   TF_DeleteSession(sess, s);
    105   CHECK_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    106   return gpu_device_name;
    107 }
    108 
    109 TEST(CAPI, Version) { EXPECT_STRNE("", TF_Version()); }
    110 
    111 TEST(CAPI, Status) {
    112   TF_Status* s = TF_NewStatus();
    113   EXPECT_EQ(TF_OK, TF_GetCode(s));
    114   EXPECT_EQ(string(), TF_Message(s));
    115   TF_SetStatus(s, TF_CANCELLED, "cancel");
    116   EXPECT_EQ(TF_CANCELLED, TF_GetCode(s));
    117   EXPECT_EQ(string("cancel"), TF_Message(s));
    118   TF_DeleteStatus(s);
    119 }
    120 
    121 void Deallocator(void* data, size_t, void* arg) {
    122   tensorflow::cpu_allocator()->DeallocateRaw(data);
    123   *reinterpret_cast<bool*>(arg) = true;
    124 }
    125 
    126 TEST(CAPI, Tensor) {
    127   const int num_bytes = 6 * sizeof(float);
    128   float* values =
    129       reinterpret_cast<float*>(tensorflow::cpu_allocator()->AllocateRaw(
    130           EIGEN_MAX_ALIGN_BYTES, num_bytes));
    131   int64_t dims[] = {2, 3};
    132   bool deallocator_called = false;
    133   TF_Tensor* t = TF_NewTensor(TF_FLOAT, dims, 2, values, num_bytes,
    134                               &Deallocator, &deallocator_called);
    135   EXPECT_FALSE(deallocator_called);
    136   EXPECT_EQ(TF_FLOAT, TF_TensorType(t));
    137   EXPECT_EQ(2, TF_NumDims(t));
    138   EXPECT_EQ(dims[0], TF_Dim(t, 0));
    139   EXPECT_EQ(dims[1], TF_Dim(t, 1));
    140   EXPECT_EQ(num_bytes, TF_TensorByteSize(t));
    141   EXPECT_EQ(static_cast<void*>(values), TF_TensorData(t));
    142   TF_DeleteTensor(t);
    143   EXPECT_TRUE(deallocator_called);
    144 }
    145 
    146 void NoOpDeallocator(void* data, size_t, void*) {}
    147 
    148 TEST(CAPI, MalformedTensor) {
    149   // See https://github.com/tensorflow/tensorflow/issues/7394
    150   // num_dims = 0 implies a scalar, so should be backed by at least 4 bytes of
    151   // data.
    152   TF_Tensor* t =
    153       TF_NewTensor(TF_FLOAT, nullptr, 0, nullptr, 0, &NoOpDeallocator, nullptr);
    154   ASSERT_TRUE(t == nullptr);
    155 }
    156 
    157 TEST(CAPI, AllocateTensor) {
    158   const int num_bytes = 6 * sizeof(float);
    159   int64_t dims[] = {2, 3};
    160   TF_Tensor* t = TF_AllocateTensor(TF_FLOAT, dims, 2, num_bytes);
    161   EXPECT_EQ(TF_FLOAT, TF_TensorType(t));
    162   EXPECT_EQ(2, TF_NumDims(t));
    163   EXPECT_EQ(dims[0], TF_Dim(t, 0));
    164   EXPECT_EQ(dims[1], TF_Dim(t, 1));
    165   EXPECT_EQ(num_bytes, TF_TensorByteSize(t));
    166   EXPECT_EQ(6, TF_TensorElementCount(t));
    167   TF_DeleteTensor(t);
    168 }
    169 
    170 TEST(CAPI, MaybeMove) {
    171   const int num_bytes = 6 * sizeof(float);
    172   float* values =
    173       reinterpret_cast<float*>(tensorflow::cpu_allocator()->AllocateRaw(
    174           EIGEN_MAX_ALIGN_BYTES, num_bytes));
    175   int64_t dims[] = {2, 3};
    176   bool deallocator_called = false;
    177   TF_Tensor* t = TF_NewTensor(TF_FLOAT, dims, 2, values, num_bytes,
    178                               &Deallocator, &deallocator_called);
    179 
    180   TF_Tensor* o = TF_TensorMaybeMove(t);
    181   ASSERT_TRUE(o == nullptr);  // It is unsafe to move memory TF might not own.
    182   TF_DeleteTensor(t);
    183   EXPECT_TRUE(deallocator_called);
    184 }
    185 
    186 TEST(CAPI, LibraryLoadFunctions) {
    187   // TODO(b/73318067): Fix linking for the GPU test generated by the
    188   // tf_cuda_cc_test() bazel rule and remove the next line.
    189   if (!GPUDeviceName().empty()) return;
    190 
    191 #if !defined(TENSORFLOW_NO_SHARED_OBJECTS)
    192   {
    193     // Load the library.
    194     TF_Status* status = TF_NewStatus();
    195     TF_Library* lib =
    196         TF_LoadLibrary("tensorflow/c/test_op1.so", status);
    197     TF_Code code = TF_GetCode(status);
    198     string status_msg(TF_Message(status));
    199     TF_DeleteStatus(status);
    200     ASSERT_EQ(TF_OK, code) << status_msg;
    201 
    202     // Test op list.
    203     TF_Buffer op_list_buf = TF_GetOpList(lib);
    204     tensorflow::OpList op_list;
    205     EXPECT_TRUE(op_list.ParseFromArray(op_list_buf.data, op_list_buf.length));
    206     ASSERT_EQ(op_list.op_size(), 1);
    207     EXPECT_EQ("TestCApi1", op_list.op(0).name());
    208     TF_DeleteLibraryHandle(lib);
    209   }
    210 #endif  // !defined(TENSORFLOW_NO_SHARED_OBJECTS)
    211   {
    212     TF_Buffer* op_list_buffer = TF_GetAllOpList();
    213     tensorflow::OpList op_list;
    214     op_list.ParseFromArray(op_list_buffer->data, op_list_buffer->length);
    215     ASSERT_GE(op_list.op_size(), 1);
    216     typedef tensorflow::protobuf::RepeatedPtrField<tensorflow::OpDef> OpDefs;
    217     const OpDefs& ops = op_list.op();
    218     bool found = std::find_if(ops.begin(), ops.end(),
    219                               [](const tensorflow::OpDef& op_def) {
    220                                 return op_def.name() == "TestCApi";
    221                               }) != ops.end();
    222     EXPECT_TRUE(found);
    223     TF_DeleteBuffer(op_list_buffer);
    224   }
    225 }
    226 
    227 void TestEncodeDecode(int line, const std::vector<string>& data) {
    228   const tensorflow::int64 n = data.size();
    229   TF_Status* status = TF_NewStatus();
    230   for (const std::vector<tensorflow::int64>& dims :
    231        std::vector<std::vector<tensorflow::int64>>{
    232            {n}, {1, n}, {n, 1}, {n / 2, 2}}) {
    233     // Create C++ Tensor
    234     Tensor src(tensorflow::DT_STRING, TensorShape(dims));
    235     for (tensorflow::int64 i = 0; i < src.NumElements(); ++i) {
    236       src.flat<string>()(i) = data[i];
    237     }
    238     TF_Tensor* dst = TF_TensorFromTensor(src, status);
    239     ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
    240 
    241     // Convert back to a C++ Tensor and ensure we get expected output.
    242     Tensor output;
    243     ASSERT_EQ(Status::OK(), TF_TensorToTensor(dst, &output)) << line;
    244     ASSERT_EQ(src.NumElements(), output.NumElements()) << line;
    245     for (tensorflow::int64 i = 0; i < src.NumElements(); ++i) {
    246       ASSERT_EQ(data[i], output.flat<string>()(i)) << line;
    247     }
    248 
    249     TF_DeleteTensor(dst);
    250   }
    251   TF_DeleteStatus(status);
    252 }
    253 
    254 TEST(CAPI, TensorEncodeDecodeStrings) {
    255   TestEncodeDecode(__LINE__, {});
    256   TestEncodeDecode(__LINE__, {"hello"});
    257   TestEncodeDecode(__LINE__,
    258                    {"the", "quick", "brown", "fox", "jumped", "over"});
    259 
    260   string big(1000, 'a');
    261   TestEncodeDecode(__LINE__, {"small", big, "small2"});
    262 }
    263 
    264 TEST(CAPI, SessionOptions) {
    265   TF_SessionOptions* opt = TF_NewSessionOptions();
    266   TF_DeleteSessionOptions(opt);
    267 }
    268 
    269 TEST(CAPI, DeprecatedSession) {
    270   TF_Status* s = TF_NewStatus();
    271   TF_SessionOptions* opt = TF_NewSessionOptions();
    272   TF_DeprecatedSession* session = TF_NewDeprecatedSession(opt, s);
    273   TF_DeleteSessionOptions(opt);
    274   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    275 
    276   TF_Buffer* run_options = TF_NewBufferFromString("", 0);
    277   TF_Buffer* run_metadata = TF_NewBuffer();
    278   TF_Run(session, run_options, nullptr, nullptr, 0, nullptr, nullptr, 0,
    279          nullptr, 0, run_metadata, s);
    280   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s)) << TF_Message(s);
    281   EXPECT_EQ("Session was not created with a graph before Run()!",
    282             string(TF_Message(s)));
    283   TF_DeleteBuffer(run_metadata);
    284   TF_DeleteBuffer(run_options);
    285 
    286   TF_DeleteDeprecatedSession(session, s);
    287   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    288 
    289   TF_DeleteStatus(s);
    290 }
    291 
    292 TEST(CAPI, DataTypeEnum) {
    293   EXPECT_EQ(TF_FLOAT, static_cast<TF_DataType>(tensorflow::DT_FLOAT));
    294   EXPECT_EQ(TF_DOUBLE, static_cast<TF_DataType>(tensorflow::DT_DOUBLE));
    295   EXPECT_EQ(TF_INT32, static_cast<TF_DataType>(tensorflow::DT_INT32));
    296   EXPECT_EQ(TF_UINT8, static_cast<TF_DataType>(tensorflow::DT_UINT8));
    297   EXPECT_EQ(TF_INT16, static_cast<TF_DataType>(tensorflow::DT_INT16));
    298   EXPECT_EQ(TF_INT8, static_cast<TF_DataType>(tensorflow::DT_INT8));
    299   EXPECT_EQ(TF_STRING, static_cast<TF_DataType>(tensorflow::DT_STRING));
    300   EXPECT_EQ(TF_COMPLEX64, static_cast<TF_DataType>(tensorflow::DT_COMPLEX64));
    301   EXPECT_EQ(TF_COMPLEX, TF_COMPLEX64);
    302   EXPECT_EQ(TF_INT64, static_cast<TF_DataType>(tensorflow::DT_INT64));
    303   EXPECT_EQ(TF_BOOL, static_cast<TF_DataType>(tensorflow::DT_BOOL));
    304   EXPECT_EQ(TF_QINT8, static_cast<TF_DataType>(tensorflow::DT_QINT8));
    305   EXPECT_EQ(TF_QUINT8, static_cast<TF_DataType>(tensorflow::DT_QUINT8));
    306   EXPECT_EQ(TF_QINT32, static_cast<TF_DataType>(tensorflow::DT_QINT32));
    307   EXPECT_EQ(TF_BFLOAT16, static_cast<TF_DataType>(tensorflow::DT_BFLOAT16));
    308   EXPECT_EQ(TF_QINT16, static_cast<TF_DataType>(tensorflow::DT_QINT16));
    309   EXPECT_EQ(TF_QUINT16, static_cast<TF_DataType>(tensorflow::DT_QUINT16));
    310   EXPECT_EQ(TF_UINT16, static_cast<TF_DataType>(tensorflow::DT_UINT16));
    311   EXPECT_EQ(TF_COMPLEX128, static_cast<TF_DataType>(tensorflow::DT_COMPLEX128));
    312   EXPECT_EQ(TF_HALF, static_cast<TF_DataType>(tensorflow::DT_HALF));
    313   EXPECT_EQ(TF_DataTypeSize(TF_DOUBLE),
    314             tensorflow::DataTypeSize(tensorflow::DT_DOUBLE));
    315   EXPECT_EQ(TF_DataTypeSize(TF_STRING),
    316             tensorflow::DataTypeSize(tensorflow::DT_STRING));
    317   // Test with invalid type; should always return 0 as documented
    318   EXPECT_EQ(TF_DataTypeSize(static_cast<TF_DataType>(0)), 0);
    319 }
    320 
    321 TEST(CAPI, StatusEnum) {
    322   EXPECT_EQ(TF_OK, static_cast<TF_Code>(tensorflow::error::OK));
    323   EXPECT_EQ(TF_CANCELLED, static_cast<TF_Code>(tensorflow::error::CANCELLED));
    324   EXPECT_EQ(TF_UNKNOWN, static_cast<TF_Code>(tensorflow::error::UNKNOWN));
    325   EXPECT_EQ(TF_INVALID_ARGUMENT,
    326             static_cast<TF_Code>(tensorflow::error::INVALID_ARGUMENT));
    327   EXPECT_EQ(TF_DEADLINE_EXCEEDED,
    328             static_cast<TF_Code>(tensorflow::error::DEADLINE_EXCEEDED));
    329   EXPECT_EQ(TF_NOT_FOUND, static_cast<TF_Code>(tensorflow::error::NOT_FOUND));
    330   EXPECT_EQ(TF_ALREADY_EXISTS,
    331             static_cast<TF_Code>(tensorflow::error::ALREADY_EXISTS));
    332   EXPECT_EQ(TF_PERMISSION_DENIED,
    333             static_cast<TF_Code>(tensorflow::error::PERMISSION_DENIED));
    334   EXPECT_EQ(TF_UNAUTHENTICATED,
    335             static_cast<TF_Code>(tensorflow::error::UNAUTHENTICATED));
    336   EXPECT_EQ(TF_RESOURCE_EXHAUSTED,
    337             static_cast<TF_Code>(tensorflow::error::RESOURCE_EXHAUSTED));
    338   EXPECT_EQ(TF_FAILED_PRECONDITION,
    339             static_cast<TF_Code>(tensorflow::error::FAILED_PRECONDITION));
    340   EXPECT_EQ(TF_ABORTED, static_cast<TF_Code>(tensorflow::error::ABORTED));
    341   EXPECT_EQ(TF_OUT_OF_RANGE,
    342             static_cast<TF_Code>(tensorflow::error::OUT_OF_RANGE));
    343   EXPECT_EQ(TF_UNIMPLEMENTED,
    344             static_cast<TF_Code>(tensorflow::error::UNIMPLEMENTED));
    345   EXPECT_EQ(TF_INTERNAL, static_cast<TF_Code>(tensorflow::error::INTERNAL));
    346   EXPECT_EQ(TF_UNAVAILABLE,
    347             static_cast<TF_Code>(tensorflow::error::UNAVAILABLE));
    348   EXPECT_EQ(TF_DATA_LOSS, static_cast<TF_Code>(tensorflow::error::DATA_LOSS));
    349 }
    350 
    351 TEST(CAPI, GetAllOpList) {
    352   TF_Buffer* buf = TF_GetAllOpList();
    353   tensorflow::OpList op_list;
    354   EXPECT_TRUE(op_list.ParseFromArray(buf->data, buf->length));
    355   EXPECT_GT(op_list.op_size(), 0);
    356   TF_DeleteBuffer(buf);
    357 }
    358 
    359 TEST(CAPI, SetShape) {
    360   TF_Status* s = TF_NewStatus();
    361   TF_Graph* graph = TF_NewGraph();
    362 
    363   TF_Operation* feed = Placeholder(graph, s);
    364   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    365   TF_Output feed_out_0 = TF_Output{feed, 0};
    366   int num_dims;
    367 
    368   // Fetch the shape, it should be completely unknown.
    369   num_dims = TF_GraphGetTensorNumDims(graph, feed_out_0, s);
    370   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    371   EXPECT_EQ(-1, num_dims);
    372 
    373   // Set the shape to be unknown, expect no change.
    374   TF_GraphSetTensorShape(graph, feed_out_0, /*dims=*/nullptr, -1, s);
    375   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    376   num_dims = TF_GraphGetTensorNumDims(graph, feed_out_0, s);
    377   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    378   EXPECT_EQ(-1, num_dims);
    379 
    380   // Set the shape to be 2 x Unknown
    381   int64_t dims[] = {2, -1};
    382   TF_GraphSetTensorShape(graph, feed_out_0, dims, 2, s);
    383   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    384 
    385   // Fetch the shape and validate it is 2 by -1.
    386   num_dims = TF_GraphGetTensorNumDims(graph, feed_out_0, s);
    387   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    388   EXPECT_EQ(2, num_dims);
    389 
    390   // Resize the dimension vector appropriately.
    391   int64_t returned_dims[2];
    392   TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, num_dims, s);
    393   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    394   EXPECT_EQ(dims[0], returned_dims[0]);
    395   EXPECT_EQ(dims[1], returned_dims[1]);
    396 
    397   // Set to a new valid shape: [2, 3]
    398   dims[1] = 3;
    399   TF_GraphSetTensorShape(graph, feed_out_0, dims, 2, s);
    400   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    401 
    402   // Fetch and see that the new value is returned.
    403   TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, num_dims, s);
    404   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    405   EXPECT_EQ(dims[0], returned_dims[0]);
    406   EXPECT_EQ(dims[1], returned_dims[1]);
    407 
    408   // Try to set 'unknown' with unknown rank on the shape and see that
    409   // it doesn't change.
    410   TF_GraphSetTensorShape(graph, feed_out_0, /*dims=*/nullptr, -1, s);
    411   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    412   TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, num_dims, s);
    413   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    414   EXPECT_EQ(2, num_dims);
    415   EXPECT_EQ(2, returned_dims[0]);
    416   EXPECT_EQ(3, returned_dims[1]);
    417 
    418   // Try to set 'unknown' with same rank on the shape and see that
    419   // it doesn't change.
    420   dims[0] = -1;
    421   dims[1] = -1;
    422   TF_GraphSetTensorShape(graph, feed_out_0, dims, 2, s);
    423   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    424   // Fetch and see that the new value is returned.
    425   TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, num_dims, s);
    426   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    427   EXPECT_EQ(2, num_dims);
    428   EXPECT_EQ(2, returned_dims[0]);
    429   EXPECT_EQ(3, returned_dims[1]);
    430 
    431   // Try to fetch a shape with the wrong num_dims
    432   TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, 5, s);
    433   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s)) << TF_Message(s);
    434 
    435   // Try to set an invalid shape (cannot change 2x3 to a 2x5).
    436   dims[1] = 5;
    437   TF_GraphSetTensorShape(graph, feed_out_0, dims, 2, s);
    438   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s)) << TF_Message(s);
    439 
    440   // Test for a scalar.
    441   TF_Operation* three = ScalarConst(3, graph, s);
    442   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    443   TF_Output three_out_0 = TF_Output{three, 0};
    444 
    445   num_dims = TF_GraphGetTensorNumDims(graph, three_out_0, s);
    446   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    447   EXPECT_EQ(0, num_dims);
    448   TF_GraphGetTensorShape(graph, three_out_0, returned_dims, num_dims, s);
    449   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    450 
    451   // Clean up
    452   TF_DeleteGraph(graph);
    453   TF_DeleteStatus(s);
    454 }
    455 
    456 TEST(CAPI, Graph) {
    457   TF_Status* s = TF_NewStatus();
    458   TF_Graph* graph = TF_NewGraph();
    459 
    460   // Make a placeholder operation.
    461   TF_Operation* feed = Placeholder(graph, s);
    462   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    463 
    464   // Test TF_Operation*() query functions.
    465   EXPECT_EQ(string("feed"), string(TF_OperationName(feed)));
    466   EXPECT_EQ(string("Placeholder"), string(TF_OperationOpType(feed)));
    467   EXPECT_EQ(string(""), string(TF_OperationDevice(feed)));
    468   EXPECT_EQ(1, TF_OperationNumOutputs(feed));
    469   EXPECT_EQ(TF_INT32, TF_OperationOutputType(TF_Output{feed, 0}));
    470   EXPECT_EQ(1, TF_OperationOutputListLength(feed, "output", s));
    471   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    472   EXPECT_EQ(0, TF_OperationNumInputs(feed));
    473   EXPECT_EQ(0, TF_OperationOutputNumConsumers(TF_Output{feed, 0}));
    474   EXPECT_EQ(0, TF_OperationNumControlInputs(feed));
    475   EXPECT_EQ(0, TF_OperationNumControlOutputs(feed));
    476 
    477   tensorflow::AttrValue attr_value;
    478   ASSERT_TRUE(GetAttrValue(feed, "dtype", &attr_value, s)) << TF_Message(s);
    479   EXPECT_EQ(attr_value.type(), tensorflow::DT_INT32);
    480 
    481   // Test not found errors in TF_Operation*() query functions.
    482   EXPECT_EQ(-1, TF_OperationOutputListLength(feed, "bogus", s));
    483   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s));
    484 
    485   ASSERT_FALSE(GetAttrValue(feed, "missing", &attr_value, s));
    486   EXPECT_EQ(string("Operation 'feed' has no attr named 'missing'."),
    487             string(TF_Message(s)));
    488 
    489   // Make a constant oper with the scalar "3".
    490   TF_Operation* three = ScalarConst(3, graph, s);
    491   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    492 
    493   // Add oper.
    494   TF_Operation* add = Add(feed, three, graph, s);
    495   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    496 
    497   // Test TF_Operation*() query functions.
    498   EXPECT_EQ(string("add"), string(TF_OperationName(add)));
    499   EXPECT_EQ(string("AddN"), string(TF_OperationOpType(add)));
    500   EXPECT_EQ(string(""), string(TF_OperationDevice(add)));
    501   EXPECT_EQ(1, TF_OperationNumOutputs(add));
    502   EXPECT_EQ(TF_INT32, TF_OperationOutputType(TF_Output{add, 0}));
    503   EXPECT_EQ(1, TF_OperationOutputListLength(add, "sum", s));
    504   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    505   EXPECT_EQ(2, TF_OperationNumInputs(add));
    506   EXPECT_EQ(2, TF_OperationInputListLength(add, "inputs", s));
    507   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    508   EXPECT_EQ(TF_INT32, TF_OperationInputType(TF_Input{add, 0}));
    509   EXPECT_EQ(TF_INT32, TF_OperationInputType(TF_Input{add, 1}));
    510   TF_Output add_in_0 = TF_OperationInput(TF_Input{add, 0});
    511   EXPECT_EQ(feed, add_in_0.oper);
    512   EXPECT_EQ(0, add_in_0.index);
    513   TF_Output add_in_1 = TF_OperationInput(TF_Input{add, 1});
    514   EXPECT_EQ(three, add_in_1.oper);
    515   EXPECT_EQ(0, add_in_1.index);
    516   EXPECT_EQ(0, TF_OperationOutputNumConsumers(TF_Output{add, 0}));
    517   EXPECT_EQ(0, TF_OperationNumControlInputs(add));
    518   EXPECT_EQ(0, TF_OperationNumControlOutputs(add));
    519 
    520   ASSERT_TRUE(GetAttrValue(add, "T", &attr_value, s)) << TF_Message(s);
    521   EXPECT_EQ(attr_value.type(), tensorflow::DT_INT32);
    522   ASSERT_TRUE(GetAttrValue(add, "N", &attr_value, s)) << TF_Message(s);
    523   EXPECT_EQ(attr_value.i(), 2);
    524 
    525   // Placeholder oper now has a consumer.
    526   ASSERT_EQ(1, TF_OperationOutputNumConsumers(TF_Output{feed, 0}));
    527   TF_Input feed_port;
    528   EXPECT_EQ(1, TF_OperationOutputConsumers(TF_Output{feed, 0}, &feed_port, 1));
    529   EXPECT_EQ(add, feed_port.oper);
    530   EXPECT_EQ(0, feed_port.index);
    531 
    532   // The scalar const oper also has a consumer.
    533   ASSERT_EQ(1, TF_OperationOutputNumConsumers(TF_Output{three, 0}));
    534   TF_Input three_port;
    535   EXPECT_EQ(1,
    536             TF_OperationOutputConsumers(TF_Output{three, 0}, &three_port, 1));
    537   EXPECT_EQ(add, three_port.oper);
    538   EXPECT_EQ(1, three_port.index);
    539 
    540   // Serialize to GraphDef.
    541   GraphDef graph_def;
    542   ASSERT_TRUE(GetGraphDef(graph, &graph_def));
    543 
    544   // Validate GraphDef is what we expect.
    545   bool found_placeholder = false;
    546   bool found_scalar_const = false;
    547   bool found_add = false;
    548   for (const auto& n : graph_def.node()) {
    549     if (IsPlaceholder(n)) {
    550       EXPECT_FALSE(found_placeholder);
    551       found_placeholder = true;
    552     } else if (IsScalarConst(n, 3)) {
    553       EXPECT_FALSE(found_scalar_const);
    554       found_scalar_const = true;
    555     } else if (IsAddN(n, 2)) {
    556       EXPECT_FALSE(found_add);
    557       found_add = true;
    558     } else {
    559       ADD_FAILURE() << "Unexpected NodeDef: " << ProtoDebugString(n);
    560     }
    561   }
    562   EXPECT_TRUE(found_placeholder);
    563   EXPECT_TRUE(found_scalar_const);
    564   EXPECT_TRUE(found_add);
    565 
    566   // Add another oper to the graph.
    567   TF_Operation* neg = Neg(add, graph, s);
    568   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    569 
    570   // Serialize to NodeDef.
    571   NodeDef node_def;
    572   ASSERT_TRUE(GetNodeDef(neg, &node_def));
    573 
    574   // Validate NodeDef is what we expect.
    575   EXPECT_TRUE(IsNeg(node_def, "add"));
    576 
    577   // Serialize to GraphDef.
    578   GraphDef graph_def2;
    579   ASSERT_TRUE(GetGraphDef(graph, &graph_def2));
    580 
    581   // Compare with first GraphDef + added NodeDef.
    582   NodeDef* added_node = graph_def.add_node();
    583   *added_node = node_def;
    584   EXPECT_EQ(ProtoDebugString(graph_def), ProtoDebugString(graph_def2));
    585 
    586   // Look up some nodes by name.
    587   TF_Operation* neg2 = TF_GraphOperationByName(graph, "neg");
    588   EXPECT_TRUE(neg == neg2);
    589   NodeDef node_def2;
    590   ASSERT_TRUE(GetNodeDef(neg2, &node_def2));
    591   EXPECT_EQ(ProtoDebugString(node_def), ProtoDebugString(node_def2));
    592 
    593   TF_Operation* feed2 = TF_GraphOperationByName(graph, "feed");
    594   EXPECT_TRUE(feed == feed2);
    595   ASSERT_TRUE(GetNodeDef(feed, &node_def));
    596   ASSERT_TRUE(GetNodeDef(feed2, &node_def2));
    597   EXPECT_EQ(ProtoDebugString(node_def), ProtoDebugString(node_def2));
    598 
    599   // Test iterating through the nodes of a graph.
    600   found_placeholder = false;
    601   found_scalar_const = false;
    602   found_add = false;
    603   bool found_neg = false;
    604   size_t pos = 0;
    605   TF_Operation* oper;
    606   while ((oper = TF_GraphNextOperation(graph, &pos)) != nullptr) {
    607     if (oper == feed) {
    608       EXPECT_FALSE(found_placeholder);
    609       found_placeholder = true;
    610     } else if (oper == three) {
    611       EXPECT_FALSE(found_scalar_const);
    612       found_scalar_const = true;
    613     } else if (oper == add) {
    614       EXPECT_FALSE(found_add);
    615       found_add = true;
    616     } else if (oper == neg) {
    617       EXPECT_FALSE(found_neg);
    618       found_neg = true;
    619     } else {
    620       ASSERT_TRUE(GetNodeDef(oper, &node_def));
    621       ADD_FAILURE() << "Unexpected Node: " << ProtoDebugString(node_def);
    622     }
    623   }
    624   EXPECT_TRUE(found_placeholder);
    625   EXPECT_TRUE(found_scalar_const);
    626   EXPECT_TRUE(found_add);
    627   EXPECT_TRUE(found_neg);
    628 
    629   // Clean up
    630   TF_DeleteGraph(graph);
    631   TF_DeleteStatus(s);
    632 }
    633 
    634 /*
    635 TODO(skyewm): this test currently DCHECKs, change to bad status
    636 
    637 TEST(CAPI, InputFromDifferentGraphError) {
    638   TF_Status* s = TF_NewStatus();
    639   TF_Graph* g1 = TF_NewGraph();
    640   TF_Graph* g2 = TF_NewGraph();
    641 
    642   TF_Operation* feed = Placeholder(g1, s);
    643   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    644 
    645   // Attempt to create node in g2 with input from g1
    646   Neg(feed, g2, s);
    647   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s));
    648   EXPECT_STREQ("foo", TF_Message(s));
    649 
    650   TF_DeleteGraph(g1);
    651   TF_DeleteGraph(g2);
    652   TF_DeleteStatus(s);
    653 }
    654 */
    655 
    656 TEST(CAPI, ImportGraphDef) {
    657   TF_Status* s = TF_NewStatus();
    658   TF_Graph* graph = TF_NewGraph();
    659 
    660   // Create a simple graph.
    661   Placeholder(graph, s);
    662   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    663   ASSERT_TRUE(TF_GraphOperationByName(graph, "feed") != nullptr);
    664   TF_Operation* oper = ScalarConst(3, graph, s);
    665   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    666   ASSERT_TRUE(TF_GraphOperationByName(graph, "scalar") != nullptr);
    667   Neg(oper, graph, s);
    668   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    669   ASSERT_TRUE(TF_GraphOperationByName(graph, "neg") != nullptr);
    670 
    671   // Export to a GraphDef.
    672   TF_Buffer* graph_def = TF_NewBuffer();
    673   TF_GraphToGraphDef(graph, graph_def, s);
    674   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    675 
    676   // Import it, with a prefix, in a fresh graph.
    677   TF_DeleteGraph(graph);
    678   graph = TF_NewGraph();
    679   TF_ImportGraphDefOptions* opts = TF_NewImportGraphDefOptions();
    680   TF_ImportGraphDefOptionsSetPrefix(opts, "imported");
    681   TF_GraphImportGraphDef(graph, graph_def, opts, s);
    682   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    683 
    684   TF_Operation* scalar = TF_GraphOperationByName(graph, "imported/scalar");
    685   TF_Operation* feed = TF_GraphOperationByName(graph, "imported/feed");
    686   TF_Operation* neg = TF_GraphOperationByName(graph, "imported/neg");
    687   ASSERT_TRUE(scalar != nullptr);
    688   ASSERT_TRUE(feed != nullptr);
    689   ASSERT_TRUE(neg != nullptr);
    690 
    691   // Test basic structure of the imported graph.
    692   EXPECT_EQ(0, TF_OperationNumInputs(scalar));
    693   EXPECT_EQ(0, TF_OperationNumInputs(feed));
    694   ASSERT_EQ(1, TF_OperationNumInputs(neg));
    695   TF_Output neg_input = TF_OperationInput({neg, 0});
    696   EXPECT_EQ(scalar, neg_input.oper);
    697   EXPECT_EQ(0, neg_input.index);
    698 
    699   // Test that we can't see control edges involving the source and sink nodes.
    700   TF_Operation* control_ops[100];
    701   EXPECT_EQ(0, TF_OperationNumControlInputs(scalar));
    702   EXPECT_EQ(0, TF_OperationGetControlInputs(scalar, control_ops, 100));
    703   EXPECT_EQ(0, TF_OperationNumControlOutputs(scalar));
    704   EXPECT_EQ(0, TF_OperationGetControlOutputs(scalar, control_ops, 100));
    705 
    706   EXPECT_EQ(0, TF_OperationNumControlInputs(feed));
    707   EXPECT_EQ(0, TF_OperationGetControlInputs(feed, control_ops, 100));
    708   EXPECT_EQ(0, TF_OperationNumControlOutputs(feed));
    709   EXPECT_EQ(0, TF_OperationGetControlOutputs(feed, control_ops, 100));
    710 
    711   EXPECT_EQ(0, TF_OperationNumControlInputs(neg));
    712   EXPECT_EQ(0, TF_OperationGetControlInputs(neg, control_ops, 100));
    713   EXPECT_EQ(0, TF_OperationNumControlOutputs(neg));
    714   EXPECT_EQ(0, TF_OperationGetControlOutputs(neg, control_ops, 100));
    715 
    716   // Import it again, with an input mapping, return outputs, and a return
    717   // operation, into the same graph.
    718   TF_DeleteImportGraphDefOptions(opts);
    719   opts = TF_NewImportGraphDefOptions();
    720   TF_ImportGraphDefOptionsSetPrefix(opts, "imported2");
    721   TF_ImportGraphDefOptionsAddInputMapping(opts, "scalar", 0, {scalar, 0});
    722   TF_ImportGraphDefOptionsAddReturnOutput(opts, "feed", 0);
    723   TF_ImportGraphDefOptionsAddReturnOutput(opts, "scalar", 0);
    724   EXPECT_EQ(2, TF_ImportGraphDefOptionsNumReturnOutputs(opts));
    725   TF_ImportGraphDefOptionsAddReturnOperation(opts, "scalar");
    726   EXPECT_EQ(1, TF_ImportGraphDefOptionsNumReturnOperations(opts));
    727   TF_ImportGraphDefResults* results =
    728       TF_GraphImportGraphDefWithResults(graph, graph_def, opts, s);
    729   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    730 
    731   TF_Operation* scalar2 = TF_GraphOperationByName(graph, "imported2/scalar");
    732   TF_Operation* feed2 = TF_GraphOperationByName(graph, "imported2/feed");
    733   TF_Operation* neg2 = TF_GraphOperationByName(graph, "imported2/neg");
    734   ASSERT_TRUE(scalar2 != nullptr);
    735   ASSERT_TRUE(feed2 != nullptr);
    736   ASSERT_TRUE(neg2 != nullptr);
    737 
    738   // Check input mapping
    739   neg_input = TF_OperationInput({neg, 0});
    740   EXPECT_EQ(scalar, neg_input.oper);
    741   EXPECT_EQ(0, neg_input.index);
    742 
    743   // Check return outputs
    744   TF_Output* return_outputs;
    745   int num_return_outputs;
    746   TF_ImportGraphDefResultsReturnOutputs(results, &num_return_outputs,
    747                                         &return_outputs);
    748   ASSERT_EQ(2, num_return_outputs);
    749   EXPECT_EQ(feed2, return_outputs[0].oper);
    750   EXPECT_EQ(0, return_outputs[0].index);
    751   EXPECT_EQ(scalar, return_outputs[1].oper);  // remapped
    752   EXPECT_EQ(0, return_outputs[1].index);
    753 
    754   // Check return operation
    755   TF_Operation** return_opers;
    756   int num_return_opers;
    757   TF_ImportGraphDefResultsReturnOperations(results, &num_return_opers,
    758                                            &return_opers);
    759   ASSERT_EQ(1, num_return_opers);
    760   EXPECT_EQ(scalar2, return_opers[0]);  // not remapped
    761 
    762   TF_DeleteImportGraphDefResults(results);
    763 
    764   // Import again, with control dependencies, into the same graph.
    765   TF_DeleteImportGraphDefOptions(opts);
    766   opts = TF_NewImportGraphDefOptions();
    767   TF_ImportGraphDefOptionsSetPrefix(opts, "imported3");
    768   TF_ImportGraphDefOptionsAddControlDependency(opts, feed);
    769   TF_ImportGraphDefOptionsAddControlDependency(opts, feed2);
    770   TF_GraphImportGraphDef(graph, graph_def, opts, s);
    771   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    772 
    773   TF_Operation* scalar3 = TF_GraphOperationByName(graph, "imported3/scalar");
    774   TF_Operation* feed3 = TF_GraphOperationByName(graph, "imported3/feed");
    775   TF_Operation* neg3 = TF_GraphOperationByName(graph, "imported3/neg");
    776   ASSERT_TRUE(scalar3 != nullptr);
    777   ASSERT_TRUE(feed3 != nullptr);
    778   ASSERT_TRUE(neg3 != nullptr);
    779 
    780   // Check that newly-imported scalar and feed have control deps (neg3 will
    781   // inherit them from input)
    782   TF_Operation* control_inputs[100];
    783   int num_control_inputs = TF_OperationGetControlInputs(
    784       scalar3, control_inputs, TF_OperationNumControlInputs(scalar3));
    785   ASSERT_EQ(2, num_control_inputs);
    786   EXPECT_EQ(feed, control_inputs[0]);
    787   EXPECT_EQ(feed2, control_inputs[1]);
    788 
    789   num_control_inputs = TF_OperationGetControlInputs(
    790       feed3, control_inputs, TF_OperationNumControlInputs(feed3));
    791   ASSERT_EQ(2, num_control_inputs);
    792   EXPECT_EQ(feed, control_inputs[0]);
    793   EXPECT_EQ(feed2, control_inputs[1]);
    794 
    795   // Export to a graph def so we can import a graph with control dependencies
    796   TF_DeleteBuffer(graph_def);
    797   graph_def = TF_NewBuffer();
    798   TF_GraphToGraphDef(graph, graph_def, s);
    799   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    800 
    801   // Import again, with remapped control dependency, into the same graph
    802   TF_DeleteImportGraphDefOptions(opts);
    803   opts = TF_NewImportGraphDefOptions();
    804   TF_ImportGraphDefOptionsSetPrefix(opts, "imported4");
    805   TF_ImportGraphDefOptionsRemapControlDependency(opts, "imported/feed", feed);
    806   TF_GraphImportGraphDef(graph, graph_def, opts, s);
    807   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    808 
    809   TF_Operation* scalar4 =
    810       TF_GraphOperationByName(graph, "imported4/imported3/scalar");
    811   TF_Operation* feed4 =
    812       TF_GraphOperationByName(graph, "imported4/imported2/feed");
    813 
    814   // Check that imported `imported3/scalar` has remapped control dep from
    815   // original graph and imported control dep
    816   num_control_inputs = TF_OperationGetControlInputs(
    817       scalar4, control_inputs, TF_OperationNumControlInputs(scalar4));
    818   ASSERT_EQ(2, num_control_inputs);
    819   EXPECT_EQ(feed, control_inputs[0]);
    820   EXPECT_EQ(feed4, control_inputs[1]);
    821 
    822   TF_DeleteImportGraphDefOptions(opts);
    823   TF_DeleteBuffer(graph_def);
    824 
    825   // Can add nodes to the imported graph without trouble.
    826   Add(feed, scalar, graph, s);
    827   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    828 
    829   TF_DeleteGraph(graph);
    830   TF_DeleteStatus(s);
    831 }
    832 
    833 TEST(CAPI, ImportGraphDef_WithReturnOutputs) {
    834   TF_Status* s = TF_NewStatus();
    835   TF_Graph* graph = TF_NewGraph();
    836 
    837   // Create a graph with two nodes: x and 3
    838   Placeholder(graph, s);
    839   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    840   ASSERT_TRUE(TF_GraphOperationByName(graph, "feed") != nullptr);
    841   TF_Operation* oper = ScalarConst(3, graph, s);
    842   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    843   ASSERT_TRUE(TF_GraphOperationByName(graph, "scalar") != nullptr);
    844   Neg(oper, graph, s);
    845   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    846   ASSERT_TRUE(TF_GraphOperationByName(graph, "neg") != nullptr);
    847 
    848   // Export to a GraphDef.
    849   TF_Buffer* graph_def = TF_NewBuffer();
    850   TF_GraphToGraphDef(graph, graph_def, s);
    851   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    852 
    853   // Import it in a fresh graph with return outputs.
    854   TF_DeleteGraph(graph);
    855   graph = TF_NewGraph();
    856   TF_ImportGraphDefOptions* opts = TF_NewImportGraphDefOptions();
    857   TF_ImportGraphDefOptionsAddReturnOutput(opts, "feed", 0);
    858   TF_ImportGraphDefOptionsAddReturnOutput(opts, "scalar", 0);
    859   EXPECT_EQ(2, TF_ImportGraphDefOptionsNumReturnOutputs(opts));
    860   TF_Output return_outputs[2];
    861   TF_GraphImportGraphDefWithReturnOutputs(graph, graph_def, opts,
    862                                           return_outputs, 2, s);
    863   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    864 
    865   TF_Operation* scalar = TF_GraphOperationByName(graph, "scalar");
    866   TF_Operation* feed = TF_GraphOperationByName(graph, "feed");
    867   TF_Operation* neg = TF_GraphOperationByName(graph, "neg");
    868   ASSERT_TRUE(scalar != nullptr);
    869   ASSERT_TRUE(feed != nullptr);
    870   ASSERT_TRUE(neg != nullptr);
    871 
    872   // Check return outputs
    873   EXPECT_EQ(feed, return_outputs[0].oper);
    874   EXPECT_EQ(0, return_outputs[0].index);
    875   EXPECT_EQ(scalar, return_outputs[1].oper);
    876   EXPECT_EQ(0, return_outputs[1].index);
    877 
    878   TF_DeleteImportGraphDefOptions(opts);
    879   TF_DeleteBuffer(graph_def);
    880   TF_DeleteGraph(graph);
    881   TF_DeleteStatus(s);
    882 }
    883 
    884 TEST(CAPI, ImportGraphDef_MissingUnusedInputMappings) {
    885   TF_Status* s = TF_NewStatus();
    886   TF_Graph* graph = TF_NewGraph();
    887 
    888   // Create a graph with two nodes: x and 3
    889   Placeholder(graph, s);
    890   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    891   ASSERT_TRUE(TF_GraphOperationByName(graph, "feed") != nullptr);
    892   TF_Operation* oper = ScalarConst(3, graph, s);
    893   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    894   ASSERT_TRUE(TF_GraphOperationByName(graph, "scalar") != nullptr);
    895   Neg(oper, graph, s);
    896   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    897   ASSERT_TRUE(TF_GraphOperationByName(graph, "neg") != nullptr);
    898 
    899   // Export to a GraphDef.
    900   TF_Buffer* graph_def = TF_NewBuffer();
    901   TF_GraphToGraphDef(graph, graph_def, s);
    902   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    903 
    904   // Import it in a fresh graph.
    905   TF_DeleteGraph(graph);
    906   graph = TF_NewGraph();
    907   TF_ImportGraphDefOptions* opts = TF_NewImportGraphDefOptions();
    908   TF_GraphImportGraphDef(graph, graph_def, opts, s);
    909   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    910 
    911   TF_Operation* scalar = TF_GraphOperationByName(graph, "scalar");
    912 
    913   // Import it in a fresh graph with an unused input mapping.
    914   TF_DeleteImportGraphDefOptions(opts);
    915   opts = TF_NewImportGraphDefOptions();
    916   TF_ImportGraphDefOptionsSetPrefix(opts, "imported");
    917   TF_ImportGraphDefOptionsAddInputMapping(opts, "scalar", 0, {scalar, 0});
    918   TF_ImportGraphDefOptionsAddInputMapping(opts, "fake", 0, {scalar, 0});
    919   TF_ImportGraphDefResults* results =
    920       TF_GraphImportGraphDefWithResults(graph, graph_def, opts, s);
    921   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    922 
    923   // Check unused input mappings
    924   int num_unused_input_mappings;
    925   const char** src_names;
    926   int* src_indexes;
    927   TF_ImportGraphDefResultsMissingUnusedInputMappings(
    928       results, &num_unused_input_mappings, &src_names, &src_indexes);
    929   ASSERT_EQ(1, num_unused_input_mappings);
    930   EXPECT_EQ(string("fake"), string(src_names[0]));
    931   EXPECT_EQ(0, src_indexes[0]);
    932 
    933   TF_DeleteImportGraphDefResults(results);
    934   TF_DeleteImportGraphDefOptions(opts);
    935   TF_DeleteBuffer(graph_def);
    936   TF_DeleteGraph(graph);
    937   TF_DeleteStatus(s);
    938 }
    939 
    940 TEST(CAPI, Session) {
    941   TF_Status* s = TF_NewStatus();
    942   TF_Graph* graph = TF_NewGraph();
    943 
    944   // Make a placeholder operation.
    945   TF_Operation* feed = Placeholder(graph, s);
    946   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    947 
    948   // Make a constant operation with the scalar "2".
    949   TF_Operation* two = ScalarConst(2, graph, s);
    950   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    951 
    952   // Add operation.
    953   TF_Operation* add = Add(feed, two, graph, s);
    954   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    955 
    956   // Create a session for this graph.
    957   CSession csession(graph, s);
    958   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    959 
    960   // Run the graph.
    961   csession.SetInputs({{feed, Int32Tensor(3)}});
    962   csession.SetOutputs({add});
    963   csession.Run(s);
    964   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    965   TF_Tensor* out = csession.output_tensor(0);
    966   ASSERT_TRUE(out != nullptr);
    967   EXPECT_EQ(TF_INT32, TF_TensorType(out));
    968   EXPECT_EQ(0, TF_NumDims(out));  // scalar
    969   ASSERT_EQ(sizeof(int32), TF_TensorByteSize(out));
    970   int32* output_contents = static_cast<int32*>(TF_TensorData(out));
    971   EXPECT_EQ(3 + 2, *output_contents);
    972 
    973   // Add another operation to the graph.
    974   TF_Operation* neg = Neg(add, graph, s);
    975   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    976 
    977   // Run up to the new operation.
    978   csession.SetInputs({{feed, Int32Tensor(7)}});
    979   csession.SetOutputs({neg});
    980   csession.Run(s);
    981   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    982   out = csession.output_tensor(0);
    983   ASSERT_TRUE(out != nullptr);
    984   EXPECT_EQ(TF_INT32, TF_TensorType(out));
    985   EXPECT_EQ(0, TF_NumDims(out));  // scalar
    986   ASSERT_EQ(sizeof(int32), TF_TensorByteSize(out));
    987   output_contents = static_cast<int32*>(TF_TensorData(out));
    988   EXPECT_EQ(-(7 + 2), *output_contents);
    989 
    990   // Clean up
    991   csession.CloseAndDelete(s);
    992   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
    993   TF_DeleteGraph(graph);
    994   TF_DeleteStatus(s);
    995 }
    996 
    997 // If `device` is non-empty, run Min op on that device.
    998 // Otherwise run it on the default device (CPU).
    999 void RunMinTest(const string& device, bool use_XLA) {
   1000   TF_Status* s = TF_NewStatus();
   1001   TF_Graph* graph = TF_NewGraph();
   1002 
   1003   // Make a placeholder operation.
   1004   TF_Operation* feed = Placeholder(graph, s);
   1005   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1006 
   1007   // Make a constant operation with the scalar "0", for axis.
   1008   TF_Operation* one = ScalarConst(0, graph, s);
   1009   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1010 
   1011   // Create a session for this graph.
   1012   CSession csession(graph, s, use_XLA);
   1013   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1014 
   1015   if (!device.empty()) {
   1016     LOG(INFO) << "Setting op Min on device " << device;
   1017   }
   1018   TF_Operation* min = MinWithDevice(feed, one, graph, device, s);
   1019   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1020 
   1021   // Run the graph.
   1022   csession.SetInputs({{feed, Int32Tensor({3, 2, 5})}});
   1023   csession.SetOutputs({min});
   1024   csession.Run(s);
   1025   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1026   TF_Tensor* out = csession.output_tensor(0);
   1027   ASSERT_TRUE(out != nullptr);
   1028   EXPECT_EQ(TF_INT32, TF_TensorType(out));
   1029   EXPECT_EQ(0, TF_NumDims(out));  // scalar
   1030   ASSERT_EQ(sizeof(int32), TF_TensorByteSize(out));
   1031   int32* output_contents = static_cast<int32*>(TF_TensorData(out));
   1032   EXPECT_EQ(2, *output_contents);
   1033 
   1034   // Clean up
   1035   csession.CloseAndDelete(s);
   1036   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1037   TF_DeleteGraph(graph);
   1038   TF_DeleteStatus(s);
   1039 }
   1040 
   1041 TEST(CAPI, Session_Min_CPU) { RunMinTest(/*device=*/"", /*use_XLA=*/false); }
   1042 
   1043 TEST(CAPI, Session_Min_XLA_CPU) { RunMinTest(/*device=*/"", /*use_XLA=*/true); }
   1044 
   1045 TEST(CAPI, Session_Min_GPU) {
   1046   const string gpu_device = GPUDeviceName();
   1047   // Skip this test if no GPU is available.
   1048   if (gpu_device.empty()) return;
   1049 
   1050   RunMinTest(gpu_device, /*use_XLA=*/false);
   1051 }
   1052 
   1053 TEST(CAPI, Session_Min_XLA_GPU) {
   1054   const string gpu_device = GPUDeviceName();
   1055   // Skip this test if no GPU is available.
   1056   if (gpu_device.empty()) return;
   1057 
   1058   RunMinTest(gpu_device, /*use_XLA=*/true);
   1059 }
   1060 
   1061 TEST(CAPI, SessionPRun) {
   1062   TF_Status* s = TF_NewStatus();
   1063   TF_Graph* graph = TF_NewGraph();
   1064 
   1065   // Construct the graph: A + 2 + B
   1066   TF_Operation* a = Placeholder(graph, s, "A");
   1067   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1068 
   1069   TF_Operation* b = Placeholder(graph, s, "B");
   1070   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1071 
   1072   TF_Operation* two = ScalarConst(2, graph, s);
   1073   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1074 
   1075   TF_Operation* plus2 = Add(a, two, graph, s, "plus2");
   1076   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1077 
   1078   TF_Operation* plusB = Add(plus2, b, graph, s, "plusB");
   1079   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1080 
   1081   // Setup a session and a partial run handle.  The partial run will allow
   1082   // computation of A + 2 + B in two phases (calls to TF_SessionPRun):
   1083   // 1. Feed A and get (A+2)
   1084   // 2. Feed B and get (A+2)+B
   1085   TF_SessionOptions* opts = TF_NewSessionOptions();
   1086   TF_Session* sess = TF_NewSession(graph, opts, s);
   1087   TF_DeleteSessionOptions(opts);
   1088 
   1089   TF_Output feeds[] = {TF_Output{a, 0}, TF_Output{b, 0}};
   1090   TF_Output fetches[] = {TF_Output{plus2, 0}, TF_Output{plusB, 0}};
   1091 
   1092   const char* handle = nullptr;
   1093   TF_SessionPRunSetup(sess, feeds, TF_ARRAYSIZE(feeds), fetches,
   1094                       TF_ARRAYSIZE(fetches), nullptr, 0, &handle, s);
   1095   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1096 
   1097   // Feed A and fetch A + 2.
   1098   TF_Output feeds1[] = {TF_Output{a, 0}};
   1099   TF_Output fetches1[] = {TF_Output{plus2, 0}};
   1100   TF_Tensor* feedValues1[] = {Int32Tensor(1)};
   1101   TF_Tensor* fetchValues1[1];
   1102   TF_SessionPRun(sess, handle, feeds1, feedValues1, 1, fetches1, fetchValues1,
   1103                  1, nullptr, 0, s);
   1104   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1105   EXPECT_EQ(3, *(static_cast<int32*>(TF_TensorData(fetchValues1[0]))));
   1106   TF_DeleteTensor(feedValues1[0]);
   1107   TF_DeleteTensor(fetchValues1[0]);
   1108 
   1109   // Feed B and fetch (A + 2) + B.
   1110   TF_Output feeds2[] = {TF_Output{b, 0}};
   1111   TF_Output fetches2[] = {TF_Output{plusB, 0}};
   1112   TF_Tensor* feedValues2[] = {Int32Tensor(4)};
   1113   TF_Tensor* fetchValues2[1];
   1114   TF_SessionPRun(sess, handle, feeds2, feedValues2, 1, fetches2, fetchValues2,
   1115                  1, nullptr, 0, s);
   1116   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1117   EXPECT_EQ(7, *(static_cast<int32*>(TF_TensorData(fetchValues2[0]))));
   1118   TF_DeleteTensor(feedValues2[0]);
   1119   TF_DeleteTensor(fetchValues2[0]);
   1120 
   1121   // Clean up.
   1122   TF_DeletePRunHandle(handle);
   1123   TF_DeleteSession(sess, s);
   1124   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1125   TF_DeleteGraph(graph);
   1126   TF_DeleteStatus(s);
   1127 }
   1128 
   1129 TEST(CAPI, ShapeInferenceError) {
   1130   // TF_FinishOperation should fail if the shape of the added operation cannot
   1131   // be inferred.
   1132   TF_Status* status = TF_NewStatus();
   1133   TF_Graph* graph = TF_NewGraph();
   1134 
   1135   // Create this failure by trying to add two nodes with incompatible shapes
   1136   // (A tensor with shape [2] and a tensor with shape [3] cannot be added).
   1137   const char data[] = {1, 2, 3};
   1138   const int64_t vec2_dims[] = {2};
   1139   unique_tensor_ptr vec2_tensor(
   1140       Int8Tensor(vec2_dims, TF_ARRAYSIZE(vec2_dims), data), TF_DeleteTensor);
   1141   TF_Operation* vec2 = Const(vec2_tensor.get(), graph, status, "vec2");
   1142   ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   1143 
   1144   const int64_t vec3_dims[] = {3};
   1145   unique_tensor_ptr vec3_tensor(
   1146       Int8Tensor(vec3_dims, TF_ARRAYSIZE(vec3_dims), data), TF_DeleteTensor);
   1147   TF_Operation* vec3 = Const(vec3_tensor.get(), graph, status, "vec3");
   1148   ASSERT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   1149 
   1150   TF_Operation* add = AddNoCheck(vec2, vec3, graph, status);
   1151   ASSERT_NE(TF_OK, TF_GetCode(status));
   1152   ASSERT_TRUE(add == nullptr);
   1153 
   1154   TF_DeleteGraph(graph);
   1155   TF_DeleteStatus(status);
   1156 }
   1157 
   1158 TEST(CAPI, GetOpDef) {
   1159   TF_Status* status = TF_NewStatus();
   1160   TF_Graph* graph = TF_NewGraph();
   1161   TF_Buffer* buffer = TF_NewBuffer();
   1162 
   1163   TF_GraphGetOpDef(graph, "Add", buffer, status);
   1164   ASSERT_EQ(TF_OK, TF_GetCode(status));
   1165   const OpDef* expected_op_def;
   1166   TF_ASSERT_OK(OpRegistry::Global()->LookUpOpDef("Add", &expected_op_def));
   1167   string expected_serialized;
   1168   expected_op_def->SerializeToString(&expected_serialized);
   1169   string actual_string(reinterpret_cast<const char*>(buffer->data),
   1170                        buffer->length);
   1171   EXPECT_EQ(expected_serialized, actual_string);
   1172 
   1173   TF_GraphGetOpDef(graph, "MyFakeOp", buffer, status);
   1174   EXPECT_EQ(TF_NOT_FOUND, TF_GetCode(status));
   1175   ExpectHasSubstr(TF_Message(status),
   1176                   "Op type not registered 'MyFakeOp' in binary");
   1177 
   1178   TF_DeleteBuffer(buffer);
   1179   TF_DeleteGraph(graph);
   1180   TF_DeleteStatus(status);
   1181 }
   1182 
   1183 void StringVectorToArrays(const std::vector<string>& v,
   1184                           std::unique_ptr<const void*[]>* ptrs,
   1185                           std::unique_ptr<size_t[]>* lens) {
   1186   ptrs->reset(new const void*[v.size()]);
   1187   lens->reset(new size_t[v.size()]);
   1188   for (size_t i = 0; i < v.size(); ++i) {
   1189     (*ptrs)[i] = v[i].data();
   1190     (*lens)[i] = v[i].size();
   1191   }
   1192 }
   1193 
   1194 class CApiColocationTest : public ::testing::Test {
   1195  protected:
   1196   CApiColocationTest() : s_(TF_NewStatus()), graph_(TF_NewGraph()) {}
   1197 
   1198   void SetUp() override {
   1199     feed1_ = Placeholder(graph_, s_, "feed1");
   1200     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1201 
   1202     feed2_ = Placeholder(graph_, s_, "feed2");
   1203     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1204 
   1205     constant_ = ScalarConst(10, graph_, s_);
   1206     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1207 
   1208     desc_ = TF_NewOperation(graph_, "AddN", "add");
   1209     TF_Output inputs[] = {{feed1_, 0}, {constant_, 0}};
   1210     TF_AddInputList(desc_, inputs, TF_ARRAYSIZE(inputs));
   1211   }
   1212 
   1213   ~CApiColocationTest() override {
   1214     TF_DeleteGraph(graph_);
   1215     TF_DeleteStatus(s_);
   1216   }
   1217 
   1218   void SetViaStringList(TF_OperationDescription* desc,
   1219                         const std::vector<string>& list) {
   1220     std::unique_ptr<const void*[]> list_ptrs;
   1221     std::unique_ptr<size_t[]> list_lens;
   1222     StringVectorToArrays(list, &list_ptrs, &list_lens);
   1223     TF_SetAttrStringList(desc, tensorflow::kColocationAttrName, list_ptrs.get(),
   1224                          list_lens.get(), list.size());
   1225   }
   1226 
   1227   void SetViaProto(TF_OperationDescription* desc,
   1228                    const std::vector<string>& list) {
   1229     tensorflow::AttrValue attr;
   1230     for (const string& v : list) {
   1231       attr.mutable_list()->add_s(v);
   1232     }
   1233     string bytes;
   1234     attr.SerializeToString(&bytes);
   1235     TF_SetAttrValueProto(desc, tensorflow::kColocationAttrName, bytes.data(),
   1236                          bytes.size(), s_);
   1237     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1238   }
   1239 
   1240   void VerifyCollocation(TF_Operation* op,
   1241                          const std::vector<string>& expected) {
   1242     TF_AttrMetadata m =
   1243         TF_OperationGetAttrMetadata(op, tensorflow::kColocationAttrName, s_);
   1244     if (expected.empty()) {
   1245       ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   1246       EXPECT_EQ("Operation 'add' has no attr named '_class'.",
   1247                 string(TF_Message(s_)));
   1248       return;
   1249     }
   1250     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1251     EXPECT_EQ(1, m.is_list);
   1252     EXPECT_EQ(expected.size(), m.list_size);
   1253     EXPECT_EQ(TF_ATTR_STRING, m.type);
   1254     std::vector<void*> values(expected.size());
   1255     std::vector<size_t> lens(expected.size());
   1256     std::unique_ptr<char[]> storage(new char[m.total_size]);
   1257     TF_OperationGetAttrStringList(op, tensorflow::kColocationAttrName,
   1258                                   values.data(), lens.data(), expected.size(),
   1259                                   storage.get(), m.total_size, s_);
   1260     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1261     for (int i = 0; i < expected.size(); ++i) {
   1262       EXPECT_EQ(expected[i],
   1263                 string(static_cast<const char*>(values[i]), lens[i]));
   1264     }
   1265   }
   1266 
   1267   void FinishAndVerify(TF_OperationDescription* desc,
   1268                        const std::vector<string>& expected) {
   1269     TF_Operation* op = TF_FinishOperation(desc_, s_);
   1270     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1271     VerifyCollocation(op, expected);
   1272   }
   1273 
   1274   TF_Status* s_;
   1275   TF_Graph* graph_;
   1276   TF_Operation* feed1_;
   1277   TF_Operation* feed2_;
   1278   TF_Operation* constant_;
   1279   TF_OperationDescription* desc_;
   1280 };
   1281 
   1282 TEST_F(CApiColocationTest, ColocateWith) {
   1283   TF_ColocateWith(desc_, feed1_);
   1284   FinishAndVerify(desc_, {"loc:@feed1"});
   1285 }
   1286 
   1287 TEST_F(CApiColocationTest, StringList) {
   1288   SetViaStringList(desc_, {"loc:@feed1"});
   1289   FinishAndVerify(desc_, {"loc:@feed1"});
   1290 }
   1291 
   1292 TEST_F(CApiColocationTest, Proto) {
   1293   SetViaProto(desc_, {"loc:@feed1"});
   1294   FinishAndVerify(desc_, {"loc:@feed1"});
   1295 }
   1296 
   1297 TEST_F(CApiColocationTest, ColocateWith_StringList) {
   1298   TF_ColocateWith(desc_, feed1_);
   1299   SetViaStringList(desc_, {"loc:@feed2"});
   1300   FinishAndVerify(desc_, {"loc:@feed2"});
   1301 }
   1302 
   1303 TEST_F(CApiColocationTest, ColocateWith_Proto) {
   1304   TF_ColocateWith(desc_, feed1_);
   1305   SetViaProto(desc_, {"loc:@feed2"});
   1306   FinishAndVerify(desc_, {"loc:@feed2"});
   1307 }
   1308 
   1309 TEST_F(CApiColocationTest, StringList_ColocateWith) {
   1310   SetViaStringList(desc_, {"loc:@feed2"});
   1311   TF_ColocateWith(desc_, feed1_);
   1312   FinishAndVerify(desc_, {"loc:@feed1", "loc:@feed2"});
   1313 }
   1314 
   1315 TEST_F(CApiColocationTest, Proto_ColocateWith) {
   1316   SetViaProto(desc_, {"loc:@feed2"});
   1317   TF_ColocateWith(desc_, feed1_);
   1318   FinishAndVerify(desc_, {"loc:@feed1", "loc:@feed2"});
   1319 }
   1320 
   1321 TEST_F(CApiColocationTest, ColocateWith_ColocateWith) {
   1322   TF_ColocateWith(desc_, feed1_);
   1323   TF_ColocateWith(desc_, feed2_);
   1324   FinishAndVerify(desc_, {"loc:@feed1", "loc:@feed2"});
   1325 }
   1326 
   1327 TEST_F(CApiColocationTest, Proto_StringList) {
   1328   SetViaProto(desc_, {"loc:@feed1"});
   1329   SetViaStringList(desc_, {"loc:@feed2"});
   1330   FinishAndVerify(desc_, {"loc:@feed2"});
   1331 }
   1332 
   1333 TEST_F(CApiColocationTest, StringList_Proto) {
   1334   SetViaStringList(desc_, {"loc:@feed1"});
   1335   SetViaProto(desc_, {"loc:@feed2"});
   1336   FinishAndVerify(desc_, {"loc:@feed2"});
   1337 }
   1338 
   1339 TEST_F(CApiColocationTest, ClearViaStringList) {
   1340   TF_ColocateWith(desc_, feed1_);
   1341   SetViaStringList(desc_, {});
   1342   FinishAndVerify(desc_, {});
   1343 }
   1344 
   1345 TEST_F(CApiColocationTest, ClearViaProto) {
   1346   TF_ColocateWith(desc_, feed1_);
   1347   SetViaProto(desc_, {});
   1348   FinishAndVerify(desc_, {});
   1349 }
   1350 
   1351 TEST(CAPI, SavedModel) {
   1352   // Load the saved model.
   1353   const char kSavedModel[] = "cc/saved_model/testdata/half_plus_two/00000123";
   1354   const string saved_model_dir = tensorflow::io::JoinPath(
   1355       tensorflow::testing::TensorFlowSrcRoot(), kSavedModel);
   1356   TF_SessionOptions* opt = TF_NewSessionOptions();
   1357   TF_Buffer* run_options = TF_NewBufferFromString("", 0);
   1358   TF_Buffer* metagraph = TF_NewBuffer();
   1359   TF_Status* s = TF_NewStatus();
   1360   const char* tags[] = {tensorflow::kSavedModelTagServe};
   1361   TF_Graph* graph = TF_NewGraph();
   1362   TF_Session* session = TF_LoadSessionFromSavedModel(
   1363       opt, run_options, saved_model_dir.c_str(), tags, 1, graph, metagraph, s);
   1364   TF_DeleteBuffer(run_options);
   1365   TF_DeleteSessionOptions(opt);
   1366   tensorflow::MetaGraphDef metagraph_def;
   1367   metagraph_def.ParseFromArray(metagraph->data, metagraph->length);
   1368   TF_DeleteBuffer(metagraph);
   1369 
   1370   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1371   CSession csession(session);
   1372 
   1373   // Retrieve the regression signature from meta graph def.
   1374   const auto signature_def_map = metagraph_def.signature_def();
   1375   const auto signature_def = signature_def_map.at("regress_x_to_y");
   1376 
   1377   const string input_name =
   1378       signature_def.inputs().at(tensorflow::kRegressInputs).name();
   1379   const string output_name =
   1380       signature_def.outputs().at(tensorflow::kRegressOutputs).name();
   1381 
   1382   // Write {0, 1, 2, 3} as tensorflow::Example inputs.
   1383   Tensor input(tensorflow::DT_STRING, TensorShape({4}));
   1384   for (tensorflow::int64 i = 0; i < input.NumElements(); ++i) {
   1385     tensorflow::Example example;
   1386     auto* feature_map = example.mutable_features()->mutable_feature();
   1387     (*feature_map)["x"].mutable_float_list()->add_value(i);
   1388     input.flat<string>()(i) = example.SerializeAsString();
   1389   }
   1390 
   1391   const tensorflow::string input_op_name(
   1392       tensorflow::ParseTensorName(input_name).first);
   1393   TF_Operation* input_op =
   1394       TF_GraphOperationByName(graph, input_op_name.c_str());
   1395   ASSERT_TRUE(input_op != nullptr);
   1396   csession.SetInputs({{input_op, TF_TensorFromTensor(input, s)}});
   1397   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1398 
   1399   const tensorflow::string output_op_name(
   1400       tensorflow::ParseTensorName(output_name).first);
   1401   TF_Operation* output_op =
   1402       TF_GraphOperationByName(graph, output_op_name.c_str());
   1403   ASSERT_TRUE(output_op != nullptr);
   1404   csession.SetOutputs({output_op});
   1405   csession.Run(s);
   1406   ASSERT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1407 
   1408   TF_Tensor* out = csession.output_tensor(0);
   1409   ASSERT_TRUE(out != nullptr);
   1410   EXPECT_EQ(TF_FLOAT, TF_TensorType(out));
   1411   EXPECT_EQ(2, TF_NumDims(out));
   1412   EXPECT_EQ(4, TF_Dim(out, 0));
   1413   EXPECT_EQ(1, TF_Dim(out, 1));
   1414   float* values = static_cast<float*>(TF_TensorData(out));
   1415   // These values are defined to be (input / 2) + 2.
   1416   EXPECT_EQ(2, values[0]);
   1417   EXPECT_EQ(2.5, values[1]);
   1418   EXPECT_EQ(3, values[2]);
   1419   EXPECT_EQ(3.5, values[3]);
   1420 
   1421   csession.CloseAndDelete(s);
   1422   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1423   TF_DeleteGraph(graph);
   1424   TF_DeleteStatus(s);
   1425 }
   1426 
   1427 TEST(CAPI, SavedModelNullArgsAreValid) {
   1428   const char kSavedModel[] = "cc/saved_model/testdata/half_plus_two/00000123";
   1429   const string saved_model_dir = tensorflow::io::JoinPath(
   1430       tensorflow::testing::TensorFlowSrcRoot(), kSavedModel);
   1431   TF_SessionOptions* opt = TF_NewSessionOptions();
   1432   TF_Status* s = TF_NewStatus();
   1433   const char* tags[] = {tensorflow::kSavedModelTagServe};
   1434   TF_Graph* graph = TF_NewGraph();
   1435   // NULL run_options and meta_graph_def should work.
   1436   TF_Session* session = TF_LoadSessionFromSavedModel(
   1437       opt, nullptr, saved_model_dir.c_str(), tags, 1, graph, nullptr, s);
   1438   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1439   TF_DeleteSessionOptions(opt);
   1440   TF_CloseSession(session, s);
   1441   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1442   TF_DeleteSession(session, s);
   1443   EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1444   TF_DeleteGraph(graph);
   1445   TF_DeleteStatus(s);
   1446 }
   1447 
   1448 TEST(CAPI, DeletingNullPointerIsSafe) {
   1449   TF_Status* status = TF_NewStatus();
   1450 
   1451   TF_DeleteStatus(nullptr);
   1452   TF_DeleteBuffer(nullptr);
   1453   TF_DeleteTensor(nullptr);
   1454   TF_DeleteSessionOptions(nullptr);
   1455   TF_DeleteGraph(nullptr);
   1456   TF_DeleteImportGraphDefOptions(nullptr);
   1457   TF_DeleteImportGraphDefResults(nullptr);
   1458   TF_DeleteFunction(nullptr);
   1459   TF_DeleteSession(nullptr, status);
   1460   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   1461   TF_DeletePRunHandle(nullptr);
   1462   TF_DeleteDeprecatedSession(nullptr, status);
   1463   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   1464   TF_DeleteDeviceList(nullptr);
   1465   TF_DeleteLibraryHandle(nullptr);
   1466   TF_DeleteApiDefMap(nullptr);
   1467 
   1468   TF_DeleteStatus(status);
   1469 }
   1470 
   1471 TEST(CAPI, TestBitcastFrom_Reshape) {
   1472   int64_t dims[] = {2, 3};
   1473   TF_Tensor* a =
   1474       TF_AllocateTensor(TF_UINT64, dims, 2, 6 * TF_DataTypeSize(TF_UINT64));
   1475   TF_Tensor* b =
   1476       TF_AllocateTensor(TF_UINT64, nullptr, 0, TF_DataTypeSize(TF_UINT64));
   1477   EXPECT_NE(a, nullptr);
   1478   EXPECT_NE(b, nullptr);
   1479 
   1480   EXPECT_EQ(6, TF_TensorElementCount(a));
   1481   EXPECT_EQ(1, TF_TensorElementCount(b));
   1482   EXPECT_EQ(6 * TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(a));
   1483   EXPECT_EQ(TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(b));
   1484 
   1485   int64_t new_dims[] = {3, 2};
   1486   TF_Status* status = TF_NewStatus();
   1487   TF_TensorBitcastFrom(a, TF_UINT64, b, new_dims, 2, status);
   1488   ASSERT_EQ(TF_OK, TF_GetCode(status));
   1489   TF_DeleteStatus(status);
   1490 
   1491   EXPECT_EQ(6, TF_TensorElementCount(a));
   1492   EXPECT_EQ(6, TF_TensorElementCount(b));
   1493   EXPECT_EQ(6 * TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(a));
   1494   EXPECT_EQ(6 * TF_DataTypeSize(TF_UINT64), TF_TensorByteSize(b));
   1495 
   1496   // Check that a write to one tensor shows up in the other.
   1497   *(static_cast<int64_t*>(TF_TensorData(a))) = 4;
   1498   EXPECT_EQ(4, *(static_cast<int64_t*>(TF_TensorData(b))));
   1499   *(static_cast<int64_t*>(TF_TensorData(b))) = 6;
   1500   EXPECT_EQ(6, *(static_cast<int64_t*>(TF_TensorData(a))));
   1501 
   1502   TF_DeleteTensor(a);
   1503   TF_DeleteTensor(b);
   1504 }
   1505 
   1506 REGISTER_OP("TestOpWithNoGradient")
   1507     .Input("x: T")
   1508     .Output("y: T")
   1509     .Attr("T: {float, double}")
   1510     .Doc(R"doc(
   1511 Test op with no grad registered.
   1512 
   1513 x: input
   1514 y: output
   1515 )doc")
   1516     .SetShapeFn(tensorflow::shape_inference::UnknownShape);
   1517 
   1518 class CApiGradientsTest : public ::testing::Test {
   1519  protected:
   1520   CApiGradientsTest()
   1521       : s_(TF_NewStatus()),
   1522         graph_(TF_NewGraph()),
   1523         expected_graph_(TF_NewGraph()) {}
   1524 
   1525   ~CApiGradientsTest() override {
   1526     TF_DeleteGraph(graph_);
   1527     TF_DeleteGraph(expected_graph_);
   1528     TF_DeleteStatus(s_);
   1529   }
   1530 
   1531   void TestGradientsSuccess(bool grad_inputs_provided) {
   1532     TF_Output inputs[2];
   1533     TF_Output outputs[1];
   1534     TF_Output grad_outputs[2];
   1535     TF_Output expected_grad_outputs[2];
   1536 
   1537     BuildSuccessGraph(inputs, outputs);
   1538     BuildExpectedGraph(grad_inputs_provided, expected_grad_outputs);
   1539 
   1540     AddGradients(grad_inputs_provided, nullptr, inputs, 2, outputs, 1,
   1541                  grad_outputs);
   1542     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1543 
   1544     // Compare that the graphs match.
   1545     GraphDef expected_gdef;
   1546     GraphDef gdef;
   1547     EXPECT_TRUE(GetGraphDef(expected_graph_, &expected_gdef));
   1548     EXPECT_TRUE(GetGraphDef(graph_, &gdef));
   1549     TF_EXPECT_GRAPH_EQ(expected_gdef, gdef);
   1550 
   1551     // Compare that the output of the gradients of both graphs match.
   1552     RunGraphsAndCompareOutputs(grad_outputs, expected_grad_outputs);
   1553   }
   1554 
   1555   void TestGradientsError(bool grad_inputs_provided) {
   1556     TF_Output inputs[1];
   1557     TF_Output outputs[1];
   1558     TF_Output grad_outputs[1];
   1559 
   1560     BuildErrorGraph(inputs, outputs);
   1561 
   1562     AddGradients(grad_inputs_provided, nullptr, inputs, 1, outputs, 1,
   1563                  grad_outputs);
   1564 
   1565     string expected_msg =
   1566         "No gradient defined for op: TestOpWithNoGradient. Please see "
   1567         "https://www.tensorflow.org/code/"
   1568         "tensorflow/cc/gradients/README.md"
   1569         " for instructions on how to add C++ gradients.";
   1570     EXPECT_EQ(expected_msg, TF_Message(s_));
   1571   }
   1572 
   1573   // Run the graph and ensure that the gradient values are as expected.
   1574   void RunGraphsAndCompareOutputs(TF_Output* grad_outputs,
   1575                                   TF_Output* expected_grad_outputs) {
   1576     std::unique_ptr<CSession> csession(new CSession(graph_, s_));
   1577     std::unique_ptr<CSession> expected_csession(
   1578         new CSession(expected_graph_, s_));
   1579 
   1580     std::vector<TF_Output> grad_outputs_vec;
   1581     grad_outputs_vec.assign(grad_outputs, grad_outputs + 2);
   1582     csession->SetOutputs(grad_outputs_vec);
   1583     csession->Run(s_);
   1584     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1585     TF_Tensor* out0 = csession->output_tensor(0);
   1586     TF_Tensor* out1 = csession->output_tensor(1);
   1587 
   1588     std::vector<TF_Output> expected_grad_outputs_vec;
   1589     expected_grad_outputs_vec.assign(expected_grad_outputs,
   1590                                      expected_grad_outputs + 2);
   1591     expected_csession->SetOutputs(expected_grad_outputs_vec);
   1592     expected_csession->Run(s_);
   1593     ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1594     TF_Tensor* expected_out0 = expected_csession->output_tensor(0);
   1595     TF_Tensor* expected_out1 = expected_csession->output_tensor(1);
   1596 
   1597     CompareTensors(out0, expected_out0);
   1598     CompareTensors(out1, expected_out1);
   1599   }
   1600 
   1601   void CompareTensors(TF_Tensor* a, TF_Tensor* b) {
   1602     float* a_data = static_cast<float*>(TF_TensorData(a));
   1603     float* b_data = static_cast<float*>(TF_TensorData(b));
   1604     EXPECT_EQ(*a_data, *b_data);
   1605   }
   1606 
   1607   void AddGradients(bool grad_inputs_provided, const char* prefix,
   1608                     TF_Output* inputs, int ninputs, TF_Output* outputs,
   1609                     int noutputs, TF_Output* grad_outputs) {
   1610     if (grad_inputs_provided) {
   1611       TF_Output grad_inputs[1];
   1612       const float grad_inputs_val[] = {1.0, 1.0, 1.0, 1.0};
   1613       TF_Operation* grad_inputs_op =
   1614           FloatConst2x2(graph_, s_, grad_inputs_val, "GradInputs");
   1615       grad_inputs[0] = TF_Output{grad_inputs_op, 0};
   1616       TF_AddGradientsWithPrefix(graph_, prefix, outputs, noutputs, inputs,
   1617                                 ninputs, grad_inputs, s_, grad_outputs);
   1618     } else {
   1619       TF_AddGradientsWithPrefix(graph_, prefix, outputs, noutputs, inputs,
   1620                                 ninputs, nullptr, s_, grad_outputs);
   1621     }
   1622   }
   1623 
   1624   void BuildErrorGraph(TF_Output* inputs, TF_Output* outputs) {
   1625     const float const0_val[] = {1.0, 2.0, 3.0, 4.0};
   1626     TF_Operation* const0 = FloatConst2x2(graph_, s_, const0_val, "Const_0");
   1627     TF_Operation* nograd = NoGradientOp(graph_, s_, const0, "NoGrad");
   1628     inputs[0] = TF_Output{const0, 0};
   1629     outputs[0] = TF_Output{nograd, 0};
   1630     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1631   }
   1632 
   1633   void BuildSuccessGraph(TF_Output* inputs, TF_Output* outputs) {
   1634     // Construct the following graph:
   1635     //            |
   1636     //           z|
   1637     //            |
   1638     //          MatMul
   1639     //         /       \
   1640     //        ^         ^
   1641     //        |         |
   1642     //       x|        y|
   1643     //        |         |
   1644     //        |         |
   1645     //      Const_0    Const_1
   1646     //
   1647     const float const0_val[] = {1.0, 2.0, 3.0, 4.0};
   1648     const float const1_val[] = {1.0, 0.0, 0.0, 1.0};
   1649     TF_Operation* const0 = FloatConst2x2(graph_, s_, const0_val, "Const_0");
   1650     TF_Operation* const1 = FloatConst2x2(graph_, s_, const1_val, "Const_1");
   1651     TF_Operation* matmul = MatMul(graph_, s_, const0, const1, "MatMul");
   1652     inputs[0] = TF_Output{const0, 0};
   1653     inputs[1] = TF_Output{const1, 0};
   1654     outputs[0] = TF_Output{matmul, 0};
   1655     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1656   }
   1657 
   1658   void BuildExpectedGraph(bool grad_inputs_provided,
   1659                           TF_Output* expected_grad_outputs) {
   1660     // The expected graph looks like this if grad_inputs_provided.
   1661     // If grad_inputs_provided is false, Const_0 will be a OnesLike op.
   1662     //      ^             ^
   1663     //    dy|           dx|        // MatMul Gradient Graph
   1664     //      |             |
   1665     //   MatMul_2      MatMul_1
   1666     //   ^   ^          ^    ^
   1667     //   |   |----------|    |
   1668     //   |        ^          |
   1669     //   |      dz|          |
   1670     //   |        |          |
   1671     //   |     Const_3       |
   1672     //   |                   |
   1673     //   |        ^          |
   1674     //   |       z|          |     // MatMul Forward Graph
   1675     //   |        |          |
   1676     //   |      MatMul       |
   1677     //   |     /       \     |
   1678     //   |    ^         ^    |
   1679     //   |    |         |    |
   1680     //   |---x|        y|----|
   1681     //        |         |
   1682     //        |         |
   1683     //      Const_0   Const_1
   1684     //
   1685     const float const0_val[] = {1.0, 2.0, 3.0, 4.0};
   1686     const float const1_val[] = {1.0, 0.0, 0.0, 1.0};
   1687     TF_Operation* const0 =
   1688         FloatConst2x2(expected_graph_, s_, const0_val, "Const_0");
   1689     TF_Operation* const1 =
   1690         FloatConst2x2(expected_graph_, s_, const1_val, "Const_1");
   1691     TF_Operation* matmul =
   1692         MatMul(expected_graph_, s_, const0, const1, "MatMul");
   1693 
   1694     TF_Operation* const3;
   1695     if (grad_inputs_provided) {
   1696       const float const3_val[] = {1.0, 1.0, 1.0, 1.0};
   1697       const3 = FloatConst2x2(expected_graph_, s_, const3_val, "GradInputs");
   1698     } else {
   1699       const3 = OnesLike(expected_graph_, s_, matmul, "gradients/OnesLike");
   1700     }
   1701 
   1702     TF_Operation* matmul1 = MatMul(expected_graph_, s_, const3, const1,
   1703                                    "gradients/MatMul", false, true);
   1704     TF_Operation* matmul2 = MatMul(expected_graph_, s_, const0, const3,
   1705                                    "gradients/MatMul_1", true, false);
   1706     expected_grad_outputs[0] = {matmul1, 0};
   1707     expected_grad_outputs[1] = {matmul2, 0};
   1708   }
   1709 
   1710   TF_Tensor* FloatTensor2x2(const float* values) {
   1711     const int64_t dims[2] = {2, 2};
   1712     TF_Tensor* t = TF_AllocateTensor(TF_FLOAT, dims, 2, sizeof(float) * 4);
   1713     memcpy(TF_TensorData(t), values, sizeof(float) * 4);
   1714     return t;
   1715   }
   1716 
   1717   TF_Operation* FloatConst2x2(TF_Graph* graph, TF_Status* s,
   1718                               const float* values, const char* name) {
   1719     unique_tensor_ptr tensor(FloatTensor2x2(values), TF_DeleteTensor);
   1720     TF_OperationDescription* desc = TF_NewOperation(graph, "Const", name);
   1721     TF_SetAttrTensor(desc, "value", tensor.get(), s);
   1722     if (TF_GetCode(s) != TF_OK) return nullptr;
   1723     TF_SetAttrType(desc, "dtype", TF_FLOAT);
   1724     TF_Operation* op = TF_FinishOperation(desc, s);
   1725     EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1726     return op;
   1727   }
   1728 
   1729   TF_Operation* MatMul(TF_Graph* graph, TF_Status* s, TF_Operation* l,
   1730                        TF_Operation* r, const char* name,
   1731                        bool transpose_a = false, bool transpose_b = false) {
   1732     TF_OperationDescription* desc = TF_NewOperation(graph, "MatMul", name);
   1733     if (transpose_a) {
   1734       TF_SetAttrBool(desc, "transpose_a", 1);
   1735     }
   1736     if (transpose_b) {
   1737       TF_SetAttrBool(desc, "transpose_b", 1);
   1738     }
   1739     TF_AddInput(desc, {l, 0});
   1740     TF_AddInput(desc, {r, 0});
   1741     TF_Operation* op = TF_FinishOperation(desc, s);
   1742     EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1743     return op;
   1744   }
   1745 
   1746   TF_Operation* OnesLike(TF_Graph* graph, TF_Status* s, TF_Operation* in,
   1747                          const char* name) {
   1748     TF_OperationDescription* desc = TF_NewOperation(graph, "OnesLike", name);
   1749     TF_AddInput(desc, {in, 0});
   1750     TF_Operation* op = TF_FinishOperation(desc, s);
   1751     EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1752     return op;
   1753   }
   1754 
   1755   TF_Operation* NoGradientOp(TF_Graph* graph, TF_Status* s, TF_Operation* in,
   1756                              const char* name) {
   1757     TF_OperationDescription* desc =
   1758         TF_NewOperation(graph, "TestOpWithNoGradient", name);
   1759     TF_AddInput(desc, {in, 0});
   1760     TF_Operation* op = TF_FinishOperation(desc, s);
   1761     EXPECT_EQ(TF_OK, TF_GetCode(s)) << TF_Message(s);
   1762     return op;
   1763   }
   1764 
   1765   void BuildGraphAndAddGradientsWithPrefixes(const char* prefix1,
   1766                                              const char* prefix2 = nullptr) {
   1767     TF_Output inputs[2];
   1768     TF_Output outputs[1];
   1769     TF_Output grad_outputs[2];
   1770 
   1771     BuildSuccessGraph(inputs, outputs);
   1772 
   1773     AddGradients(false, prefix1, inputs, 2, outputs, 1, grad_outputs);
   1774     if (prefix2 != nullptr) {
   1775       AddGradients(false, prefix2, inputs, 2, outputs, 1, grad_outputs);
   1776     }
   1777   }
   1778 
   1779   TF_Status* s_;
   1780   TF_Graph* graph_;
   1781   TF_Graph* expected_graph_;
   1782 };
   1783 
   1784 TEST_F(CApiGradientsTest, Gradients_GradInputs) { TestGradientsSuccess(true); }
   1785 
   1786 TEST_F(CApiGradientsTest, Gradients_NoGradInputs) {
   1787   TestGradientsSuccess(false);
   1788 }
   1789 
   1790 TEST_F(CApiGradientsTest, OpWithNoGradientRegistered_GradInputs) {
   1791   TestGradientsError(true);
   1792 }
   1793 
   1794 TEST_F(CApiGradientsTest, OpWithNoGradientRegistered_NoGradInputs) {
   1795   TestGradientsError(false);
   1796 }
   1797 
   1798 TEST_F(CApiGradientsTest, GradientsPrefix_PrefixIsOk) {
   1799   BuildGraphAndAddGradientsWithPrefixes("gradients");
   1800   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1801 }
   1802 
   1803 TEST_F(CApiGradientsTest, GradientsPrefix_TwoGradientsWithDistinctPrefixes) {
   1804   BuildGraphAndAddGradientsWithPrefixes("gradients", "gradients_1");
   1805   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1806 }
   1807 
   1808 TEST_F(CApiGradientsTest, GradientsPrefix_TwoGradientsInSameScope) {
   1809   BuildGraphAndAddGradientsWithPrefixes("scope/gradients", "scope/gradients_1");
   1810   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1811 }
   1812 
   1813 TEST_F(CApiGradientsTest, GradientsPrefix_TwoGradientsInDifferentScopes) {
   1814   BuildGraphAndAddGradientsWithPrefixes("scope/gradients", "scope_1/gradients");
   1815   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1816 }
   1817 
   1818 TEST_F(CApiGradientsTest, GradientsPrefix_2ndGradientsAsSubScopeOf1st) {
   1819   BuildGraphAndAddGradientsWithPrefixes("gradients", "gradients/sub");
   1820   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1821 }
   1822 
   1823 TEST_F(CApiGradientsTest, GradientsPrefix_PrefixMatchesExistingNodeName) {
   1824   BuildGraphAndAddGradientsWithPrefixes("Const_0");
   1825   ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   1826 }
   1827 
   1828 TEST_F(CApiGradientsTest, GradientsPrefix_TwoGradientsWithIdenticalPrefixes) {
   1829   BuildGraphAndAddGradientsWithPrefixes("gradients", "gradients");
   1830   ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   1831 }
   1832 
   1833 TEST_F(CApiGradientsTest, GradientsPrefix_2ndGradientsMatchingNodeOf1st) {
   1834   BuildGraphAndAddGradientsWithPrefixes("gradients", "gradients/MatMul");
   1835   ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   1836 }
   1837 
   1838 TEST_F(CApiGradientsTest, GradientsPrefix_1stGradientsMatchingNodeOf2nd) {
   1839   BuildGraphAndAddGradientsWithPrefixes("gradients/MatMul", "gradients");
   1840   ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   1841 }
   1842 
   1843 TEST_F(CApiGradientsTest, GradientsPrefix_2ndGradientsAsParentScopeOf1st) {
   1844   BuildGraphAndAddGradientsWithPrefixes("gradients/sub", "gradients");
   1845   ASSERT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   1846 }
   1847 
   1848 void ScalarFloatFromTensor(const TF_Tensor* t, float* f) {
   1849   ASSERT_TRUE(t != nullptr);
   1850   ASSERT_EQ(TF_FLOAT, TF_TensorType(t));
   1851   ASSERT_EQ(0, TF_NumDims(t));
   1852   ASSERT_EQ(4, TF_TensorByteSize(t));
   1853   float* p = static_cast<float*>(TF_TensorData(t));
   1854   *f = *p;
   1855 }
   1856 
   1857 TEST_F(CApiGradientsTest, MultipleCallsToAddGradients) {
   1858   const float X = 3.0f, Y = 7.0f;
   1859   TF_Operation* x = Placeholder(graph_, s_, "x", TF_FLOAT);
   1860   TF_Operation* y = Placeholder(graph_, s_, "y", TF_FLOAT);
   1861   TF_Operation* xy = Mul(x, y, graph_, s_, "xy");
   1862   TF_Output dxy_dx, dxy_dy;
   1863 
   1864   TF_Output outputs[1] = {{xy, 0}};
   1865   TF_Output inputs[1] = {{x, 0}};
   1866   TF_AddGradients(graph_, outputs, 1, inputs, 1, nullptr, s_, &dxy_dx);
   1867   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1868 
   1869   inputs[0] = {y, 0};
   1870   TF_AddGradients(graph_, outputs, 1, inputs, 1, nullptr, s_, &dxy_dy);
   1871   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1872 
   1873   TF_SessionOptions* opts = TF_NewSessionOptions();
   1874   TF_Session* sess = TF_NewSession(graph_, opts, s_);
   1875   TF_DeleteSessionOptions(opts);
   1876   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1877 
   1878   TF_Output feeds[] = {{x, 0}, {y, 0}};
   1879   TF_Tensor* feedValues[] = {FloatTensor(X), FloatTensor(Y)};
   1880   TF_Output fetches[] = {dxy_dx, dxy_dy};
   1881   TF_Tensor* fetchValues[] = {nullptr, nullptr};
   1882 
   1883   TF_SessionRun(sess, nullptr /* run_options */, feeds, feedValues, 2, fetches,
   1884                 fetchValues, 2, nullptr /* target_opers */, 0,
   1885                 nullptr /* run_metadata */, s_);
   1886   TF_DeleteTensor(feedValues[0]);
   1887   TF_DeleteTensor(feedValues[1]);
   1888   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1889   TF_DeleteSession(sess, s_);
   1890   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1891 
   1892   float dxy_dxValue = 0.0f, dxy_dyValue = 0.0f;
   1893   ScalarFloatFromTensor(fetchValues[0], &dxy_dxValue);
   1894   EXPECT_EQ(Y, dxy_dxValue);
   1895 
   1896   ScalarFloatFromTensor(fetchValues[1], &dxy_dyValue);
   1897   EXPECT_EQ(X, dxy_dyValue);
   1898 
   1899   TF_DeleteTensor(fetchValues[0]);
   1900   TF_DeleteTensor(fetchValues[1]);
   1901 }
   1902 
   1903 // REGISTER_OP for CApiAttributesTest test cases.
   1904 // Registers two ops, each with a single attribute called 'v'.
   1905 // The attribute in one op will have a type 'type', the other
   1906 // will have list(type).
   1907 #define ATTR_TEST_REGISTER_OP(type)                           \
   1908   REGISTER_OP("CApiAttributesTestOp" #type)                   \
   1909       .Attr("v: " #type)                                      \
   1910       .SetShapeFn(tensorflow::shape_inference::UnknownShape); \
   1911   REGISTER_OP("CApiAttributesTestOpList" #type)               \
   1912       .Attr("v: list(" #type ")")                             \
   1913       .SetShapeFn(tensorflow::shape_inference::UnknownShape)
   1914 ATTR_TEST_REGISTER_OP(string);
   1915 ATTR_TEST_REGISTER_OP(int);
   1916 ATTR_TEST_REGISTER_OP(float);
   1917 ATTR_TEST_REGISTER_OP(bool);
   1918 ATTR_TEST_REGISTER_OP(type);
   1919 ATTR_TEST_REGISTER_OP(shape);
   1920 ATTR_TEST_REGISTER_OP(tensor);
   1921 #undef ATTR_TEST_REGISTER_OP
   1922 
   1923 class CApiAttributesTest : public ::testing::Test {
   1924  protected:
   1925   CApiAttributesTest()
   1926       : s_(TF_NewStatus()), graph_(TF_NewGraph()), counter_(0) {}
   1927 
   1928   ~CApiAttributesTest() override {
   1929     TF_DeleteGraph(graph_);
   1930     TF_DeleteStatus(s_);
   1931   }
   1932 
   1933   TF_OperationDescription* init(string type) {
   1934     // Construct op_name to match the name used by REGISTER_OP in the
   1935     // ATTR_TEST_REGISTER calls above.
   1936     string op_name = "CApiAttributesTestOp";
   1937     if (type.find("list(") == 0) {
   1938       op_name += "List";
   1939       type = type.replace(0, 5, "");
   1940       type = type.replace(type.size() - 1, 1, "");
   1941     }
   1942     op_name += type;
   1943     return TF_NewOperation(
   1944         graph_, op_name.c_str(),
   1945         ::tensorflow::strings::StrCat("name", counter_++).c_str());
   1946   }
   1947 
   1948   TF_Status* s_;
   1949 
   1950  private:
   1951   TF_Graph* graph_;
   1952   int counter_;
   1953 };
   1954 
   1955 // Helper macros for the TF_OperationGetAttr* tests.
   1956 // TODO(ashankar): Use gmock matchers instead?
   1957 // (https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#writing-new-parameterized-matchers-quickly)
   1958 // That will require setting up the tensorflow build with gmock.
   1959 #define EXPECT_TF_META(attr_name, expected_list_size, expected_type, \
   1960                        expected_total_size)                          \
   1961   do {                                                               \
   1962     auto m = TF_OperationGetAttrMetadata(oper, attr_name, s_);       \
   1963     EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);              \
   1964     const unsigned char e = expected_list_size >= 0 ? 1 : 0;         \
   1965     EXPECT_EQ(e, m.is_list);                                         \
   1966     EXPECT_EQ(expected_list_size, m.list_size);                      \
   1967     EXPECT_EQ(expected_type, m.type);                                \
   1968     EXPECT_EQ(expected_total_size, m.total_size);                    \
   1969   } while (0)
   1970 
   1971 TEST_F(CApiAttributesTest, String) {
   1972   auto desc = init("string");
   1973   TF_SetAttrString(desc, "v", "bunny", 5);
   1974 
   1975   auto oper = TF_FinishOperation(desc, s_);
   1976   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1977   EXPECT_TF_META("v", -1, TF_ATTR_STRING, 5);
   1978   std::unique_ptr<char[]> value(new char[5]);
   1979 
   1980   TF_OperationGetAttrString(oper, "v", value.get(), 5, s_);
   1981   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   1982   EXPECT_EQ("bunny", string(static_cast<const char*>(value.get()), 5));
   1983 }
   1984 
   1985 TEST_F(CApiAttributesTest, StringList) {
   1986   std::vector<string> list = {"bugs", "bunny", "duck"};
   1987   std::unique_ptr<const void*[]> list_ptrs;
   1988   std::unique_ptr<size_t[]> list_lens;
   1989   StringVectorToArrays(list, &list_ptrs, &list_lens);
   1990   int list_total_size = 0;
   1991   for (const auto& s : list) {
   1992     list_total_size += s.size();
   1993   }
   1994 
   1995   auto desc = init("list(string)");
   1996   TF_SetAttrStringList(desc, "v", list_ptrs.get(), list_lens.get(),
   1997                        list.size());
   1998 
   1999   auto oper = TF_FinishOperation(desc, s_);
   2000   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2001 
   2002   EXPECT_TF_META("v", list.size(), TF_ATTR_STRING, list_total_size);
   2003   std::unique_ptr<void*[]> values(new void*[list.size()]);
   2004   std::unique_ptr<size_t[]> lens(new size_t[list.size()]);
   2005   std::unique_ptr<char[]> storage(new char[list_total_size]);
   2006   TF_OperationGetAttrStringList(oper, "v", values.get(), lens.get(),
   2007                                 list.size(), storage.get(), list_total_size,
   2008                                 s_);
   2009   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2010   for (size_t i = 0; i < list.size(); ++i) {
   2011     EXPECT_EQ(list[i].size(), lens[i]) << i;
   2012     EXPECT_EQ(list[i], string(static_cast<const char*>(values[i]), lens[i]))
   2013         << i;
   2014   }
   2015 }
   2016 
   2017 TEST_F(CApiAttributesTest, Int) {
   2018   auto desc = init("int");
   2019   TF_SetAttrInt(desc, "v", 31415);
   2020 
   2021   auto oper = TF_FinishOperation(desc, s_);
   2022   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2023   EXPECT_TF_META("v", -1, TF_ATTR_INT, -1);
   2024 
   2025   int64_t value;
   2026   TF_OperationGetAttrInt(oper, "v", &value, s_);
   2027   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2028   EXPECT_EQ(31415, value);
   2029 }
   2030 
   2031 TEST_F(CApiAttributesTest, IntList) {
   2032   const int64_t list[] = {1, 2, 3, 4};
   2033   const size_t list_size = TF_ARRAYSIZE(list);
   2034 
   2035   auto desc = init("list(int)");
   2036   TF_SetAttrIntList(desc, "v", list, list_size);
   2037 
   2038   auto oper = TF_FinishOperation(desc, s_);
   2039   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2040 
   2041   int64_t values[list_size];
   2042   EXPECT_TF_META("v", list_size, TF_ATTR_INT, -1);
   2043   TF_OperationGetAttrIntList(oper, "v", values, list_size, s_);
   2044   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2045   EXPECT_TRUE(std::equal(std::begin(list), std::end(list), std::begin(values)));
   2046 }
   2047 
   2048 TEST_F(CApiAttributesTest, Float) {
   2049   auto desc = init("float");
   2050   TF_SetAttrFloat(desc, "v", 2.718);
   2051 
   2052   auto oper = TF_FinishOperation(desc, s_);
   2053   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2054   EXPECT_TF_META("v", -1, TF_ATTR_FLOAT, -1);
   2055 
   2056   float value;
   2057   TF_OperationGetAttrFloat(oper, "v", &value, s_);
   2058   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2059   EXPECT_FLOAT_EQ(2.718, value);
   2060 }
   2061 
   2062 TEST_F(CApiAttributesTest, FloatList) {
   2063   const float list[] = {1.414, 2.718, 3.1415};
   2064   const size_t list_size = TF_ARRAYSIZE(list);
   2065 
   2066   auto desc = init("list(float)");
   2067   TF_SetAttrFloatList(desc, "v", list, list_size);
   2068 
   2069   auto oper = TF_FinishOperation(desc, s_);
   2070   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2071 
   2072   float values[list_size];
   2073   EXPECT_TF_META("v", list_size, TF_ATTR_FLOAT, -1);
   2074   TF_OperationGetAttrFloatList(oper, "v", values, list_size, s_);
   2075   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2076   EXPECT_TRUE(std::equal(std::begin(list), std::end(list), std::begin(values)));
   2077 }
   2078 
   2079 TEST_F(CApiAttributesTest, Bool) {
   2080   auto desc = init("bool");
   2081   TF_SetAttrBool(desc, "v", 1);
   2082 
   2083   auto oper = TF_FinishOperation(desc, s_);
   2084   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2085   EXPECT_TF_META("v", -1, TF_ATTR_BOOL, -1);
   2086 
   2087   unsigned char value;
   2088   TF_OperationGetAttrBool(oper, "v", &value, s_);
   2089   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2090   EXPECT_EQ(1, value);
   2091 }
   2092 
   2093 TEST_F(CApiAttributesTest, BoolList) {
   2094   const unsigned char list[] = {0, 1, 1, 0, 0, 1, 1};
   2095   const size_t list_size = TF_ARRAYSIZE(list);
   2096 
   2097   auto desc = init("list(bool)");
   2098   TF_SetAttrBoolList(desc, "v", list, list_size);
   2099 
   2100   auto oper = TF_FinishOperation(desc, s_);
   2101   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2102 
   2103   unsigned char values[list_size];
   2104   EXPECT_TF_META("v", list_size, TF_ATTR_BOOL, -1);
   2105   TF_OperationGetAttrBoolList(oper, "v", values, list_size, s_);
   2106   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2107   EXPECT_TRUE(std::equal(std::begin(list), std::end(list), std::begin(values)));
   2108 }
   2109 
   2110 TEST_F(CApiAttributesTest, Type) {
   2111   auto desc = init("type");
   2112   TF_SetAttrType(desc, "v", TF_COMPLEX128);
   2113 
   2114   auto oper = TF_FinishOperation(desc, s_);
   2115   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2116   EXPECT_TF_META("v", -1, TF_ATTR_TYPE, -1);
   2117 
   2118   TF_DataType value;
   2119   TF_OperationGetAttrType(oper, "v", &value, s_);
   2120   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2121   EXPECT_EQ(TF_COMPLEX128, value);
   2122 }
   2123 
   2124 TEST_F(CApiAttributesTest, TypeList) {
   2125   const TF_DataType list[] = {TF_FLOAT, TF_DOUBLE, TF_HALF, TF_COMPLEX128};
   2126   const size_t list_size = TF_ARRAYSIZE(list);
   2127 
   2128   auto desc = init("list(type)");
   2129   TF_SetAttrTypeList(desc, "v", list, list_size);
   2130 
   2131   auto oper = TF_FinishOperation(desc, s_);
   2132   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2133 
   2134   TF_DataType values[list_size];
   2135   EXPECT_TF_META("v", list_size, TF_ATTR_TYPE, -1);
   2136   TF_OperationGetAttrTypeList(oper, "v", values, list_size, s_);
   2137   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2138   EXPECT_TRUE(std::equal(std::begin(list), std::end(list), std::begin(values)));
   2139 }
   2140 
   2141 TEST_F(CApiAttributesTest, Shape) {
   2142   // Unknown shape
   2143   auto desc = init("shape");
   2144   TF_SetAttrShape(desc, "v", nullptr, -1);
   2145   auto oper = TF_FinishOperation(desc, s_);
   2146   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2147   EXPECT_TF_META("v", -1, TF_ATTR_SHAPE, -1);
   2148   TF_OperationGetAttrShape(oper, "v", nullptr, 10, s_);
   2149   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2150 
   2151   // Partially specified shape
   2152   const int64_t partial_shape[] = {17, -1};
   2153   const size_t sz = TF_ARRAYSIZE(partial_shape);
   2154   desc = init("shape");
   2155   TF_SetAttrShape(desc, "v", partial_shape, sz);
   2156   oper = TF_FinishOperation(desc, s_);
   2157   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2158   EXPECT_TF_META("v", -1, TF_ATTR_SHAPE, sz);
   2159   int64_t values[sz];
   2160   TF_OperationGetAttrShape(oper, "v", values, sz, s_);
   2161   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2162   EXPECT_TRUE(
   2163       std::equal(std::begin(partial_shape), std::end(partial_shape), values));
   2164 }
   2165 
   2166 TEST_F(CApiAttributesTest, ShapeList) {
   2167   const int64_t shape_1[] = {1, 3};
   2168   const int64_t shape_2[] = {2, 4, 6};
   2169   const int64_t* list[] = {&shape_1[0], &shape_2[0]};
   2170   const size_t list_size = TF_ARRAYSIZE(list);
   2171   const int ndims[] = {TF_ARRAYSIZE(shape_1), TF_ARRAYSIZE(shape_2)};
   2172   const int total_ndims = 5;  // ndims[0] + ndims[1]
   2173 
   2174   auto desc = init("list(shape)");
   2175   TF_SetAttrShapeList(desc, "v", list, ndims, list_size);
   2176   auto oper = TF_FinishOperation(desc, s_);
   2177   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2178 
   2179   EXPECT_TF_META("v", list_size, TF_ATTR_SHAPE, total_ndims);
   2180   int64_t* values[list_size];
   2181   int values_ndims[list_size];
   2182   int64_t storage[total_ndims];
   2183   TF_OperationGetAttrShapeList(oper, "v", values, values_ndims, list_size,
   2184                                storage, total_ndims, s_);
   2185   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2186   for (size_t i = 0; i < list_size; ++i) {
   2187     EXPECT_EQ(ndims[i], values_ndims[i]) << i;
   2188     for (int j = 0; j < values_ndims[i]; ++j) {
   2189       EXPECT_EQ(list[i][j], values[i][j]) << "(" << i << ", " << j << ")";
   2190     }
   2191   }
   2192 }
   2193 
   2194 TEST_F(CApiAttributesTest, TensorShapeProto) {
   2195   const tensorflow::int64 pts[] = {2, 4, -1, 8};
   2196   tensorflow::TensorShapeProto proto;
   2197   tensorflow::PartialTensorShape(pts).AsProto(&proto);
   2198   string bytes;
   2199   proto.SerializeToString(&bytes);
   2200 
   2201   auto desc = init("shape");
   2202   TF_SetAttrTensorShapeProto(desc, "v", bytes.data(), bytes.length(), s_);
   2203   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2204   auto oper = TF_FinishOperation(desc, s_);
   2205   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2206 
   2207   EXPECT_TF_META("v", -1, TF_ATTR_SHAPE, 4);
   2208   TF_Buffer* value = TF_NewBuffer();
   2209   TF_OperationGetAttrTensorShapeProto(oper, "v", value, s_);
   2210   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2211   EXPECT_EQ(bytes.length(), value->length);
   2212   EXPECT_EQ(0, memcmp(bytes.data(), value->data, value->length));
   2213   TF_DeleteBuffer(value);
   2214 }
   2215 
   2216 TEST_F(CApiAttributesTest, TensorShapeProtoList) {
   2217   string bytes1, bytes2;
   2218   tensorflow::TensorShapeProto proto;
   2219 
   2220   const tensorflow::int64 pts1[] = {2, 4, -1, 8};
   2221   tensorflow::PartialTensorShape(pts1).AsProto(&proto);
   2222   proto.SerializeToString(&bytes1);
   2223 
   2224   const tensorflow::int64 pts2[] = {1, 3, 5, 7};
   2225   tensorflow::PartialTensorShape(pts2).AsProto(&proto);
   2226   proto.SerializeToString(&bytes2);
   2227 
   2228   std::unique_ptr<const void*[]> list_ptrs;
   2229   std::unique_ptr<size_t[]> list_lens;
   2230   const std::vector<string> list = {bytes1, bytes2};
   2231   StringVectorToArrays(list, &list_ptrs, &list_lens);
   2232 
   2233   auto desc = init("list(shape)");
   2234   TF_SetAttrTensorShapeProtoList(desc, "v", list_ptrs.get(), list_lens.get(),
   2235                                  list.size(), s_);
   2236   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2237   auto oper = TF_FinishOperation(desc, s_);
   2238   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2239 
   2240   EXPECT_TF_META("v", 2, TF_ATTR_SHAPE, 8);
   2241   TF_Buffer* values[2];
   2242   TF_OperationGetAttrTensorShapeProtoList(oper, "v", values, 2, s_);
   2243   EXPECT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2244   for (int i = 0; i < 2; ++i) {
   2245     int le = list_lens[i];
   2246     int la = values[i]->length;
   2247     const void* e = list_ptrs[i];
   2248     const void* a = values[i]->data;
   2249     EXPECT_EQ(le, la) << i;
   2250     EXPECT_EQ(0, memcmp(e, a, std::min(le, la))) << i;
   2251     TF_DeleteBuffer(values[i]);
   2252   }
   2253 }
   2254 
   2255 TEST_F(CApiAttributesTest, Tensor) {
   2256   const char tensor[] = {5, 7};
   2257   const int64_t dims[] = {1, 2};
   2258   const size_t ndims = TF_ARRAYSIZE(dims);
   2259 
   2260   auto desc = init("tensor");
   2261   unique_tensor_ptr v(Int8Tensor(dims, ndims, tensor), TF_DeleteTensor);
   2262   TF_SetAttrTensor(desc, "v", v.get(), s_);
   2263   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2264 
   2265   auto oper = TF_FinishOperation(desc, s_);
   2266   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2267 
   2268   EXPECT_TF_META("v", -1, TF_ATTR_TENSOR, -1);
   2269   TF_Tensor* value;
   2270   TF_OperationGetAttrTensor(oper, "v", &value, s_);
   2271   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2272   ASSERT_NE(nullptr, value);
   2273   EXPECT_EQ(TF_INT8, TF_TensorType(value));
   2274   EXPECT_EQ(ndims, TF_NumDims(value));
   2275   for (int i = 0; i < TF_NumDims(value); ++i) {
   2276     EXPECT_EQ(dims[i], TF_Dim(value, i)) << i;
   2277   }
   2278   EXPECT_EQ(sizeof(char) * TF_ARRAYSIZE(tensor), TF_TensorByteSize(value));
   2279   EXPECT_EQ(0, memcmp(tensor, TF_TensorData(value), TF_TensorByteSize(value)));
   2280   TF_DeleteTensor(value);
   2281 }
   2282 
   2283 TEST_F(CApiAttributesTest, StringTensor) {
   2284   // Create the string-Tensor "attribute" value.
   2285   char encoded[] = {
   2286       0,   0, 0, 0, 0, 0, 0, 0,  // array[uint64] offsets
   2287       1,                         // varint encoded string length
   2288       'A',
   2289   };
   2290   auto deallocator = [](void* data, size_t len, void* arg) {};
   2291   unique_tensor_ptr t_in(TF_NewTensor(TF_STRING, nullptr, 0, &encoded[0],
   2292                                       sizeof(encoded), deallocator, nullptr),
   2293                          TF_DeleteTensor);
   2294 
   2295   // Create a TF_Operation with the attribute t_in
   2296   auto desc = init("tensor");
   2297   TF_SetAttrTensor(desc, "v", t_in.get(), s_);
   2298   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2299 
   2300   auto oper = TF_FinishOperation(desc, s_);
   2301   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2302 
   2303   // Fetch the attribute back.
   2304   EXPECT_TF_META("v", -1, TF_ATTR_TENSOR, -1);
   2305   TF_Tensor* t_out = nullptr;
   2306   TF_OperationGetAttrTensor(oper, "v", &t_out, s_);
   2307   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2308   EXPECT_EQ(TF_STRING, TF_TensorType(t_out));
   2309   EXPECT_EQ(0, TF_NumDims(t_out));
   2310   ASSERT_EQ(TF_TensorByteSize(t_in.get()), TF_TensorByteSize(t_out));
   2311   EXPECT_EQ(0, memcmp(TF_TensorData(t_in.get()), TF_TensorData(t_out),
   2312                       TF_TensorByteSize(t_out)));
   2313   TF_DeleteTensor(t_out);
   2314 }
   2315 
   2316 TEST_F(CApiAttributesTest, TensorList) {
   2317   const char tensor1[] = {5, 7};
   2318   const int64_t dims1[] = {1, 2};
   2319   const size_t ndims1 = TF_ARRAYSIZE(dims1);
   2320 
   2321   const char tensor2[] = {2, 4, 6, 8};
   2322   const int64_t dims2[] = {2, 2};
   2323   const size_t ndims2 = TF_ARRAYSIZE(dims2);
   2324 
   2325   auto desc = init("list(tensor)");
   2326   TF_Tensor* tmp[] = {
   2327       Int8Tensor(dims1, ndims1, tensor1),
   2328       Int8Tensor(dims2, ndims2, tensor2),
   2329   };
   2330   TF_SetAttrTensorList(desc, "v", tmp, TF_ARRAYSIZE(tmp), s_);
   2331   for (int i = 0; i < TF_ARRAYSIZE(tmp); ++i) {
   2332     TF_DeleteTensor(tmp[i]);
   2333   }
   2334   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2335   auto oper = TF_FinishOperation(desc, s_);
   2336   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2337 
   2338   EXPECT_TF_META("v", 2, TF_ATTR_TENSOR, -1);
   2339   TF_Tensor* values[2];
   2340   TF_OperationGetAttrTensorList(oper, "v", &values[0], TF_ARRAYSIZE(values),
   2341                                 s_);
   2342   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2343 
   2344   const char* tensor_data[] = {&tensor1[0], &tensor2[0]};
   2345   const size_t tensor_size[] = {TF_ARRAYSIZE(tensor1), TF_ARRAYSIZE(tensor2)};
   2346   const int64_t* tensor_dims[] = {&dims1[0], &dims2[0]};
   2347   const size_t tensor_ndims[] = {ndims1, ndims2};
   2348   for (int i = 0; i < 2; ++i) {
   2349     TF_Tensor* v = values[i];
   2350     ASSERT_NE(nullptr, v) << i;
   2351     EXPECT_EQ(TF_INT8, TF_TensorType(v)) << i;
   2352     EXPECT_EQ(tensor_ndims[i], TF_NumDims(v)) << i;
   2353     for (int j = 0; j < TF_NumDims(v); ++j) {
   2354       EXPECT_EQ(tensor_dims[i][j], TF_Dim(v, j))
   2355           << "Tensor #" << i << ", dimension #" << j;
   2356     }
   2357     EXPECT_EQ(sizeof(char) * tensor_size[i], TF_TensorByteSize(v)) << i;
   2358     EXPECT_EQ(0,
   2359               memcmp(tensor_data[i], TF_TensorData(v), TF_TensorByteSize(v)));
   2360     TF_DeleteTensor(v);
   2361   }
   2362 }
   2363 
   2364 TEST_F(CApiAttributesTest, EmptyList) {
   2365   auto desc = init("list(int)");
   2366   TF_SetAttrIntList(desc, "v", nullptr, 0);
   2367   auto oper = TF_FinishOperation(desc, s_);
   2368   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2369   EXPECT_TF_META("v", 0, TF_ATTR_INT, -1);
   2370 }
   2371 
   2372 TEST_F(CApiAttributesTest, Errors) {
   2373   auto desc = init("int");
   2374   TF_SetAttrInt(desc, "v", 3);
   2375   auto oper = TF_FinishOperation(desc, s_);
   2376   ASSERT_EQ(TF_OK, TF_GetCode(s_)) << TF_Message(s_);
   2377   TF_OperationGetAttrString(oper, "v", nullptr, 0, s_);
   2378   EXPECT_EQ(TF_INVALID_ARGUMENT, TF_GetCode(s_)) << TF_Message(s_);
   2379 }
   2380 
   2381 TEST(TestApiDef, TestCreateApiDef) {
   2382   // TODO(b/73318067): Fix linking for the GPU test generated by the
   2383   // tf_cuda_cc_test() bazel rule and remove the next line.
   2384   if (!GPUDeviceName().empty()) return;
   2385 
   2386   TF_Buffer* op_list_buf = TF_GetAllOpList();
   2387   TF_Status* status = TF_NewStatus();
   2388   auto* api_def_map = TF_NewApiDefMap(op_list_buf, status);
   2389   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2390   TF_DeleteStatus(status);
   2391 
   2392   string op_name = "TestCApi";
   2393   status = TF_NewStatus();
   2394   auto* api_def_buf =
   2395       TF_ApiDefMapGet(api_def_map, op_name.c_str(), op_name.size(), status);
   2396   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2397   TF_DeleteStatus(status);
   2398 
   2399   tensorflow::ApiDef api_def;
   2400   EXPECT_TRUE(api_def.ParseFromArray(api_def_buf->data, api_def_buf->length));
   2401   EXPECT_EQ(op_name, api_def.graph_op_name());
   2402   EXPECT_EQ(R"doc(Used to test C API)doc", api_def.summary());
   2403 
   2404   TF_DeleteBuffer(api_def_buf);
   2405   TF_DeleteApiDefMap(api_def_map);
   2406   TF_DeleteBuffer(op_list_buf);
   2407 }
   2408 
   2409 TEST(TestApiDef, TestCreateApiDefWithOverwrites) {
   2410   // TODO(b/73318067): Fix linking for the GPU test generated by the
   2411   // tf_cuda_cc_test() bazel rule and remove the next line.
   2412   if (!GPUDeviceName().empty()) return;
   2413 
   2414   TF_Buffer* op_list_buf = TF_GetAllOpList();
   2415   TF_Status* status = TF_NewStatus();
   2416   auto* api_def_map = TF_NewApiDefMap(op_list_buf, status);
   2417   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2418   TF_DeleteStatus(status);
   2419 
   2420   string api_def_overwrites = R"(op: <
   2421   graph_op_name: "TestCApi"
   2422   summary: "New summary"
   2423 >
   2424 )";
   2425   status = TF_NewStatus();
   2426   TF_ApiDefMapPut(api_def_map, api_def_overwrites.c_str(),
   2427                   api_def_overwrites.size(), status);
   2428   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2429   TF_DeleteStatus(status);
   2430 
   2431   string op_name = "TestCApi";
   2432   status = TF_NewStatus();
   2433   auto* api_def_buf =
   2434       TF_ApiDefMapGet(api_def_map, op_name.c_str(), op_name.size(), status);
   2435   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2436   TF_DeleteStatus(status);
   2437 
   2438   tensorflow::ApiDef api_def;
   2439   EXPECT_TRUE(api_def.ParseFromArray(api_def_buf->data, api_def_buf->length));
   2440   EXPECT_EQ(op_name, api_def.graph_op_name());
   2441   EXPECT_EQ("New summary", api_def.summary());
   2442 
   2443   TF_DeleteBuffer(api_def_buf);
   2444   TF_DeleteApiDefMap(api_def_map);
   2445   TF_DeleteBuffer(op_list_buf);
   2446 }
   2447 
   2448 class DummyKernel : public tensorflow::OpKernel {
   2449  public:
   2450   explicit DummyKernel(tensorflow::OpKernelConstruction* context)
   2451       : OpKernel(context) {}
   2452   void Compute(tensorflow::OpKernelContext* context) override {}
   2453 };
   2454 
   2455 // Test we can query kernels
   2456 REGISTER_OP("TestOpWithSingleKernel")
   2457     .Input("a: float")
   2458     .Input("b: float")
   2459     .Output("o: float");
   2460 REGISTER_KERNEL_BUILDER(
   2461     Name("TestOpWithSingleKernel").Device(tensorflow::DEVICE_CPU), DummyKernel);
   2462 
   2463 TEST(TestKernel, TestGetAllRegisteredKernels) {
   2464   TF_Status* status = TF_NewStatus();
   2465   TF_Buffer* kernel_list_buf = TF_GetAllRegisteredKernels(status);
   2466   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2467   KernelList kernel_list;
   2468   kernel_list.ParseFromArray(kernel_list_buf->data, kernel_list_buf->length);
   2469   ASSERT_GT(kernel_list.kernel_size(), 0);
   2470   TF_DeleteBuffer(kernel_list_buf);
   2471   TF_DeleteStatus(status);
   2472 }
   2473 
   2474 TEST(TestKernel, TestGetRegisteredKernelsForOp) {
   2475   TF_Status* status = TF_NewStatus();
   2476   TF_Buffer* kernel_list_buf =
   2477       TF_GetRegisteredKernelsForOp("TestOpWithSingleKernel", status);
   2478   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2479   KernelList kernel_list;
   2480   kernel_list.ParseFromArray(kernel_list_buf->data, kernel_list_buf->length);
   2481   ASSERT_EQ(kernel_list.kernel_size(), 1);
   2482   EXPECT_EQ(kernel_list.kernel(0).op(), "TestOpWithSingleKernel");
   2483   EXPECT_EQ(kernel_list.kernel(0).device_type(), "CPU");
   2484   TF_DeleteBuffer(kernel_list_buf);
   2485   TF_DeleteStatus(status);
   2486 }
   2487 
   2488 TEST(TestKernel, TestGetRegisteredKernelsForOpNoKernels) {
   2489   TF_Status* status = TF_NewStatus();
   2490   TF_Buffer* kernel_list_buf = TF_GetRegisteredKernelsForOp("Unknown", status);
   2491   EXPECT_EQ(TF_OK, TF_GetCode(status)) << TF_Message(status);
   2492   KernelList kernel_list;
   2493   kernel_list.ParseFromArray(kernel_list_buf->data, kernel_list_buf->length);
   2494   ASSERT_EQ(kernel_list.kernel_size(), 0);
   2495   TF_DeleteBuffer(kernel_list_buf);
   2496   TF_DeleteStatus(status);
   2497 }
   2498 
   2499 #undef EXPECT_TF_META
   2500 
   2501 }  // namespace
   2502 }  // namespace tensorflow
   2503 
   2504 // TODO(josh11b): Test:
   2505 // * TF_SetDevice(desc, "/job:worker");
   2506 // * control inputs / outputs
   2507 // * targets
   2508 // * TF_DeleteGraph() before TF_DeleteSession()
   2509