diff --git a/shell/platform/linux/fl_value.cc b/shell/platform/linux/fl_value.cc index 2468be81b30254e83b8026287c7b2c1c44efcc0c..ad24e600c216d3e17770096d91e5cb0d4f420085 100644 --- a/shell/platform/linux/fl_value.cc +++ b/shell/platform/linux/fl_value.cc @@ -92,6 +92,122 @@ static ssize_t fl_value_lookup_index(FlValue* self, FlValue* key) { return -1; } +// Converts an integer to a string and adds it to the buffer +static void int_to_string(int64_t value, GString* buffer) { + g_string_append_printf(buffer, "%" G_GINT64_FORMAT, value); +} + +// Converts a floating point number to a string and adds it to the buffer +static void float_to_string(double value, GString* buffer) { + g_string_append_printf(buffer, "%.16f", value); + + // Strip trailing zeros + int zero_count = 0; + for (int i = buffer->len - 1; i >= 0; i--) { + // Leave one zero after a decimal point + if (buffer->str[i] == '.') { + zero_count = zero_count == 0 ? 0 : zero_count - 1; + break; + } + if (buffer->str[i] != '0') + break; + zero_count++; + } + g_string_truncate(buffer, buffer->len - zero_count); +} + +static void value_to_string(FlValue* value, GString* buffer) { + switch (value->type) { + case FL_VALUE_TYPE_NULL: + g_string_append(buffer, "null"); + return; + case FL_VALUE_TYPE_BOOL: + if (fl_value_get_bool(value)) + g_string_append(buffer, "true"); + else + g_string_append(buffer, "false"); + return; + case FL_VALUE_TYPE_INT: + int_to_string(fl_value_get_int(value), buffer); + return; + case FL_VALUE_TYPE_FLOAT: + float_to_string(fl_value_get_float(value), buffer); + return; + case FL_VALUE_TYPE_STRING: { + g_string_append(buffer, fl_value_get_string(value)); + return; + } + case FL_VALUE_TYPE_UINT8_LIST: { + g_string_append(buffer, "["); + const uint8_t* values = fl_value_get_uint8_list(value); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (i != 0) + g_string_append(buffer, ", "); + int_to_string(values[i], buffer); + } + g_string_append(buffer, "]"); + return; + } + case FL_VALUE_TYPE_INT32_LIST: { + g_string_append(buffer, "["); + const int32_t* values = fl_value_get_int32_list(value); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (i != 0) + g_string_append(buffer, ", "); + int_to_string(values[i], buffer); + } + g_string_append(buffer, "]"); + return; + } + case FL_VALUE_TYPE_INT64_LIST: { + g_string_append(buffer, "["); + const int64_t* values = fl_value_get_int64_list(value); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (i != 0) + g_string_append(buffer, ", "); + int_to_string(values[i], buffer); + } + g_string_append(buffer, "]"); + return; + } + case FL_VALUE_TYPE_FLOAT_LIST: { + g_string_append(buffer, "["); + const double* values = fl_value_get_float_list(value); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (i != 0) + g_string_append(buffer, ", "); + float_to_string(values[i], buffer); + } + g_string_append(buffer, "]"); + return; + } + case FL_VALUE_TYPE_LIST: { + g_string_append(buffer, "["); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (i != 0) + g_string_append(buffer, ", "); + value_to_string(fl_value_get_list_value(value, i), buffer); + } + g_string_append(buffer, "]"); + return; + } + case FL_VALUE_TYPE_MAP: { + g_string_append(buffer, "{"); + for (size_t i = 0; i < fl_value_get_length(value); i++) { + if (i != 0) + g_string_append(buffer, ", "); + value_to_string(fl_value_get_map_key(value, i), buffer); + g_string_append(buffer, ": "); + value_to_string(fl_value_get_map_value(value, i), buffer); + } + g_string_append(buffer, "}"); + return; + } + default: + g_string_append_printf(buffer, "", value->type); + } +} + G_MODULE_EXPORT FlValue* fl_value_new_null() { return fl_value_new(FL_VALUE_TYPE_NULL, sizeof(FlValue)); } @@ -570,3 +686,9 @@ FlValue* fl_value_lookup_string(FlValue* self, const gchar* key) { g_autoptr(FlValue) string_key = fl_value_new_string(key); return fl_value_lookup(self, string_key); } + +gchar* fl_value_to_string(FlValue* value) { + GString* buffer = g_string_new(""); + value_to_string(value, buffer); + return g_string_free(buffer, FALSE); +} diff --git a/shell/platform/linux/fl_value_test.cc b/shell/platform/linux/fl_value_test.cc index c5f2a790cb97bc7153fcd29932ae5d0710ea9c16..da4062c2c0aabb19e2a7f6c600f0563aeb3a7789 100644 --- a/shell/platform/linux/fl_value_test.cc +++ b/shell/platform/linux/fl_value_test.cc @@ -18,6 +18,12 @@ TEST(FlValueTest, NullEqual) { EXPECT_TRUE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, NullToString) { + g_autoptr(FlValue) value = fl_value_new_null(); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "null"); +} + TEST(FlValueTest, BoolTrue) { g_autoptr(FlValue) value = fl_value_new_bool(TRUE); ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_BOOL); @@ -42,6 +48,18 @@ TEST(FlValueTest, BoolNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, BoolTrueToString) { + g_autoptr(FlValue) value = fl_value_new_bool(TRUE); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "true"); +} + +TEST(FlValueTest, BoolFalseToString) { + g_autoptr(FlValue) value = fl_value_new_bool(FALSE); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "false"); +} + TEST(FlValueTest, IntZero) { g_autoptr(FlValue) value = fl_value_new_int(0); ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_INT); @@ -84,6 +102,12 @@ TEST(FlValueTest, IntNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, IntToString) { + g_autoptr(FlValue) value = fl_value_new_int(42); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "42"); +} + TEST(FlValueTest, FloatZero) { g_autoptr(FlValue) value = fl_value_new_float(0.0); ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_FLOAT); @@ -120,6 +144,12 @@ TEST(FlValueTest, FloatNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, FloatToString) { + g_autoptr(FlValue) value = fl_value_new_float(M_PI); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "3.1415926535897931"); +} + TEST(FlValueTest, String) { g_autoptr(FlValue) value = fl_value_new_string("hello"); ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); @@ -162,6 +192,12 @@ TEST(FlValueTest, StringNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, StringToString) { + g_autoptr(FlValue) value = fl_value_new_string("hello"); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "hello"); +} + TEST(FlValueTest, Uint8List) { uint8_t data[] = {0x00, 0x01, 0xFE, 0xFF}; g_autoptr(FlValue) value = fl_value_new_uint8_list(data, 4); @@ -216,6 +252,13 @@ TEST(FlValueTest, Uint8ListEmptyNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, Uint8ListToString) { + uint8_t data[] = {0x00, 0x01, 0xFE, 0xFF}; + g_autoptr(FlValue) value = fl_value_new_uint8_list(data, 4); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "[0, 1, 254, 255]"); +} + TEST(FlValueTest, Int32List) { int32_t data[] = {0, -1, G_MAXINT32, G_MININT32}; g_autoptr(FlValue) value = fl_value_new_int32_list(data, 4); @@ -270,6 +313,13 @@ TEST(FlValueTest, Int32ListEmptyNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, Int32ListToString) { + int32_t data[] = {0, G_MAXINT32, G_MININT32}; + g_autoptr(FlValue) value = fl_value_new_int32_list(data, 3); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "[0, 2147483647, -2147483648]"); +} + TEST(FlValueTest, Int64List) { int64_t data[] = {0, -1, G_MAXINT64, G_MININT64}; g_autoptr(FlValue) value = fl_value_new_int64_list(data, 4); @@ -324,6 +374,13 @@ TEST(FlValueTest, Int64ListEmptyNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, Int64ListToString) { + int64_t data[] = {0, G_MAXINT64, G_MININT64}; + g_autoptr(FlValue) value = fl_value_new_int64_list(data, 3); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "[0, 9223372036854775807, -9223372036854775808]"); +} + TEST(FlValueTest, FloatList) { double data[] = {0.0, -1.0, M_PI}; g_autoptr(FlValue) value = fl_value_new_float_list(data, 4); @@ -377,6 +434,13 @@ TEST(FlValueTest, FloatListEmptyNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, FloatListToString) { + double data[] = {0, -0.5, M_PI}; + g_autoptr(FlValue) value = fl_value_new_float_list(data, 3); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, "[0.0, -0.5, 3.1415926535897931]"); +} + TEST(FlValueTest, ListEmpty) { g_autoptr(FlValue) value = fl_value_new_list(); ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_LIST); @@ -510,6 +574,23 @@ TEST(FlValueTest, ListEmptyNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, ListToString) { + g_autoptr(FlValue) value = fl_value_new_list(); + fl_value_append_take(value, fl_value_new_null()); + fl_value_append_take(value, fl_value_new_bool(TRUE)); + fl_value_append_take(value, fl_value_new_int(42)); + fl_value_append_take(value, fl_value_new_float(M_PI)); + fl_value_append_take(value, fl_value_new_uint8_list(nullptr, 0)); + fl_value_append_take(value, fl_value_new_int32_list(nullptr, 0)); + fl_value_append_take(value, fl_value_new_int64_list(nullptr, 0)); + fl_value_append_take(value, fl_value_new_float_list(nullptr, 0)); + fl_value_append_take(value, fl_value_new_list()); + fl_value_append_take(value, fl_value_new_map()); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, + "[null, true, 42, 3.1415926535897931, [], [], [], [], [], {}]"); +} + TEST(FlValueTest, MapEmpty) { g_autoptr(FlValue) value = fl_value_new_map(); ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); @@ -600,6 +681,90 @@ TEST(FlValueTest, MapLookupString) { ASSERT_EQ(v, nullptr); } +TEST(FlValueTest, MapValueypes) { + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set_take(value, fl_value_new_string("null"), fl_value_new_null()); + fl_value_set_take(value, fl_value_new_string("bool"), + fl_value_new_bool(TRUE)); + fl_value_set_take(value, fl_value_new_string("int"), fl_value_new_int(42)); + fl_value_set_take(value, fl_value_new_string("float"), + fl_value_new_float(M_PI)); + fl_value_set_take(value, fl_value_new_string("uint8_list"), + fl_value_new_uint8_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("int32_list"), + fl_value_new_int32_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("int64_list"), + fl_value_new_int64_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("float_list"), + fl_value_new_float_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("list"), fl_value_new_list()); + fl_value_set_take(value, fl_value_new_string("map"), fl_value_new_map()); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(value), static_cast(10)); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 0)), + FL_VALUE_TYPE_NULL); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 1)), + FL_VALUE_TYPE_BOOL); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 2)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 3)), + FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 4)), + FL_VALUE_TYPE_UINT8_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 5)), + FL_VALUE_TYPE_INT32_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 6)), + FL_VALUE_TYPE_INT64_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 7)), + FL_VALUE_TYPE_FLOAT_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 8)), + FL_VALUE_TYPE_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_value(value, 9)), + FL_VALUE_TYPE_MAP); +} + +TEST(FlValueTest, MapKeyTypes) { + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set_take(value, fl_value_new_null(), fl_value_new_string("null")); + fl_value_set_take(value, fl_value_new_bool(TRUE), + fl_value_new_string("bool")); + fl_value_set_take(value, fl_value_new_int(42), fl_value_new_string("int")); + fl_value_set_take(value, fl_value_new_float(M_PI), + fl_value_new_string("float")); + fl_value_set_take(value, fl_value_new_uint8_list(nullptr, 0), + fl_value_new_string("uint8_list")); + fl_value_set_take(value, fl_value_new_int32_list(nullptr, 0), + fl_value_new_string("int32_list")); + fl_value_set_take(value, fl_value_new_int64_list(nullptr, 0), + fl_value_new_string("int64_list")); + fl_value_set_take(value, fl_value_new_float_list(nullptr, 0), + fl_value_new_string("float_list")); + fl_value_set_take(value, fl_value_new_list(), fl_value_new_string("list")); + fl_value_set_take(value, fl_value_new_map(), fl_value_new_string("map")); + ASSERT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); + ASSERT_EQ(fl_value_get_length(value), static_cast(10)); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 0)), + FL_VALUE_TYPE_NULL); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 1)), + FL_VALUE_TYPE_BOOL); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 2)), + FL_VALUE_TYPE_INT); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 3)), + FL_VALUE_TYPE_FLOAT); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 4)), + FL_VALUE_TYPE_UINT8_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 5)), + FL_VALUE_TYPE_INT32_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 6)), + FL_VALUE_TYPE_INT64_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 7)), + FL_VALUE_TYPE_FLOAT_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 8)), + FL_VALUE_TYPE_LIST); + EXPECT_EQ(fl_value_get_type(fl_value_get_map_key(value, 9)), + FL_VALUE_TYPE_MAP); +} + TEST(FlValueTest, MapEqual) { g_autoptr(FlValue) value1 = fl_value_new_map(); fl_value_set_string_take(value1, "one", fl_value_new_int(1)); @@ -676,6 +841,49 @@ TEST(FlValueTest, MapEmptyNotEqual) { EXPECT_FALSE(fl_value_equal(value1, value2)); } +TEST(FlValueTest, MapToString) { + g_autoptr(FlValue) value = fl_value_new_map(); + fl_value_set_take(value, fl_value_new_string("null"), fl_value_new_null()); + fl_value_set_take(value, fl_value_new_string("bool"), + fl_value_new_bool(TRUE)); + fl_value_set_take(value, fl_value_new_string("int"), fl_value_new_int(42)); + fl_value_set_take(value, fl_value_new_string("float"), + fl_value_new_float(M_PI)); + fl_value_set_take(value, fl_value_new_string("uint8_list"), + fl_value_new_uint8_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("int32_list"), + fl_value_new_int32_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("int64_list"), + fl_value_new_int64_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("float_list"), + fl_value_new_float_list(nullptr, 0)); + fl_value_set_take(value, fl_value_new_string("list"), fl_value_new_list()); + fl_value_set_take(value, fl_value_new_string("map"), fl_value_new_map()); + fl_value_set_take(value, fl_value_new_null(), fl_value_new_string("null")); + fl_value_set_take(value, fl_value_new_bool(TRUE), + fl_value_new_string("bool")); + fl_value_set_take(value, fl_value_new_int(42), fl_value_new_string("int")); + fl_value_set_take(value, fl_value_new_float(M_PI), + fl_value_new_string("float")); + fl_value_set_take(value, fl_value_new_uint8_list(nullptr, 0), + fl_value_new_string("uint8_list")); + fl_value_set_take(value, fl_value_new_int32_list(nullptr, 0), + fl_value_new_string("int32_list")); + fl_value_set_take(value, fl_value_new_int64_list(nullptr, 0), + fl_value_new_string("int64_list")); + fl_value_set_take(value, fl_value_new_float_list(nullptr, 0), + fl_value_new_string("float_list")); + fl_value_set_take(value, fl_value_new_list(), fl_value_new_string("list")); + fl_value_set_take(value, fl_value_new_map(), fl_value_new_string("map")); + g_autofree gchar* text = fl_value_to_string(value); + EXPECT_STREQ(text, + "{null: null, bool: true, int: 42, float: 3.1415926535897931, " + "uint8_list: [], int32_list: [], int64_list: [], float_list: " + "[], list: [], map: {}, null: null, true: bool, 42: int, " + "3.1415926535897931: float, []: uint8_list, []: int32_list, []: " + "int64_list, []: float_list, []: list, {}: map}"); +} + TEST(FlValueTest, EqualSameObject) { g_autoptr(FlValue) value = fl_value_new_null(); EXPECT_TRUE(fl_value_equal(value, value)); diff --git a/shell/platform/linux/public/flutter_linux/fl_value.h b/shell/platform/linux/public/flutter_linux/fl_value.h index 4f0ffd3c975d0ccc187109f9083c5582e5444033..f5632642e61c16420c0ee22e6743c7a0a3cdcd5f 100644 --- a/shell/platform/linux/public/flutter_linux/fl_value.h +++ b/shell/platform/linux/public/flutter_linux/fl_value.h @@ -22,7 +22,7 @@ G_BEGIN_DECLS * channel used by Flutter. * * In Dart the values are represented as follows: - * - #FL_VALUE_TYPE_NULL: null + * - #FL_VALUE_TYPE_NULL: Null * - #FL_VALUE_TYPE_BOOL: bool * - #FL_VALUE_TYPE_INT: num * - #FL_VALUE_TYPE_FLOAT: num @@ -31,8 +31,8 @@ G_BEGIN_DECLS * - #FL_VALUE_TYPE_INT32_LIST: Int32List * - #FL_VALUE_TYPE_INT64_LIST: Int64List * - #FL_VALUE_TYPE_FLOAT_LIST: Float64List - * - #FL_VALUE_TYPE_LIST: List - * - #FL_VALUE_TYPE_MAP: Map + * - #FL_VALUE_TYPE_LIST: List + * - #FL_VALUE_TYPE_MAP: Map * * See #FlMessageCodec to encode and decode these values. */ @@ -198,7 +198,7 @@ FlValue* fl_value_new_float_list(const double* value, size_t value_length); * * Creates an ordered list. Children can be added to the list using * fl_value_append(). The children are accessed using fl_value_get_length() - * and fl_value_get_list_value(). The equivalent Dart type is a List. + * and fl_value_get_list_value(). The equivalent Dart type is a List. * * The following example shows a simple list of values: * @@ -241,7 +241,7 @@ FlValue* fl_value_new_list_from_strv(const gchar* const* value); * fl_value_set_string_take(). The children are accessed using * fl_value_get_length(), fl_value_get_map_key(), fl_value_get_map_value(), * fl_value_lookup() and fl_value_lookup_string(). The equivalent Dart type is a - * Map. + * Map. * * The following example shows how to create a map of values keyed by strings: * @@ -568,6 +568,17 @@ FlValue* fl_value_lookup(FlValue* value, FlValue* key); */ FlValue* fl_value_lookup_string(FlValue* value, const gchar* key); +/** + * fl_value_to_string: + * @value: an #FlValue. + * + * Converts an #FlValue to a text representation, suitable for logging purposes. + * The text is formatted to be match the equivalent Dart toString() methods. + * + * Returns: UTF-8 text. + */ +gchar* fl_value_to_string(FlValue* value); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlValue, fl_value_unref) G_END_DECLS