Commit 03358347 authored by Rouslan Solomakhin's avatar Rouslan Solomakhin Committed by Commit Bot

[Web Payment][Chrome OS] Android app communication test support.

This patch adds a helper for setting up the environment necessary to
unit test communication with Android apps. At this time, only the
Android subsystem on Chrome OS has a concrete implementation.

Bug: 1061503
Change-Id: I6900aa13cd111d14adeaab6a0116a623c4ace817
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2250351
Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Reviewed-by: default avatarDanyao Wang <danyao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797802}
parent 9e4a5332
......@@ -136,6 +136,8 @@ static_library("utils") {
source_set("unit_tests") {
testonly = true
sources = [
"android_app_communication_test_support.h",
"android_app_communication_unittest.cc",
"payment_method_manifest_table_unittest.cc",
"service_worker_payment_app_finder_unittest.cc",
"test_content_payment_request_delegate.cc",
......@@ -164,6 +166,7 @@ source_set("unit_tests") {
"//components/autofill/core/browser:test_support",
"//components/payments/core",
"//components/payments/core:error_strings",
"//components/payments/core:method_strings",
"//components/payments/core:test_support",
"//components/strings:components_strings_grit",
"//components/webdata/common",
......@@ -177,4 +180,18 @@ source_set("unit_tests") {
"//third_party/icu",
"//third_party/libaddressinput:test_support",
]
if (is_chromeos) {
sources += [ "android_app_communication_test_support_chrome_os.cc" ]
deps += [
"//components/arc",
"//components/arc:arc_test_support",
"//components/arc/mojom",
]
}
if (!is_chromeos) {
sources += [ "android_app_communication_test_support_stub.cc" ]
}
}
include_rules = [
"-components/payments/content/android",
"+components/arc",
"+components/autofill",
"+components/keyed_service/content",
"+components/keyed_service/core",
......
// Copyright 2020 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 COMPONENTS_PAYMENTS_CONTENT_ANDROID_APP_COMMUNICATION_TEST_SUPPORT_H_
#define COMPONENTS_PAYMENTS_CONTENT_ANDROID_APP_COMMUNICATION_TEST_SUPPORT_H_
#include <memory>
#include <string>
#include <vector>
#include "components/payments/core/android_app_description.h"
namespace content {
class BrowserContext;
} // namespace content
namespace payments {
// Cross-platform test support for Android payment app communication. On Chrome
// OS, this connects to the Android subsystem.
//
// The test expectations are platform-specific. For example, on Chrome OS,
// expectations are setup in the mock Mojo IPC service.
class AndroidAppCommunicationTestSupport {
public:
// The object that initializes the ability to invoke Android apps in tests.
// For example, on Chrome OS, this object creates a mock Mojo IPC connection
// for the Android subsystem. This is meant to be placed on the stack, so it
// can perform clean up in its destructor.
class ScopedInitialization {
public:
ScopedInitialization() = default;
virtual ~ScopedInitialization() = default;
ScopedInitialization(const ScopedInitialization& other) = delete;
ScopedInitialization& operator=(const ScopedInitialization& other) = delete;
};
// Defined in platform-specific files.
static std::unique_ptr<AndroidAppCommunicationTestSupport> Create();
virtual ~AndroidAppCommunicationTestSupport() = default;
// Disallow copy and assign.
AndroidAppCommunicationTestSupport(
const AndroidAppCommunicationTestSupport& other) = delete;
AndroidAppCommunicationTestSupport& operator=(
const AndroidAppCommunicationTestSupport& other) = delete;
// Whether this platform supports Android apps. Used in tests to determine the
// expected outcome when attempting to query and invoke Android payment apps.
virtual bool AreAndroidAppsSupportedOnThisPlatform() const = 0;
// Creates the object that initializes the ability to invoke Android apps
// in tests. Places this on the stack, so it can perform clean up in its
// destructor. It's also useful to have a test case that does not invoke this
// method, so the code path that handles inability to invoke Android apps is
// tested.
virtual std::unique_ptr<ScopedInitialization>
CreateScopedInitialization() = 0;
// Sets up the expectation that the test case will not query the list of
// Android payment apps. This can happen, for example, when there is no
// ScopedInitialization object in scope.
virtual void ExpectNoListOfPaymentAppsQuery() = 0;
// Sets up the expectation that the test case will not query an
// IS_READY_TO_PAY service.
virtual void ExpectNoIsReadyToPayQuery() = 0;
// Sets up the expectation that the test case will not invoke a PAY activity.
virtual void ExpectNoPaymentAppInvoke() = 0;
// Sets up the expectation that the test case will query the list of Android
// payment apps. When that happens, the given list of |apps| will be used for
// the response.
virtual void ExpectQueryListOfPaymentAppsAndRespond(
std::vector<std::unique_ptr<AndroidAppDescription>> apps) = 0;
// Sets up the expectation that the test case will query an IS_READY_TO_PAY
// service. When that happens, the service will reply with the given
// |is_ready_to_pay| answer.
virtual void ExpectQueryIsReadyToPayAndRespond(bool is_ready_to_pay) = 0;
// Sets up the expectation that the test case will invoke a PAY activity. When
// that happens, the activity will reply with the given parameters.
virtual void ExpectInvokePaymentAppAndRespond(
bool is_activity_result_ok,
const std::string& payment_method_identifier,
const std::string& stringified_details) = 0;
// Returns the browser context to use.
virtual content::BrowserContext* context() = 0;
protected:
AndroidAppCommunicationTestSupport() = default;
};
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CONTENT_ANDROID_APP_COMMUNICATION_TEST_SUPPORT_H_
// Copyright 2020 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 "components/payments/content/android_app_communication_test_support.h"
#include <utility>
#include "components/arc/mojom/payment_app.mojom.h"
#include "components/arc/pay/arc_payment_app_bridge.h"
#include "components/arc/test/arc_payment_app_bridge_test_support.h"
#include "components/payments/core/method_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace payments {
namespace {
class ScopedInitializationChromeOS
: public AndroidAppCommunicationTestSupport::ScopedInitialization {
public:
ScopedInitializationChromeOS(arc::ArcServiceManager* manager,
arc::mojom::PaymentAppInstance* instance)
: scoped_set_instance_(manager, instance) {}
~ScopedInitializationChromeOS() override = default;
ScopedInitializationChromeOS(const ScopedInitializationChromeOS& other) =
delete;
ScopedInitializationChromeOS& operator=(
const ScopedInitializationChromeOS& other) = delete;
private:
arc::ArcPaymentAppBridgeTestSupport::ScopedSetInstance scoped_set_instance_;
};
class AndroidAppCommunicationTestSupportChromeOS
: public AndroidAppCommunicationTestSupport {
public:
AndroidAppCommunicationTestSupportChromeOS() = default;
~AndroidAppCommunicationTestSupportChromeOS() override = default;
AndroidAppCommunicationTestSupportChromeOS(
const AndroidAppCommunicationTestSupportChromeOS& other) = delete;
AndroidAppCommunicationTestSupportChromeOS& operator=(
const AndroidAppCommunicationTestSupportChromeOS& other) = delete;
bool AreAndroidAppsSupportedOnThisPlatform() const override { return true; }
std::unique_ptr<ScopedInitialization> CreateScopedInitialization() override {
return std::make_unique<ScopedInitializationChromeOS>(support_.manager(),
support_.instance());
}
void ExpectNoListOfPaymentAppsQuery() override {
EXPECT_CALL(*support_.instance(),
IsPaymentImplemented(testing::_, testing::_))
.Times(0);
}
void ExpectNoIsReadyToPayQuery() override {
EXPECT_CALL(*support_.instance(), IsReadyToPay(testing::_, testing::_))
.Times(0);
}
void ExpectNoPaymentAppInvoke() override {
EXPECT_CALL(*support_.instance(), InvokePaymentApp(testing::_, testing::_))
.Times(0);
}
void ExpectQueryListOfPaymentAppsAndRespond(
std::vector<std::unique_ptr<AndroidAppDescription>> apps) override {
// Move |apps| into a member variable, so it's still alive by the time the
// RespondToGetAppDescriptions() method is executed at some point in the
// future.
apps_ = std::move(apps);
EXPECT_CALL(*support_.instance(),
IsPaymentImplemented(testing::_, testing::_))
.WillOnce(testing::Invoke(
[&](const std::string& package_name,
arc::ArcPaymentAppBridge::IsPaymentImplementedCallback
callback) {
RespondToGetAppDescriptions(package_name, std::move(callback));
}));
}
void ExpectQueryIsReadyToPayAndRespond(bool is_ready_to_pay) override {
EXPECT_CALL(*support_.instance(), IsReadyToPay(testing::_, testing::_))
.WillOnce(testing::Invoke(
[is_ready_to_pay](
arc::mojom::PaymentParametersPtr parameters,
arc::ArcPaymentAppBridge::IsReadyToPayCallback callback) {
std::move(callback).Run(
arc::mojom::IsReadyToPayResult::NewResponse(is_ready_to_pay));
}));
}
void ExpectInvokePaymentAppAndRespond(
bool is_activity_result_ok,
const std::string& payment_method_identifier,
const std::string& stringified_details) override {
EXPECT_CALL(*support_.instance(), InvokePaymentApp(testing::_, testing::_))
.WillOnce(testing::Invoke(
[is_activity_result_ok, stringified_details](
arc::mojom::PaymentParametersPtr parameters,
arc::ArcPaymentAppBridge::InvokePaymentAppCallback callback) {
// Chrome OS supports only kGooglePlayBilling payment method
// identifier at this time, so the |payment_method_identifier|
// parameter is ignored here.
auto valid = arc::mojom::InvokePaymentAppValidResult::New();
valid->is_activity_result_ok = is_activity_result_ok;
valid->stringified_details = stringified_details;
std::move(callback).Run(
arc::mojom::InvokePaymentAppResult::NewValid(
std::move(valid)));
}));
}
content::BrowserContext* context() override { return support_.context(); }
private:
void RespondToGetAppDescriptions(
const std::string& package_name,
arc::ArcPaymentAppBridge::IsPaymentImplementedCallback callback) {
auto valid = arc::mojom::IsPaymentImplementedValidResult::New();
for (const auto& app : apps_) {
if (app->package == package_name) {
for (const auto& activity : app->activities) {
// Chrome OS supports only kGooglePlayBilling method at this time.
if (activity->default_payment_method == methods::kGooglePlayBilling) {
valid->activity_names.push_back(activity->name);
}
}
valid->service_names = app->service_names;
// Chrome OS supports only one payment app in Android subsystem at this
// time, i.e., the TWA that invoked Chrome.
break;
}
}
std::move(callback).Run(
arc::mojom::IsPaymentImplementedResult::NewValid(std::move(valid)));
}
arc::ArcPaymentAppBridgeTestSupport support_;
std::vector<std::unique_ptr<AndroidAppDescription>> apps_;
};
} // namespace
// Declared in cross-platform file
// //components/payments/content/android_app_communication_test_support.h
// static
std::unique_ptr<AndroidAppCommunicationTestSupport>
AndroidAppCommunicationTestSupport::Create() {
return std::make_unique<AndroidAppCommunicationTestSupportChromeOS>();
}
} // namespace payments
// Copyright 2020 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 "components/payments/content/android_app_communication_test_support.h"
#include <utility>
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
namespace payments {
namespace {
class AndroidAppCommunicationTestSupportStub
: public AndroidAppCommunicationTestSupport {
public:
AndroidAppCommunicationTestSupportStub() = default;
~AndroidAppCommunicationTestSupportStub() override = default;
AndroidAppCommunicationTestSupportStub(
const AndroidAppCommunicationTestSupportStub& other) = delete;
AndroidAppCommunicationTestSupportStub& operator=(
const AndroidAppCommunicationTestSupportStub& other) = delete;
bool AreAndroidAppsSupportedOnThisPlatform() const override { return false; }
std::unique_ptr<ScopedInitialization> CreateScopedInitialization() override {
return std::make_unique<ScopedInitialization>();
}
void ExpectNoListOfPaymentAppsQuery() override {}
void ExpectNoIsReadyToPayQuery() override {}
void ExpectNoPaymentAppInvoke() override {}
void ExpectQueryListOfPaymentAppsAndRespond(
std::vector<std::unique_ptr<AndroidAppDescription>> apps) override {}
void ExpectQueryIsReadyToPayAndRespond(bool is_ready_to_pay) override {}
void ExpectInvokePaymentAppAndRespond(
bool is_activity_result_ok,
const std::string& payment_method_identifier,
const std::string& stringified_details) override {}
content::BrowserContext* context() override { return &context_; }
private:
content::BrowserTaskEnvironment environment_;
content::TestBrowserContext context_;
};
} // namespace
// Declared in cross-platform file
// //components/payments/content/android_app_communication_test_support.h
// static
std::unique_ptr<AndroidAppCommunicationTestSupport>
AndroidAppCommunicationTestSupport::Create() {
return std::make_unique<AndroidAppCommunicationTestSupportStub>();
}
} // namespace payments
// Copyright 2020 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 "components/payments/content/android_app_communication_test_support.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace payments {
namespace {
TEST(AndroidAppCommunicationTest, SmokeTest) {
auto support = AndroidAppCommunicationTestSupport::Create();
auto scoped_initialization = support->CreateScopedInitialization();
support->ExpectNoListOfPaymentAppsQuery();
support->ExpectNoIsReadyToPayQuery();
support->ExpectNoPaymentAppInvoke();
}
} // namespace
} // namespace payments
......@@ -4,6 +4,8 @@
static_library("core") {
sources = [
"android_app_description.cc",
"android_app_description.h",
"autofill_card_validation.cc",
"autofill_card_validation.h",
"basic_card_response.cc",
......
// Copyright 2020 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 "components/payments/core/android_app_description.h"
namespace payments {
AndroidActivityDescription::AndroidActivityDescription() = default;
AndroidActivityDescription::~AndroidActivityDescription() = default;
AndroidAppDescription::AndroidAppDescription() = default;
AndroidAppDescription::~AndroidAppDescription() = default;
} // namespace payments
// Copyright 2020 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 COMPONENTS_PAYMENTS_CORE_ANDROID_APP_DESCRIPTION_H_
#define COMPONENTS_PAYMENTS_CORE_ANDROID_APP_DESCRIPTION_H_
#include <string>
#include <vector>
namespace payments {
// Describes an Android activity with org.chromium.intent.action.PAY intent
// filter. Documentation:
// https://web.dev/android-payment-apps-overview/
struct AndroidActivityDescription {
AndroidActivityDescription();
~AndroidActivityDescription();
// Disallow copy and assign.
AndroidActivityDescription(const AndroidActivityDescription& other) = delete;
AndroidActivityDescription& operator=(
const AndroidActivityDescription& other) = delete;
// The name of the activity, e.g., "com.example.app.PaymentActivity".
std::string name;
// The payment method identifier from the
// "org.chromium.default_payment_method_name" metadata value of this activity.
// For example, "https://example.com/web-pay".
//
// The metadata value of "org.chromium.payment_method_names" is not yet used
// here, so it's omitted from the struct at this time.
std::string default_payment_method;
};
// Describes an Android app that can handle payments.
struct AndroidAppDescription {
AndroidAppDescription();
~AndroidAppDescription();
// Disallow copy and assign.
AndroidAppDescription(const AndroidAppDescription& other) = delete;
AndroidAppDescription& operator=(const AndroidAppDescription& other) = delete;
// The name of the Android package of this app, e.g., "com.example.app".
std::string package;
// The list of activities with org.chromium.intent.action.PAY intent filters
// in this app.
std::vector<std::unique_ptr<AndroidActivityDescription>> activities;
// The list of service names with org.chromium.intent.action.IS_READY_TO_PAY
// intent filters in this app. For example,
// ["com.example.app.IsReadyToPayService"].
//
// Note that it's a mistake to declare multiple IS_READY_TO_PAY services in an
// app. This mistake would be reported to the developer.
std::vector<std::string> service_names;
};
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CORE_ANDROID_APP_DESCRIPTION_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment