Add output formatters for more mojom types. Add output formatters for printing the contents of Mojo arrays, maps, and structs. These are very useful for writing debug logs. Review URL: https://codereview.chromium.org/1519673002 . R=viettrungluu@chromium.org, abarth, jamesr, viettrungluu BUG= Review URL: https://codereview.chromium.org/1532993002 .
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn index 01726c9..0b6464f 100644 --- a/mojo/public/cpp/bindings/BUILD.gn +++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -10,6 +10,7 @@ mojo_sdk_source_set("serialization") { sources = [ "array.h", + "formatting.h", "lib/array_internal.cc", "lib/array_internal.h", "lib/array_serialization.h",
diff --git a/mojo/public/cpp/bindings/formatting.h b/mojo/public/cpp/bindings/formatting.h new file mode 100644 index 0000000..af6377a --- /dev/null +++ b/mojo/public/cpp/bindings/formatting.h
@@ -0,0 +1,77 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_FORMATTING_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_FORMATTING_H_ + +#include <iosfwd> + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/map.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" + +namespace mojo { + +// Prints the contents of an array to an output stream in a human-readable +// format. +template <typename T> +std::ostream& operator<<(std::ostream& os, const mojo::Array<T>& array) { + if (array) { + os << "["; + bool first = true; + for (auto it = array.storage().cbegin(); it != array.storage().cend(); + ++it) { + if (first) + first = false; + else + os << ", "; + os << *it; + } + os << "]"; + } else { + os << "null"; + } + return os; +} + +// Prints the contents of a map to an output stream in a human-readable +// format. +template <typename Key, typename Value> +std::ostream& operator<<(std::ostream& os, const mojo::Map<Key, Value>& map) { + if (map) { + os << "{"; + bool first = true; + for (auto it = map.cbegin(); it != map.cend(); ++it) { + if (first) + first = false; + else + os << ", "; + os << it.GetKey() << ": " << it.GetValue(); + } + os << "}"; + } else { + os << "null"; + } + return os; +} + +// Prints the pointee of a Mojo structure pointer to an output stream +// assuming there exists an operator<< overload that accepts a const +// reference to the object. +template <typename T, typename = typename T::Data_> +auto operator<<(std::ostream& os, const T* value) -> decltype(os << *value) { + return value ? os << *value : os << "null"; +} +template <typename T> +std::ostream& operator<<(std::ostream& os, const StructPtr<T>& value) { + return os << value.get(); +} +template <typename T> +std::ostream& operator<<(std::ostream& os, const InlinedStructPtr<T>& value) { + return os << value.get(); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_FORMATTING_H_
diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h index 4cf8631..7f2ca31 100644 --- a/mojo/public/cpp/bindings/string.h +++ b/mojo/public/cpp/bindings/string.h
@@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ #define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ +#include <iosfwd> #include <string> #include "mojo/public/cpp/bindings/lib/array_internal.h" @@ -122,6 +123,8 @@ return !(a == b); } +// TODO(jeffbrown): Decide whether this should print a sentinel value +// such as "<null>" when formatting null strings. inline std::ostream& operator<<(std::ostream& out, const String& s) { return out << s.get(); }
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn index d082323..0ccb023 100644 --- a/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -19,6 +19,7 @@ "constant_unittest.cc", "container_test_util.cc", "equals_unittest.cc", + "formatting_unittest.cc", "handle_passing_unittest.cc", "interface_ptr_unittest.cc", "interface_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/formatting_unittest.cc b/mojo/public/cpp/bindings/tests/formatting_unittest.cc new file mode 100644 index 0000000..9082243 --- /dev/null +++ b/mojo/public/cpp/bindings/tests/formatting_unittest.cc
@@ -0,0 +1,104 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <sstream> + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/formatting.h" +#include "mojo/public/cpp/bindings/map.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace test { +namespace { +RectPtr MakeRect(int32_t factor = 1) { + RectPtr rect(Rect::New()); + rect->x = 1 * factor; + rect->y = 2 * factor; + rect->width = 10 * factor; + rect->height = 20 * factor; + return rect; +} +} // namespace + +std::ostream& operator<<(std::ostream& os, const Rect& value) { + return os << "{x=" << value.x << ", y=" << value.y + << ", width=" << value.width << ", height=" << value.height << "}"; +} + +std::ostream& operator<<(std::ostream& os, const RectPair& value) { + return os << "{first=" << value.first << ", second=" << value.second << "}"; +} + +TEST(FormattingTest, Arrays) { + Array<int32_t> null_array; + Array<int32_t> empty_array; + empty_array.resize(0); + Array<int32_t> one_element_array; + one_element_array.push_back(123); + Array<int32_t> three_element_array; + three_element_array.push_back(4); + three_element_array.push_back(5); + three_element_array.push_back(6); + + std::ostringstream so; + so << "null_array=" << null_array << ", empty_array=" << empty_array + << ", one_element_array=" << one_element_array + << ", three_element_array=" << three_element_array; + + EXPECT_EQ( + "null_array=null, " + "empty_array=[], " + "one_element_array=[123], " + "three_element_array=[4, 5, 6]", + so.str()); +} + +TEST(FormattingTest, Maps) { + Map<int32_t, std::string> null_map; + Map<int32_t, std::string> empty_map; + empty_map.mark_non_null(); + Map<int32_t, std::string> one_element_map; + one_element_map.insert(123, "abc"); + Map<int32_t, std::string> three_element_map; + three_element_map.insert(4, "d"); + three_element_map.insert(5, "e"); + three_element_map.insert(6, "f"); + + std::ostringstream so; + so << "null_map=" << null_map << ", empty_map=" << empty_map + << ", one_element_map=" << one_element_map + << ", three_element_map=" << three_element_map; + + EXPECT_EQ( + "null_map=null, " + "empty_map={}, " + "one_element_map={123: abc}, " + "three_element_map={4: d, 5: e, 6: f}", + so.str()); +} + +TEST(FormattingTest, Structs) { + InlinedStructPtr<Rect> inlined_struct_ptr = MakeRect(1); + InlinedStructPtr<Rect> null_inlined_struct_ptr; + StructPtr<RectPair> struct_ptr = RectPair::New(); + struct_ptr->first = MakeRect(2); + StructPtr<RectPair> null_struct_ptr; + + std::ostringstream so; + so << "inlined_struct_ptr=" << inlined_struct_ptr + << ", null_inlined_struct_ptr=" << null_inlined_struct_ptr + << ", struct_ptr=" << struct_ptr + << ", null_struct_ptr=" << null_struct_ptr; + EXPECT_EQ( + "inlined_struct_ptr={x=1, y=2, width=10, height=20}, " + "null_inlined_struct_ptr=null, " + "struct_ptr={first={x=2, y=4, width=20, height=40}, second=null}, " + "null_struct_ptr=null", + so.str()); +} + +} // namespace test +} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/string_unittest.cc b/mojo/public/cpp/bindings/tests/string_unittest.cc index 0769071..4089039 100644 --- a/mojo/public/cpp/bindings/tests/string_unittest.cc +++ b/mojo/public/cpp/bindings/tests/string_unittest.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <sstream> + #include "mojo/public/cpp/bindings/string.h" #include "testing/gtest/include/gtest/gtest.h" @@ -116,5 +118,14 @@ EXPECT_FALSE(real < null); } +TEST(StringTest, OutputFormatting) { + String s("abc"); + String null; + + std::ostringstream so; + so << "s=" << s << ", null=" << null; + EXPECT_EQ("s=abc, null=", so.str()); +} + } // namespace test } // namespace mojo