// Copyright 2013 The Flutter 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 "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h" #include "flutter/shell/platform/linux/fl_method_codec_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h" #include "gtest/gtest.h" G_DECLARE_FINAL_TYPE(FlTestMethodCodec, fl_test_method_codec, FL, TEST_METHOD_CODEC, FlMethodCodec) // Implement the FlMethodCodec API for the following tests to check it works as // expected. struct _FlTestMethodCodec { FlMethodCodec parent_instance; }; G_DEFINE_TYPE(FlTestMethodCodec, fl_test_method_codec, fl_method_codec_get_type()) // Helper function to convert binary data to text. static gchar* message_to_text(GBytes* message) { size_t data_length; const gchar* data = static_cast(g_bytes_get_data(message, &data_length)); return g_strndup(data, data_length); } // Helper function to convert text to binary data. static GBytes* text_to_message(const gchar* text) { return g_bytes_new(text, strlen(text)); } // Implements FlMethodCodec::encode_method_call. static GBytes* fl_test_codec_encode_method_call(FlMethodCodec* codec, const gchar* name, FlValue* args, GError** error) { EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec)); g_autofree gchar* text = nullptr; if (args == nullptr || fl_value_get_type(args) == FL_VALUE_TYPE_NULL) text = g_strdup_printf("%s()", name); else if (fl_value_get_type(args) == FL_VALUE_TYPE_INT) { text = g_strdup_printf("%s(%" G_GINT64_FORMAT ")", name, fl_value_get_int(args)); } else { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "ERROR"); return nullptr; } return text_to_message(text); } // Implements FlMethodCodec::decode_method_call. static gboolean fl_test_codec_decode_method_call(FlMethodCodec* codec, GBytes* message, gchar** name, FlValue** args, GError** error) { EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec)); g_autofree gchar* m = message_to_text(message); if (strcmp(m, "error") == 0) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "ERROR"); return FALSE; } else { *name = g_strdup(m); *args = fl_value_new_null(); return TRUE; } } // Implements FlMethodCodec::encode_success_envelope. static GBytes* fl_test_codec_encode_success_envelope(FlMethodCodec* codec, FlValue* result, GError** error) { EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec)); g_autofree gchar* text = nullptr; if (result == nullptr || fl_value_get_type(result) == FL_VALUE_TYPE_NULL) text = g_strdup("(null)"); else if (fl_value_get_type(result) == FL_VALUE_TYPE_INT) text = g_strdup_printf("%" G_GINT64_FORMAT, fl_value_get_int(result)); else { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "ERROR"); return nullptr; } return text_to_message(text); } // Implements FlMethodCodec::encode_error_envelope. static GBytes* fl_test_codec_encode_error_envelope(FlMethodCodec* codec, const gchar* code, const gchar* message, FlValue* details, GError** error) { EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec)); if (details != nullptr && fl_value_get_type(details) != FL_VALUE_TYPE_INT) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "ERROR"); return nullptr; } g_autofree gchar* text = nullptr; if (message == nullptr) { if (details == nullptr || fl_value_get_type(details) == FL_VALUE_TYPE_NULL) text = g_strdup_printf("Error_%s()", code); else { text = g_strdup_printf("Error_%s(%" G_GINT64_FORMAT ")", code, fl_value_get_int(details)); } } else { if (details == nullptr || fl_value_get_type(details) == FL_VALUE_TYPE_NULL) text = g_strdup_printf("Error_%s(%s)", code, message); else { text = g_strdup_printf("Error_%s(%s,%" G_GINT64_FORMAT ")", code, message, fl_value_get_int(details)); } } return text_to_message(text); } // Implements FlMethodCodec::decode_response. static FlMethodResponse* fl_test_codec_decode_response(FlMethodCodec* codec, GBytes* message, GError** error) { EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec)); g_autofree gchar* m = message_to_text(message); if (strcmp(m, "codec-error") == 0) { g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, "ERROR"); return nullptr; } else if (strcmp(m, "error") == 0) { g_autoptr(FlValue) details = fl_value_new_int(42); return FL_METHOD_RESPONSE( fl_method_error_response_new("code", "message", details)); } else { g_autoptr(FlValue) result = fl_value_new_string(m); return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); } } static void fl_test_method_codec_class_init(FlTestMethodCodecClass* klass) { FL_METHOD_CODEC_CLASS(klass)->encode_method_call = fl_test_codec_encode_method_call; FL_METHOD_CODEC_CLASS(klass)->decode_method_call = fl_test_codec_decode_method_call; FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope = fl_test_codec_encode_success_envelope; FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope = fl_test_codec_encode_error_envelope; FL_METHOD_CODEC_CLASS(klass)->decode_response = fl_test_codec_decode_response; } static void fl_test_method_codec_init(FlTestMethodCodec* self) {} static FlTestMethodCodec* fl_test_method_codec_new() { return FL_TEST_METHOD_CODEC( g_object_new(fl_test_method_codec_get_type(), nullptr)); } TEST(FlMethodCodecTest, EncodeMethodCall) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_method_call( FL_METHOD_CODEC(codec), "foo", nullptr, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "foo()"); } TEST(FlMethodCodecTest, EncodeMethodCallEmptyName) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_method_call( FL_METHOD_CODEC(codec), "", nullptr, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "()"); } TEST(FlMethodCodecTest, EncodeMethodCallArgs) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(FlValue) args = fl_value_new_int(42); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_method_call( FL_METHOD_CODEC(codec), "foo", args, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "foo(42)"); } TEST(FlMethodCodecTest, EncodeMethodCallError) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(FlValue) args = fl_value_new_bool(FALSE); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_method_call( FL_METHOD_CODEC(codec), "foo", args, &error); EXPECT_EQ(message, nullptr); EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED)); } TEST(FlMethodCodecTest, DecodeMethodCall) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GBytes) message = text_to_message("foo"); g_autofree gchar* name = nullptr; g_autoptr(FlValue) args = nullptr; g_autoptr(GError) error = nullptr; gboolean result = fl_method_codec_decode_method_call( FL_METHOD_CODEC(codec), message, &name, &args, &error); EXPECT_EQ(error, nullptr); EXPECT_TRUE(result); EXPECT_STREQ(name, "foo"); ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL); } TEST(FlMethodCodecTest, EncodeSuccessEnvelope) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(FlValue) result = fl_value_new_int(42); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope( FL_METHOD_CODEC(codec), result, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "42"); } TEST(FlMethodCodecTest, EncodeSuccessEnvelopeEmpty) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope( FL_METHOD_CODEC(codec), nullptr, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "(null)"); } TEST(FlMethodCodecTest, EncodeSuccessEnvelopeError) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(FlValue) result = fl_value_new_string("X"); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope( FL_METHOD_CODEC(codec), result, &error); EXPECT_EQ(message, nullptr); EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED)); } TEST(FlMethodCodecTest, EncodeErrorEnvelopeNoMessageOrDetails) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope( FL_METHOD_CODEC(codec), "code", nullptr, nullptr, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "Error_code()"); } TEST(FlMethodCodecTest, EncodeErrorEnvelopeMessage) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope( FL_METHOD_CODEC(codec), "code", "message", nullptr, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "Error_code(message)"); } TEST(FlMethodCodecTest, EncodeErrorEnvelopeDetails) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(FlValue) details = fl_value_new_int(42); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope( FL_METHOD_CODEC(codec), "code", nullptr, details, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "Error_code(42)"); } TEST(FlMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(FlValue) details = fl_value_new_int(42); g_autoptr(GError) error = nullptr; g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope( FL_METHOD_CODEC(codec), "code", "message", details, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(message, nullptr); g_autofree gchar* message_text = message_to_text(message); EXPECT_STREQ(message_text, "Error_code(message,42)"); } TEST(FlMethodCodecTest, DecodeResponseSuccess) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GBytes) message = text_to_message("echo"); g_autoptr(GError) error = nullptr; g_autoptr(FlMethodResponse) response = fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(response, nullptr); ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); FlValue* result = fl_method_success_response_get_result( FL_METHOD_SUCCESS_RESPONSE(response)); ASSERT_NE(result, nullptr); ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING); EXPECT_STREQ(fl_value_get_string(result), "echo"); } TEST(FlMethodCodecTest, DecodeResponseNotImplemented) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GBytes) message = g_bytes_new(nullptr, 0); g_autoptr(GError) error = nullptr; g_autoptr(FlMethodResponse) response = fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(response, nullptr); ASSERT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response)); } TEST(FlMethodCodecTest, DecodeResponseCodecError) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GBytes) message = text_to_message("codec-error"); g_autoptr(GError) error = nullptr; g_autoptr(FlMethodResponse) response = fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED)); EXPECT_EQ(response, nullptr); } TEST(FlMethodCodecTest, DecodeResponseError) { g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new(); g_autoptr(GBytes) message = text_to_message("error"); g_autoptr(GError) error = nullptr; g_autoptr(FlMethodResponse) response = fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); EXPECT_EQ(error, nullptr); EXPECT_NE(response, nullptr); ASSERT_TRUE(FL_METHOD_ERROR_RESPONSE(response)); EXPECT_STREQ( fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)), "code"); EXPECT_STREQ( fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)), "message"); FlValue* details = fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response)); ASSERT_NE(details, nullptr); ASSERT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_INT); EXPECT_EQ(fl_value_get_int(details), 42); }