blob: d97493c9b934e018254e04e00ba179314b1b38f9 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/android/password_ui_view_android.h"
#include <jni.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/export/password_csv_writer.h"
#include "components/password_manager/core/browser/ui/credential_provider_interface.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
namespace android {
using autofill::PasswordForm;
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
namespace {
// Specific deleter for PasswordUIViewAndroid, which calls
// PasswordUIViewAndroid::Destroy on the object. Use this with a
// std::unique_ptr.
struct PasswordUIViewAndroidDestroyDeleter {
inline void operator()(void* ptr) const {
(static_cast<PasswordUIViewAndroid*>(ptr)->Destroy(
nullptr, JavaParamRef<jobject>(nullptr)));
}
};
class FakeCredentialProvider
: public password_manager::CredentialProviderInterface {
public:
FakeCredentialProvider() = default;
~FakeCredentialProvider() override = default;
// password_manager::CredentialProviderInterface
std::vector<std::unique_ptr<PasswordForm>> GetAllPasswords() override;
// Adds a PasswordForm specified by the arguments to the list returned by
// GetAllPasswords.
void AddPasswordEntry(const std::string& origin,
const std::string& username,
const std::string& password);
private:
std::vector<std::unique_ptr<PasswordForm>> passwords_;
DISALLOW_COPY_AND_ASSIGN(FakeCredentialProvider);
};
std::vector<std::unique_ptr<PasswordForm>>
FakeCredentialProvider::GetAllPasswords() {
std::vector<std::unique_ptr<PasswordForm>> clone;
for (const auto& password : passwords_) {
clone.push_back(std::make_unique<PasswordForm>(*password));
}
return clone;
}
void FakeCredentialProvider::AddPasswordEntry(const std::string& origin,
const std::string& username,
const std::string& password) {
auto form = std::make_unique<PasswordForm>();
form->origin = GURL(origin);
form->signon_realm = origin;
form->username_value = base::UTF8ToUTF16(username);
form->password_value = base::UTF8ToUTF16(password);
passwords_.push_back(std::move(form));
}
} // namespace
class PasswordUIViewAndroidTest : public ::testing::Test {
protected:
PasswordUIViewAndroidTest()
: testing_profile_manager_(TestingBrowserProcess::GetGlobal()),
env_(AttachCurrentThread()) {}
void SetUp() override {
ASSERT_TRUE(testing_profile_manager_.SetUp());
testing_profile_ =
testing_profile_manager_.CreateTestingProfile("test profile");
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
content::TestBrowserThreadBundle thread_bundle_;
TestingProfileManager testing_profile_manager_;
TestingProfile* testing_profile_;
JNIEnv* env_;
base::ScopedTempDir temp_dir_;
};
// Test that the asynchronous processing of password serialization controlled by
// PasswordUIViewAndroid arrives at the same result as synchronous way to
// serialize the passwords.
TEST_F(PasswordUIViewAndroidTest, GetSerializedPasswords) {
FakeCredentialProvider provider;
provider.AddPasswordEntry("https://example.com", "username", "password");
// Let the PasswordCSVWriter compute the result instead of hard-coding it,
// because this test focuses on PasswordUIView and not on detecting changes in
// PasswordCSVWriter.
const std::string expected_result =
password_manager::PasswordCSVWriter::SerializePasswords(
provider.GetAllPasswords());
std::unique_ptr<PasswordUIViewAndroid, PasswordUIViewAndroidDestroyDeleter>
password_ui_view(
new PasswordUIViewAndroid(env_, JavaParamRef<jobject>(nullptr)));
PasswordUIViewAndroid::SerializationResult serialized_passwords;
password_ui_view->set_export_target_for_testing(&serialized_passwords);
password_ui_view->set_credential_provider_for_testing(&provider);
password_ui_view->HandleSerializePasswords(
env_, nullptr,
base::android::ConvertUTF8ToJavaString(
env_, temp_dir_.GetPath().AsUTF8Unsafe()),
nullptr, nullptr);
content::RunAllTasksUntilIdle();
// The buffer for actual result is 1 byte longer than the expected data to be
// able to detect when the actual data are too long.
char actual_result[expected_result.size() + 1];
int number_of_bytes_read = base::ReadFile(
base::FilePath::FromUTF8Unsafe(serialized_passwords.exported_file_path),
actual_result, expected_result.size() + 1);
EXPECT_EQ(static_cast<int>(expected_result.size()), number_of_bytes_read);
EXPECT_EQ(expected_result,
std::string(actual_result,
(number_of_bytes_read < 0) ? 0 : number_of_bytes_read));
EXPECT_EQ(1, serialized_passwords.entries_count);
EXPECT_FALSE(serialized_passwords.exported_file_path.empty());
EXPECT_EQ(std::string(), serialized_passwords.error);
}
// Test that destroying the PasswordUIView when tasks are pending does not lead
// to crashes.
TEST_F(PasswordUIViewAndroidTest, GetSerializedPasswords_Cancelled) {
FakeCredentialProvider provider;
provider.AddPasswordEntry("https://example.com", "username", "password");
std::unique_ptr<PasswordUIViewAndroid, PasswordUIViewAndroidDestroyDeleter>
password_ui_view(
new PasswordUIViewAndroid(env_, JavaParamRef<jobject>(nullptr)));
PasswordUIViewAndroid::SerializationResult serialized_passwords;
serialized_passwords.entries_count = 123;
serialized_passwords.exported_file_path = "somepath";
password_ui_view->set_export_target_for_testing(&serialized_passwords);
password_ui_view->set_credential_provider_for_testing(&provider);
base::android::ScopedJavaLocalRef<jstring> java_target_dir =
base::android::ConvertUTF8ToJavaString(
env_, temp_dir_.GetPath().AsUTF8Unsafe());
password_ui_view->HandleSerializePasswords(
env_, nullptr,
base::android::JavaParamRef<jstring>(env_, java_target_dir.obj()),
nullptr, nullptr);
// Register the PasswordUIView for deletion. It should not destruct itself
// before the background tasks are run. The results of the background tasks
// are waited for and then thrown out, so |serialized_passwords| should not be
// overwritten.
password_ui_view.reset();
// Now run the background tasks (and the subsequent deletion).
content::RunAllTasksUntilIdle();
EXPECT_EQ(123, serialized_passwords.entries_count);
EXPECT_EQ("somepath", serialized_passwords.exported_file_path);
EXPECT_EQ(std::string(), serialized_passwords.error);
}
// Test that an I/O error is reported.
TEST_F(PasswordUIViewAndroidTest, GetSerializedPasswords_WriteFailed) {
FakeCredentialProvider provider;
provider.AddPasswordEntry("https://example.com", "username", "password");
std::unique_ptr<PasswordUIViewAndroid, PasswordUIViewAndroidDestroyDeleter>
password_ui_view(
new PasswordUIViewAndroid(env_, JavaParamRef<jobject>(nullptr)));
PasswordUIViewAndroid::SerializationResult serialized_passwords;
password_ui_view->set_export_target_for_testing(&serialized_passwords);
password_ui_view->set_credential_provider_for_testing(&provider);
base::android::ScopedJavaLocalRef<jstring> java_temp_file =
base::android::ConvertUTF8ToJavaString(
env_, "/This directory cannot be created");
password_ui_view->HandleSerializePasswords(
env_, nullptr,
base::android::JavaParamRef<jstring>(env_, java_temp_file.obj()), nullptr,
nullptr);
content::RunAllTasksUntilIdle();
EXPECT_EQ(0, serialized_passwords.entries_count);
EXPECT_FALSE(serialized_passwords.error.empty());
}
} // namespace android