Google celebrates pride. See how.

LLCPP FIDL library

Prerequisites

This tutorial builds on the Compiling FIDL tutorial. For more information on other FIDL tutorials, see the Overview.

Overview

This tutorial details how to use the LLCPP FIDL bindings by creating a unit test that you can use as a "playground" for exploring the LLCPP bindings.

This document covers how to complete the following tasks:

The example code is located in your Fuchsia checkout in //examples/fidl/llcpp/unittests/. If you want to write all the code as you follow this tutorial, you can remove the example code:

rm -r examples/fidl/llcpp/unittests/*

Write a C++ FIDL LLCPP test

  1. Add a gtest stub to examples/fidl/llcpp/unittests/main.cc:

    #include <gtest/gtest.h>
    
    namespace {
    
    } // namespace
    
  2. Define a test target, and a package containing the test in examples/fidl/llcpp/unittests/BUILD.gn:

    import("//build/components.gni")
    import("//build/test.gni")
    
    
    test("test") {
      testonly = true
      output_name = "fidl_example_llcpp_test"
      sources = [ "main.cc" ]
      deps = [ "//src/lib/fxl/test:gtest_main" ]
    }
    
    fuchsia_unittest_package("fidl-example-llcpp-test") {
      deps = [ ":test" ]
    }
    
    group("unittests") {
      testonly = true
      deps = [ ":fidl-example-llcpp-test" ]
    }
    
  3. Build and run the empty test suite on a running instance of Fuchsia:

    fx set core.x64 --with //examples/fidl/llcpp/unittests:fidl-example-llcpp-test
    fx build
    

    In a separate terminal, run

    fx qemu -kN
    

    In a separate terminal, run

    fx serve
    

    Back in the original terminal (with fx build), run

    fx test -vo fidl-example-llcpp-test
    

    You should see test output indicating that zero tests have run, since no tests have been added yet.

Add the LLCPP FIDL bindings as a dependency

For each FIDL library declaration, including the one in Compiling FIDL, C++ library containing LLCPP bindings code for that library is generated under the original target name appended with _llcpp.

Add a dependency on the LLCPP bindings by referencing this target. The new test target should look like:

test("test") {
  testonly = true
  output_name = "fidl_example_llcpp_test"
  sources = [ "main.cc" ]
  deps = [
    # For a FIDL library named "fuchsia.examples", the corresponding
    # GN target for the LLCPP generated bindings is suffixed with "_llcpp",
    # i.e. "fuchsia.examples_llcpp".
    "//examples/fidl/fuchsia.examples:fuchsia.examples_llcpp",
    "//src/lib/fxl/test:gtest_main",
  ]
}

(Optional) To view the newly generated bindings:

  1. Rebuild using fx build.
  2. Change to the generated files directory: out/default/fidling/gen/examples/fidl/fuchsia.examples/fuchsia/examples, where the generated files are located. You may need to change out/default if you have set a different build output directory. You can check your build output directory with cat .fx-build-dir.

For more information on how to find generated bindings code, see Viewing generated bindings code.

Include the LLCPP bindings in your C++ code

To include the bindings, add the following include statement to the top of examples/fidl/llcpp/unittests/main.cc

#include <fidl/fuchsia.examples/cpp/wire.h>

Inspect and use the generated bindings code

You can now write some tests by referring to the generated code. For more information on the bindings, see LLCPP Bindings Reference.

To get started, you can also use some example code. You can add this inside the anonymous namespace in main.cc:

TEST(FidlExamples, Bits) {
  auto flags = FileMode::kRead | FileMode::kWrite | FileMode::kExecute;
  ASSERT_EQ(flags, FileMode::kMask);
}

TEST(FidlExamples, Enums) {
  ASSERT_EQ(static_cast<uint32_t>(fuchsia_examples::wire::LocationType::kMuseum), 1u);
}

TEST(FidlExamples, Structs) {
  fuchsia_examples::wire::Color default_color;
  ASSERT_EQ(default_color.id, 0u);
  // Default values are currently not supported.
  ASSERT_TRUE(default_color.name.is_null());
  ASSERT_TRUE(default_color.name.empty());

  fuchsia_examples::wire::Color blue = {1, "blue"};
  ASSERT_EQ(blue.id, 1u);
}

TEST(FidlExamples, Unions) {
  fidl::Arena allocator;
  auto int_val = fuchsia_examples::wire::JsonValue::WithIntValue(1);
  ASSERT_TRUE(int_val.is_int_value());
  ASSERT_EQ(1, int_val.int_value());

  auto str_val = fuchsia_examples::wire::JsonValue::WithStringValue(allocator, "1");
  ASSERT_TRUE(str_val.is_string_value());
  ASSERT_EQ("1", str_val.string_value().get());
}

TEST(FidlExamples, Tables) {
  fidl::Arena arena;
  // Construct a table creating a builder with an arena.
  auto builder = fuchsia_examples::wire::User::Builder(arena);
  // The |arena| passed to the builder will be used to allocate the table frame,
  // the inline portions of any fields and passed to the constructor of field
  // types.
  builder.name("jdoe");
  // The builder is turned into an actual instance by calling |Build()|.
  auto user = builder.Build();
  ASSERT_FALSE(user.IsEmpty());
  ASSERT_EQ(user.name().get(), "jdoe");
}

TEST(FidlExamples, TablesInlineSetter) {
  fidl::Arena arena;
  // Construct a table creating a builder with an arena.
  auto builder = fuchsia_examples::wire::User::Builder(arena);
  // Small values <= 4 bytes are inlined inside the frame of the table.
  builder.age(30);
  // The builder is turned into an actual instance by calling |Build()|.
  auto user = builder.Build();
  ASSERT_FALSE(user.IsEmpty());
  ASSERT_EQ(user.age(), 30);
}

TEST(FidlExamples, TablesDefaultConstructor) {
  fidl::Arena allocator;
  // In some situations it could be difficult to provide an arena when
  // constructing tables. For example, here it is hard to provide constructor
  // arguments to 10 tables at once. When a table is default constructed, it
  // does not have an associated |fidl::WireTableFrame<T>|. A new table
  // instance should be built and assigned to the default constructed table.
  fidl::Array<fuchsia_examples::wire::User, 10> users;
  for (auto& user : users) {
    ASSERT_TRUE(user.IsEmpty());
    user = fuchsia_examples::wire::User::Builder(allocator).age(30).name("jdoe").Build();
    ASSERT_FALSE(user.IsEmpty());
    ASSERT_EQ(user.age(), 30);
  }
  ASSERT_EQ(users[0].age(), 30);
}

To rebuild and rerun the tests, run:

fx test -vo fidl-example-llcpp-test