From 7ccd9683b00f8ada49760a387df366be79fe4513 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 27 Jan 2022 19:42:18 -0300 Subject: [PATCH 01/20] [local_auth] Windows support. --- packages/local_auth/local_auth/CHANGELOG.md | 4 + .../local_auth/example/windows/CMakeLists.txt | 95 +++++++ .../example/windows/flutter/CMakeLists.txt | 103 ++++++++ .../windows/flutter/generated_plugins.cmake | 16 ++ .../example/windows/runner/CMakeLists.txt | 17 ++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 64 +++++ .../example/windows/runner/flutter_window.h | 36 +++ .../example/windows/runner/main.cpp | 45 ++++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 66 +++++ .../local_auth/example/windows/runner/utils.h | 22 ++ .../example/windows/runner/win32_window.cpp | 240 ++++++++++++++++++ .../example/windows/runner/win32_window.h | 98 +++++++ .../local_auth/local_auth/lib/local_auth.dart | 5 +- packages/local_auth/local_auth/pubspec.yaml | 4 +- .../local_auth/windows/CMakeLists.txt | 59 +++++ .../include/local_auth/local_auth_plugin.h | 26 ++ .../local_auth/windows/local_auth_plugin.cpp | 187 ++++++++++++++ 21 files changed, 1241 insertions(+), 3 deletions(-) create mode 100644 packages/local_auth/local_auth/example/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/runner/Runner.rc create mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/main.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/resource.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico create mode 100644 packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest create mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.h create mode 100644 packages/local_auth/local_auth/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/windows/include/local_auth/local_auth_plugin.h create mode 100644 packages/local_auth/local_auth/windows/local_auth_plugin.cpp diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index d17eb4a97a21..da07adb83d1e 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.0 + +* Added Windows support. + ## 1.1.10 * Removes dependency on `meta`. diff --git a/packages/local_auth/local_auth/example/windows/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..1633297a0c7c --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..8b492fc5cbe3 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + local_auth +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..de2d8916b72b --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/local_auth/local_auth/example/windows/runner/Runner.rc b/packages/local_auth/local_auth/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..7e35b9f56a22 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.flutter.plugins" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 io.flutter.plugins. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..217bf9b69e67 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +// 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_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..7cbf3d3ebbb2 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h @@ -0,0 +1,36 @@ +// 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. +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/main.cpp b/packages/local_auth/local_auth/example/windows/runner/main.cpp new file mode 100644 index 000000000000..1285aabf714a --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/main.cpp @@ -0,0 +1,45 @@ +// 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 +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/local_auth/local_auth/example/windows/runner/resource.h b/packages/local_auth/local_auth/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico b/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.cpp b/packages/local_auth/local_auth/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..8b8eaa54539a --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/utils.cpp @@ -0,0 +1,66 @@ +// 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 "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.h b/packages/local_auth/local_auth/example/windows/runner/utils.h new file mode 100644 index 000000000000..6d1cc48f0426 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/utils.h @@ -0,0 +1,22 @@ +// 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. +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..34738de2d35b --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp @@ -0,0 +1,240 @@ +// 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 "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.h b/packages/local_auth/local_auth/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..0f8bd1b7f920 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +// 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. +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/local_auth/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart index 3e925c00e5ae..be94c9e103bb 100644 --- a/packages/local_auth/local_auth/lib/local_auth.dart +++ b/packages/local_auth/local_auth/lib/local_auth.dart @@ -113,10 +113,11 @@ class LocalAuthentication { args.addAll(iOSAuthStrings.args); } else if (_platform.isAndroid) { args.addAll(androidAuthStrings.args); - } else { + } else if (!_platform.isWindows) { throw PlatformException( code: otherOperatingSystem, - message: 'Local authentication does not support non-Android/iOS ' + message: + 'Local authentication does not support non-Android/iOS/Windows ' 'operating systems.', details: 'Your operating system is ${_platform.operatingSystem}', ); diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index cd9d0d9760f4..b7836783397a 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.10 +version: 1.2.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -17,6 +17,8 @@ flutter: pluginClass: LocalAuthPlugin ios: pluginClass: FLTLocalAuthPlugin + windows: + pluginClass: LocalAuthPlugin dependencies: flutter: diff --git a/packages/local_auth/local_auth/windows/CMakeLists.txt b/packages/local_auth/local_auth/windows/CMakeLists.txt new file mode 100644 index 000000000000..23826be79e6b --- /dev/null +++ b/packages/local_auth/local_auth/windows/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.15) +set(PROJECT_NAME "local_auth") +set(CPPWINRT_VERSION "2.0.210806.1") +project(${PROJECT_NAME} LANGUAGES CXX) +include(FetchContent) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +FetchContent_Declare(nuget + URL "https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe" + URL_HASH SHA256=04eb6c4fe4213907e2773e1be1bbbd730e9a655a3c9c58387ce8d4a714a5b9e1 + DOWNLOAD_NO_EXTRACT true +) + +find_program(NUGET nuget) +if (NOT NUGET) + message("Nuget.exe not found, trying to download or use cached version.") + FetchContent_MakeAvailable(nuget) + set(NUGET ${nuget_SOURCE_DIR}/nuget.exe) +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}") +endif() + +set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) +execute_process(COMMAND + ${CPPWINRT} -input sdk -output include + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to run cppwinrt.exe") +endif() + +include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) + +add_library(${PLUGIN_NAME} SHARED + "include/local_auth/local_auth_plugin.h" + "local_auth_plugin.cpp" + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_features(${PLUGIN_NAME} PRIVATE cxx_std_20) +target_compile_options(${PLUGIN_NAME} PRIVATE /await) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin windowsapp) + +# List of absolute paths to libraries that should be bundled with the plugin +set(file_chooser_bundled_libraries + "" + PARENT_SCOPE +) \ No newline at end of file diff --git a/packages/local_auth/local_auth/windows/include/local_auth/local_auth_plugin.h b/packages/local_auth/local_auth/windows/include/local_auth/local_auth_plugin.h new file mode 100644 index 000000000000..2e4893e75abd --- /dev/null +++ b/packages/local_auth/local_auth/windows/include/local_auth/local_auth_plugin.h @@ -0,0 +1,26 @@ +// 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. +#ifndef FLUTTER_PLUGIN_LOCAL_AUTH_PLUGIN_H_ +#define FLUTTER_PLUGIN_LOCAL_AUTH_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_LOCAL_AUTH_PLUGIN_H_ diff --git a/packages/local_auth/local_auth/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth/windows/local_auth_plugin.cpp new file mode 100644 index 000000000000..05d597912836 --- /dev/null +++ b/packages/local_auth/local_auth/windows/local_auth_plugin.cpp @@ -0,0 +1,187 @@ +// 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 "include/local_auth/local_auth_plugin.h" + +// This must be included before many other Windows headers. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace { + +using namespace flutter; +using namespace winrt; + +template +T GetArgument(const std::string arg, const EncodableValue* args, T fallback) { + T result{fallback}; + const auto* arguments = std::get_if(args); + if (arguments) { + auto result_it = arguments->find(EncodableValue(arg)); + if (result_it != arguments->end()) { + result = std::get(result_it->second); + } + } + return result; +} + +HWND GetRootWindow(flutter::FlutterView* view) { + return ::GetAncestor(view->GetNativeWindow(), GA_ROOT); +} + +std::wstring s2ws(const std::string& s) { + int len; + int slength = (int)s.length() + 1; + len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); + std::wstring r(len, L'\0'); + MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, &r[0], len); + return r; +} + +class LocalAuthPlugin : public Plugin { + public: + static void RegisterWithRegistrar(PluginRegistrarWindows* registrar); + + LocalAuthPlugin(flutter::PluginRegistrarWindows* registrar); + + virtual ~LocalAuthPlugin(); + + private: + flutter::PluginRegistrarWindows* registrar_; + + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall(const MethodCall& method_call, + std::unique_ptr> result); + + winrt::fire_and_forget Authenticate( + const MethodCall& method_call, + std::unique_ptr> result); + winrt::fire_and_forget GetAvailableBiometrics( + std::unique_ptr> result); + winrt::fire_and_forget IsDeviceSupported( + std::unique_ptr> result); +}; + +// static +void LocalAuthPlugin::RegisterWithRegistrar(PluginRegistrarWindows* registrar) { + auto channel = std::make_unique>( + registrar->messenger(), "plugins.flutter.io/local_auth", + &StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(registrar); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +LocalAuthPlugin::LocalAuthPlugin(flutter::PluginRegistrarWindows* registrar) + : registrar_(registrar) {} + +LocalAuthPlugin::~LocalAuthPlugin() {} + +void LocalAuthPlugin::HandleMethodCall( + const MethodCall& method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("authenticate") == 0) { + Authenticate(method_call, std::move(result)); + } else if (method_call.method_name().compare("getAvailableBiometrics") == 0) { + GetAvailableBiometrics(std::move(result)); + } else if (method_call.method_name().compare("isDeviceSupported") == 0) { + IsDeviceSupported(std::move(result)); + } else { + result->NotImplemented(); + } +} + +winrt::fire_and_forget LocalAuthPlugin::Authenticate( + const MethodCall& method_call, + std::unique_ptr> result) { + auto reasonW = s2ws(GetArgument( + "localizedReason", method_call.arguments(), std::string())); + + auto ucvAvailability = co_await Windows::Security::Credentials::UI:: + UserConsentVerifier::CheckAvailabilityAsync(); + if (ucvAvailability != Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + result->Error("NotAvailable", "Required security features not enabled"); + co_return; + } + + auto userConsentVerifierInterop = winrt::get_activation_factory< + Windows::Security::Credentials::UI::UserConsentVerifier, + IUserConsentVerifierInterop>(); + + auto hWnd = GetRootWindow(registrar_->GetView()); + + HSTRING hReason; + WindowsCreateString(reasonW.c_str(), (uint32_t)reasonW.size(), &hReason); + + try { + auto consentResult = + co_await winrt::capture>( + userConsentVerifierInterop, + &::IUserConsentVerifierInterop::RequestVerificationForWindowAsync, + hWnd, hReason); + + result->Success(EncodableValue( + consentResult == Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified)); + } catch (...) { + result->Success(EncodableValue(false)); + } + WindowsDeleteString(hReason); +} + +winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( + std::unique_ptr> result) { + try { + flutter::EncodableList biometrics; + auto ucvAvailability = co_await Windows::Security::Credentials::UI:: + UserConsentVerifier::CheckAvailabilityAsync(); + if (ucvAvailability == Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + biometrics.push_back(EncodableValue("fingerprint")); + biometrics.push_back(EncodableValue("face")); + biometrics.push_back(EncodableValue("iris")); + } + result->Success(biometrics); + } catch (const std::exception& e) { + result->Error("no_biometrics_available", e.what()); + } +} + +winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( + std::unique_ptr> result) { + auto ucvAvailability = co_await Windows::Security::Credentials::UI:: + UserConsentVerifier::CheckAvailabilityAsync(); + result->Success(EncodableValue( + ucvAvailability == Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available)); +} + +} // namespace + +void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + LocalAuthPlugin::RegisterWithRegistrar( + PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} From c997ad8ae500875b4be4150d80c3b0c52540166b Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 24 Feb 2022 18:07:30 -0800 Subject: [PATCH 02/20] Handling biometricOnly parameter. --- .../local_auth/lib/error_codes.dart | 5 ++- .../local_auth/windows/local_auth_plugin.cpp | 31 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/local_auth/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/error_codes.dart index bcf15b7b2154..024bcc0da151 100644 --- a/packages/local_auth/local_auth/lib/error_codes.dart +++ b/packages/local_auth/local_auth/lib/error_codes.dart @@ -15,7 +15,7 @@ const String notEnrolled = 'NotEnrolled'; /// Indicates the device does not have a Touch ID/fingerprint scanner. const String notAvailable = 'NotAvailable'; -/// Indicates the device operating system is not iOS or Android. +/// Indicates the device operating system is not iOS, Android or Windows. const String otherOperatingSystem = 'OtherOperatingSystem'; /// Indicates the API lock out due to too many attempts. @@ -24,3 +24,6 @@ const String lockedOut = 'LockedOut'; /// Indicates the API being disabled due to too many lock outs. /// Strong authentication like PIN/Pattern/Password is required to unlock. const String permanentlyLockedOut = 'PermanentlyLockedOut'; + +/// Indicates that the biometricOnly parameter can't be true on Windows +const String biometricOnlyNotSupported = 'biometricOnlyNotSupported'; diff --git a/packages/local_auth/local_auth/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth/windows/local_auth_plugin.cpp index 05d597912836..430e425e16f0 100644 --- a/packages/local_auth/local_auth/windows/local_auth_plugin.cpp +++ b/packages/local_auth/local_auth/windows/local_auth_plugin.cpp @@ -26,6 +26,7 @@ using namespace flutter; using namespace winrt; template +// Helper method for getting an argument from an EncodableValue T GetArgument(const std::string arg, const EncodableValue* args, T fallback) { T result{fallback}; const auto* arguments = std::get_if(args); @@ -38,10 +39,12 @@ T GetArgument(const std::string arg, const EncodableValue* args, T fallback) { return result; } +// Returns the window's HWND for a given FlutterView HWND GetRootWindow(flutter::FlutterView* view) { return ::GetAncestor(view->GetNativeWindow(), GA_ROOT); } +// Converts the given UTF-8 string to UTF-16. std::wstring s2ws(const std::string& s) { int len; int slength = (int)s.length() + 1; @@ -91,6 +94,7 @@ void LocalAuthPlugin::RegisterWithRegistrar(PluginRegistrarWindows* registrar) { registrar->AddPlugin(std::move(plugin)); } +// Default constructor for LocalAuthPlugin LocalAuthPlugin::LocalAuthPlugin(flutter::PluginRegistrarWindows* registrar) : registrar_(registrar) {} @@ -110,16 +114,37 @@ void LocalAuthPlugin::HandleMethodCall( } } +// Starts authentication process winrt::fire_and_forget LocalAuthPlugin::Authenticate( const MethodCall& method_call, std::unique_ptr> result) { auto reasonW = s2ws(GetArgument( "localizedReason", method_call.arguments(), std::string())); + auto biometricOnly = + GetArgument("biometricOnly", method_call.arguments(), false); + if (biometricOnly) { + result->Error("biometricOnlyNotSupported", + "Windows doesn't support the biometricOnly parameter."); + co_return; + } + auto ucvAvailability = co_await Windows::Security::Credentials::UI:: UserConsentVerifier::CheckAvailabilityAsync(); - if (ucvAvailability != Windows::Security::Credentials::UI:: - UserConsentVerifierAvailability::Available) { + + if (ucvAvailability == + Windows::Security::Credentials::UI::UserConsentVerifierAvailability:: + DeviceNotPresent) { + result->Error("NoHardware", "No biometric hardware found"); + co_return; + } else if (ucvAvailability == + Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::NotConfiguredForUser) { + result->Error("NotEnrolled", "No biometrics enrolled on this device."); + co_return; + } else if (ucvAvailability != + Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { result->Error("NotAvailable", "Required security features not enabled"); co_return; } @@ -150,6 +175,7 @@ winrt::fire_and_forget LocalAuthPlugin::Authenticate( WindowsDeleteString(hReason); } +// Returns biometric types available on device winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( std::unique_ptr> result) { try { @@ -168,6 +194,7 @@ winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( } } +// Returns whether the device supports Windows Hello or not winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( std::unique_ptr> result) { auto ucvAvailability = co_await Windows::Security::Credentials::UI:: From 975ca8db6d3ebdb9477d8039cd7d4719ad66b82d Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 7 Apr 2022 16:03:04 -0700 Subject: [PATCH 03/20] Fixed readme --- packages/local_auth/local_auth/CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index ae3b86171041..b24be54ee07f 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,10 +1,7 @@ -## 1.2.0 - -* Added Windows support. - ## NEXT * Adds OS version support information to README. +* Added Windows support. ## 1.1.11 From 3fcbca7c22d2fe48bfa7432148dcfa4163e18625 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 8 Apr 2022 11:23:01 -0700 Subject: [PATCH 04/20] [local_auth] Fix version and readme. --- packages/local_auth/local_auth/README.md | 6 +++--- packages/local_auth/local_auth/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 3a60e45a57eb..e7fd2e3ded70 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -6,9 +6,9 @@ the user. This means referring to biometric authentication on iOS (Touch ID or lock code) and the fingerprint APIs on Android (introduced in Android 6.0). -| | Android | iOS | -|-------------|-----------|------| -| **Support** | SDK 16+\* | 9.0+ | +| | Android | iOS | Windows | +|-------------|-----------|------|-------------| +| **Support** | SDK 16+\* | 9.0+ | Windows 10+ | ## Usage diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index b7836783397a..a79b8511fc70 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.2.0 +version: 1.1.11 environment: sdk: ">=2.14.0 <3.0.0" From 835f5cc979bf9c8c967bae23a5c303ca51945405 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 15 Apr 2022 12:37:29 -0700 Subject: [PATCH 05/20] [local_auth] Small fixes. --- packages/local_auth/local_auth/pubspec.yaml | 2 +- .../local_auth_windows/example/lib/main.dart | 2 +- .../local_auth_windows/example/pubspec.yaml | 2 +- .../example/windows/CMakeLists.txt | 9 ++++++-- .../example/windows/runner/flutter_window.cpp | 4 ++++ .../example/windows/runner/flutter_window.h | 4 ++++ .../example/windows/runner/main.cpp | 11 ++++++---- .../example/windows/runner/resource.h | 10 ++++----- .../example/windows/runner/utils.cpp | 15 ++++++++----- .../example/windows/runner/utils.h | 4 ++++ .../example/windows/runner/win32_window.cpp | 22 ++++++++----------- .../example/windows/runner/win32_window.h | 13 ++++++----- .../lib/local_auth_windows.dart | 6 +++-- 13 files changed, 63 insertions(+), 41 deletions(-) diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index b634740ef268..fab82bf311e1 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 2.0.0 +version: 2.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart index 077f0027becf..f799c2cd20cf 100644 --- a/packages/local_auth/local_auth_windows/example/lib/main.dart +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -8,8 +8,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:local_auth_windows/local_auth_windows.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; void main() { runApp(MyApp()); diff --git a/packages/local_auth/local_auth_windows/example/pubspec.yaml b/packages/local_auth/local_auth_windows/example/pubspec.yaml index 0acba266eaf3..266c9fc7140d 100644 --- a/packages/local_auth/local_auth_windows/example/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/example/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + local_auth_platform_interface: ^1.0.0 local_auth_windows: # When depending on this package from a real application you should use: # local_auth_windows: ^x.y.z @@ -16,7 +17,6 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - local_auth_platform_interface: ^1.0.0 dev_dependencies: flutter_driver: diff --git a/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt index 1633297a0c7c..6fd0f75e569e 100644 --- a/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt +++ b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.14) -project(example LANGUAGES CXX) +project(local_auth_windows_example LANGUAGES CXX) -set(BINARY_NAME "example") +set(BINARY_NAME "local_auth_windows_example") cmake_policy(SET CMP0063 NEW) @@ -46,6 +46,11 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) # Application build add_subdirectory("runner") +# Enable the test target. +set(local_auth_windows_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS local_auth_windows_test) + # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp index b43b9095ea3a..8254bd9ff3c1 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp +++ b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp @@ -1,3 +1,7 @@ +// 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_window.h" #include diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h index 6da0652f05f2..f1fc669093d0 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h +++ b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h @@ -1,3 +1,7 @@ +// 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. + #ifndef RUNNER_FLUTTER_WINDOW_H_ #define RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp index bcb57b0e2aac..4e37ae286c01 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp +++ b/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp @@ -1,3 +1,7 @@ +// 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 #include #include @@ -6,7 +10,7 @@ #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _In_ wchar_t* command_line, _In_ int show_command) { // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { @@ -19,15 +23,14 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, flutter::DartProject project(L"data"); - std::vector command_line_arguments = - GetCommandLineArguments(); + std::vector command_line_arguments = GetCommandLineArguments(); project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"example", origin, size)) { + if (!window.CreateAndShow(L"local_auth_windows_example", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/resource.h b/packages/local_auth/local_auth_windows/example/windows/runner/resource.h index 66a65d1e4a79..d5d958dc4257 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/resource.h +++ b/packages/local_auth/local_auth_windows/example/windows/runner/resource.h @@ -2,15 +2,15 @@ // Microsoft Visual C++ generated include file. // Used by Runner.rc // -#define IDI_APP_ICON 101 +#define IDI_APP_ICON 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp index d19bdbbcc322..fb7e945a63b7 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp +++ b/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp @@ -1,3 +1,7 @@ +// 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 "utils.h" #include @@ -9,7 +13,7 @@ void CreateAndAttachConsole() { if (::AllocConsole()) { - FILE *unused; + FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { _dup2(_fileno(stdout), 1); } @@ -45,17 +49,16 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); if (target_length == 0) { return std::string(); } std::string utf8_string; utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), target_length, nullptr, nullptr); if (converted_length == 0) { return std::string(); diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/utils.h b/packages/local_auth/local_auth_windows/example/windows/runner/utils.h index 3879d5475579..bd81e1e02338 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/utils.h +++ b/packages/local_auth/local_auth_windows/example/windows/runner/utils.h @@ -1,3 +1,7 @@ +// 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. + #ifndef RUNNER_UTILS_H_ #define RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp index c10f08dc7da6..85aa3614e8ad 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp +++ b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp @@ -1,3 +1,7 @@ +// 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 "win32_window.h" #include @@ -93,17 +97,14 @@ void WindowClassRegistrar::UnregisterWindowClass() { class_registered_ = false; } -Win32Window::Win32Window() { - ++g_active_window_count; -} +Win32Window::Win32Window() { ++g_active_window_count; } Win32Window::~Win32Window() { --g_active_window_count; Destroy(); } -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, const Size& size) { Destroy(); @@ -130,8 +131,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title, } // static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { if (message == WM_NCCREATE) { @@ -150,9 +150,7 @@ LRESULT CALLBACK Win32Window::WndProc(HWND const window, } LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { switch (message) { case WM_DESTROY: @@ -227,9 +225,7 @@ RECT Win32Window::GetClientArea() { return frame; } -HWND Win32Window::GetHandle() { - return window_handle_; -} +HWND Win32Window::GetHandle() { return window_handle_; } void Win32Window::SetQuitOnClose(bool quit_on_close) { quit_on_close_ = quit_on_close; diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h index 17ba431125b4..d2a730052223 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h +++ b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h @@ -1,3 +1,7 @@ +// 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. + #ifndef RUNNER_WIN32_WINDOW_H_ #define RUNNER_WIN32_WINDOW_H_ @@ -34,8 +38,7 @@ class Win32Window { // consistent size to will treat the width height passed in to this function // as logical pixels and scale to appropriate for the default monitor. Returns // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, + bool CreateAndShow(const std::wstring& title, const Point& origin, const Size& size); // Release OS resources associated with window. @@ -58,8 +61,7 @@ class Win32Window { // Processes and route salient window messages for mouse handling, // size change and DPI. Delegates handling of these to member overloads that // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, + virtual LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; @@ -78,8 +80,7 @@ class Win32Window { // non-client DPI scaling so that the non-client area automatically // responsponds to changes in DPI. All other messages are handled by // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; diff --git a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart index cd9f5c04a243..cfecbc95f75b 100644 --- a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart +++ b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart @@ -81,7 +81,9 @@ class LocalAuthWindows extends LocalAuthPlatform { Future isDeviceSupported() async => (await _channel.invokeMethod('isDeviceSupported')) ?? false; + /// Always returns false as this method is not supported on Windows. @override - Future stopAuthentication() async => - await _channel.invokeMethod('stopAuthentication') ?? false; + Future stopAuthentication() async { + return false; + } } From 9eaba3ccc274718f89e11fa12885b6330c39747b Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 15 Apr 2022 14:01:33 -0700 Subject: [PATCH 06/20] Small fix. --- packages/local_auth/local_auth/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 0a79b8fe3ce9..ca3ee886b2f7 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 2.1.0 * Adds Windows support. ## 2.0.0 From 22ec14ced453b59326c441e071bb29dfff14d7f1 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 15 Apr 2022 14:12:49 -0700 Subject: [PATCH 07/20] Fixed tests. --- packages/local_auth/local_auth/CHANGELOG.md | 3 --- packages/local_auth/local_auth/README.md | 6 +++--- .../example/windows/flutter/generated_plugins.cmake | 1 - packages/local_auth/local_auth/pubspec.yaml | 10 ++++++---- .../local_auth_windows/test/local_auth_test.dart | 11 +++-------- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index ca3ee886b2f7..deac871d935f 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,6 +1,3 @@ -## 2.1.0 -* Adds Windows support. - ## 2.0.0 * Migrates plugin to federated architecture. diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index e7fd2e3ded70..3a60e45a57eb 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -6,9 +6,9 @@ the user. This means referring to biometric authentication on iOS (Touch ID or lock code) and the fingerprint APIs on Android (introduced in Android 6.0). -| | Android | iOS | Windows | -|-------------|-----------|------|-------------| -| **Support** | SDK 16+\* | 9.0+ | Windows 10+ | +| | Android | iOS | +|-------------|-----------|------| +| **Support** | SDK 16+\* | 9.0+ | ## Usage diff --git a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake index 8dfe35a3460a..4d10c2518654 100644 --- a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake +++ b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake @@ -3,7 +3,6 @@ # list(APPEND FLUTTER_PLUGIN_LIST - local_auth_windows ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index fab82bf311e1..4f36572d11d3 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 2.1.0 +version: 2.0.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -16,8 +16,9 @@ flutter: default_package: local_auth_android ios: default_package: local_auth_ios - windows: - default_package: local_auth_windows + # Uncomment once the PR #4806 is merged. + # windows: + # default_package: local_auth_windows dependencies: flutter: @@ -25,7 +26,8 @@ dependencies: intl: ^0.17.0 local_auth_android: ^1.0.0 local_auth_ios: ^1.0.1 - local_auth_windows: ^1.0.0 + # Uncomment once the PR #4806 is merged. + # local_auth_windows: ^1.0.0 local_auth_platform_interface: ^1.0.1 dev_dependencies: diff --git a/packages/local_auth/local_auth_windows/test/local_auth_test.dart b/packages/local_auth/local_auth_windows/test/local_auth_test.dart index f0230af90a29..ba125745079d 100644 --- a/packages/local_auth/local_auth_windows/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_windows/test/local_auth_test.dart @@ -74,14 +74,9 @@ void main() { ); }); - test('stopAuthentication calls platform', () async { - await localAuthentication.stopAuthentication(); - expect( - log, - [ - isMethodCall('stopAuthentication', arguments: null), - ], - ); + test('stopAuthentication returns false', () async { + final bool result = await localAuthentication.stopAuthentication(); + expect(result, false); }); group('With device auth fail over', () { From 9349aab8d330352d7183f3f36dd9c4e471e512f3 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 15 Apr 2022 14:51:09 -0700 Subject: [PATCH 08/20] Added local_auth_windows tests. --- .../local_auth_windows/windows/CMakeLists.txt | 47 +- .../windows/test/local_auth_plugin_test.cpp | 1010 ++++++++++++++++ .../windows/test/local_auth_test.cpp | 344 ++++++ .../local_auth_windows/windows/test/mocks.h | 1015 +++++++++++++++++ 4 files changed, 2415 insertions(+), 1 deletion(-) create mode 100644 packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/test/mocks.h diff --git a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt index b9dcfbfe6cb3..3ef03f5d5e99 100644 --- a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt +++ b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt @@ -56,4 +56,49 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin wind set(file_chooser_bundled_libraries "" PARENT_SCOPE -) \ No newline at end of file +) + + +# === Tests === + +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/mocks.h + test/local_auth_plugin_test.cpp + test/local_auth_test.cpp + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE windowsapp) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp new file mode 100644 index 000000000000..76a68f7569bb --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp @@ -0,0 +1,1010 @@ +// 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 "local_auth_plugin.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" + +namespace local_auth_windows { +namespace test { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; +using ::testing::DoAll; +using ::testing::EndsWith; +using ::testing::Eq; +using ::testing::Pointee; +using ::testing::Return; + +TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerSuccessIfNoLocalAuths) { + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr local_auth_factory_ = + std::make_unique(); + std::unique_ptr result = + std::make_unique(); + + MockLocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(local_auth_factory_)); + + EXPECT_CALL(plugin, EnumerateVideoCaptureDeviceSources) + .Times(1) + .WillOnce([](IMFActivate*** devices, UINT32* count) { + *count = 0U; + *devices = static_cast( + CoTaskMemAlloc(sizeof(IMFActivate*) * (*count))); + return true; + }); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal).Times(1); + + plugin.HandleMethodCall( + flutter::MethodCall("availableLocalAuths", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerErrorIfFailsToEnumerateDevices) { + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr local_auth_factory_ = + std::make_unique(); + std::unique_ptr result = + std::make_unique(); + + MockLocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(local_auth_factory_)); + + EXPECT_CALL(plugin, EnumerateVideoCaptureDeviceSources) + .Times(1) + .WillOnce([](IMFActivate*** devices, UINT32* count) { return false; }); + + EXPECT_CALL(*result, ErrorInternal).Times(1); + EXPECT_CALL(*result, SuccessInternal).Times(0); + + plugin.HandleMethodCall( + flutter::MethodCall("availableLocalAuths", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, CreateHandlerCallsInitLocalAuth) { + std::unique_ptr result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr local_auth_factory_ = + std::make_unique(); + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kCreateLocalAuth))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, + AddPendingResult(Eq(PendingResultType::kCreateLocalAuth), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + EXPECT_CALL(*local_auth, InitLocalAuth) + .Times(1) + .WillOnce([cam = local_auth.get()]( + flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset) { + assert(cam->pending_result_); + return cam->pending_result_->Success(EncodableValue(1)); + }); + + // Move mocked local_auth to the factory to be passed + // for plugin with CreateLocalAuth function. + local_auth_factory_->pending_local_auth_ = std::move(local_auth); + + EXPECT_CALL(*local_auth_factory_, CreateLocalAuth(MOCK_DEVICE_ID)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(1)))); + + LocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(local_auth_factory_)); + EncodableMap args = { + {EncodableValue("local_authName"), EncodableValue(MOCK_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(result)); +} + +TEST(LocalAuthPlugin, CreateHandlerErrorOnInvalidDeviceId) { + std::unique_ptr result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr local_auth_factory_ = + std::make_unique(); + + LocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(local_auth_factory_)); + EncodableMap args = { + {EncodableValue("local_authName"), EncodableValue(MOCK_INVALID_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + EXPECT_CALL(*result, ErrorInternal).Times(1); + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(result)); +} + +TEST(LocalAuthPlugin, CreateHandlerErrorOnExistingDeviceId) { + std::unique_ptr first_create_result = + std::make_unique(); + std::unique_ptr second_create_result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr local_auth_factory_ = + std::make_unique(); + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kCreateLocalAuth))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, + AddPendingResult(Eq(PendingResultType::kCreateLocalAuth), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + EXPECT_CALL(*local_auth, InitLocalAuth) + .Times(1) + .WillOnce([cam = local_auth.get()]( + flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset) { + assert(cam->pending_result_); + return cam->pending_result_->Success(EncodableValue(1)); + }); + + EXPECT_CALL(*local_auth, HasDeviceId(Eq(MOCK_DEVICE_ID))) + .Times(1) + .WillOnce([cam = local_auth.get()](std::string& device_id) { + return cam->device_id_ == device_id; + }); + + // Move mocked local_auth to the factory to be passed + // for plugin with CreateLocalAuth function. + local_auth_factory_->pending_local_auth_ = std::move(local_auth); + + EXPECT_CALL(*local_auth_factory_, CreateLocalAuth(MOCK_DEVICE_ID)); + + EXPECT_CALL(*first_create_result, ErrorInternal).Times(0); + EXPECT_CALL(*first_create_result, + SuccessInternal(Pointee(EncodableValue(1)))); + + LocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(local_auth_factory_)); + EncodableMap args = { + {EncodableValue("local_authName"), EncodableValue(MOCK_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(first_create_result)); + + EXPECT_CALL(*second_create_result, ErrorInternal).Times(1); + EXPECT_CALL(*second_create_result, SuccessInternal).Times(0); + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(second_create_result)); +} + +TEST(LocalAuthPlugin, InitializeHandlerCallStartPreview) { + int64_t mock_local_auth_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kInitialize))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kInitialize), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, StartPreview()) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("initialize", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, InitializeHandlerErrorOnInvalidLocalAuthId) { + int64_t mock_local_auth_id = 1234; + int64_t missing_local_auth_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); + EXPECT_CALL(*local_auth, AddPendingResult).Times(0); + EXPECT_CALL(*local_auth, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, StartPreview).Times(0); + + local_auth->local_auth_id_ = mock_local_auth_id; + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("initialize", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, TakePictureHandlerCallsTakePictureWithPath) { + int64_t mock_local_auth_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kTakePicture))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kTakePicture), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, TakePicture(EndsWith(".jpeg"))) + .Times(1) + .WillOnce([cam = local_auth.get()](const std::string& file_path) { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("takePicture", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, TakePictureHandlerErrorOnInvalidLocalAuthId) { + int64_t mock_local_auth_id = 1234; + int64_t missing_local_auth_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); + EXPECT_CALL(*local_auth, AddPendingResult).Times(0); + EXPECT_CALL(*local_auth, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, TakePicture).Times(0); + + local_auth->local_auth_id_ = mock_local_auth_id; + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("takePicture", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { + int64_t mock_local_auth_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kStartRecord))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, StartRecord(EndsWith(".mp4"), -1)) + .Times(1) + .WillOnce([cam = local_auth.get()](const std::string& file_path, + int64_t max_video_duration_ms) { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("startVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, + StartVideoRecordingHandlerCallsStartRecordWithPathAndCaptureDuration) { + int64_t mock_local_auth_id = 1234; + int32_t mock_video_duration = 100000; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kStartRecord))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, + StartRecord(EndsWith(".mp4"), Eq(mock_video_duration))) + .Times(1) + .WillOnce([cam = local_auth.get()](const std::string& file_path, + int64_t max_video_duration_ms) { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + {EncodableValue("maxVideoDuration"), EncodableValue(mock_video_duration)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("startVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, StartVideoRecordingHandlerErrorOnInvalidLocalAuthId) { + int64_t mock_local_auth_id = 1234; + int64_t missing_local_auth_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); + EXPECT_CALL(*local_auth, AddPendingResult).Times(0); + EXPECT_CALL(*local_auth, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, StartRecord(_, -1)).Times(0); + + local_auth->local_auth_id_ = mock_local_auth_id; + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("startVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, StopVideoRecordingHandlerCallsStopRecord) { + int64_t mock_local_auth_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kStopRecord))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kStopRecord), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, StopRecord) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("stopVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, StopVideoRecordingHandlerErrorOnInvalidLocalAuthId) { + int64_t mock_local_auth_id = 1234; + int64_t missing_local_auth_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); + EXPECT_CALL(*local_auth, AddPendingResult).Times(0); + EXPECT_CALL(*local_auth, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, StopRecord).Times(0); + + local_auth->local_auth_id_ = mock_local_auth_id; + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("stopVideoRecording", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, ResumePreviewHandlerCallsResumePreview) { + int64_t mock_local_auth_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kResumePreview))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, + AddPendingResult(Eq(PendingResultType::kResumePreview), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, ResumePreview) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("resumePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, ResumePreviewHandlerErrorOnInvalidLocalAuthId) { + int64_t mock_local_auth_id = 1234; + int64_t missing_local_auth_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); + EXPECT_CALL(*local_auth, AddPendingResult).Times(0); + EXPECT_CALL(*local_auth, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, ResumePreview).Times(0); + + local_auth->local_auth_id_ = mock_local_auth_id; + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("resumePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, PausePreviewHandlerCallsPausePreview) { + int64_t mock_local_auth_id = 1234; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, + HasPendingResultByType(Eq(PendingResultType::kPausePreview))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*local_auth, + AddPendingResult(Eq(PendingResultType::kPausePreview), _)) + .Times(1) + .WillOnce([cam = local_auth.get()](PendingResultType type, + std::unique_ptr> result) { + cam->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*local_auth, GetCaptureController) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->capture_controller_.get(); + }); + + EXPECT_CALL(*capture_controller, PausePreview) + .Times(1) + .WillOnce([cam = local_auth.get()]() { + assert(cam->pending_result_); + return cam->pending_result_->Success(); + }); + + local_auth->local_auth_id_ = mock_local_auth_id; + local_auth->capture_controller_ = std::move(capture_controller); + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("pausePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +TEST(LocalAuthPlugin, PausePreviewHandlerErrorOnInvalidLocalAuthId) { + int64_t mock_local_auth_id = 1234; + int64_t missing_local_auth_id = 5678; + + std::unique_ptr initialize_result = + std::make_unique(); + + std::unique_ptr local_auth = + std::make_unique(MOCK_DEVICE_ID); + + std::unique_ptr capture_controller = + std::make_unique(); + + EXPECT_CALL(*local_auth, HasLocalAuthId) + .Times(1) + .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { + return cam->local_auth_id_ == local_auth_id; + }); + + EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); + EXPECT_CALL(*local_auth, AddPendingResult).Times(0); + EXPECT_CALL(*local_auth, GetCaptureController).Times(0); + EXPECT_CALL(*capture_controller, PausePreview).Times(0); + + local_auth->local_auth_id_ = mock_local_auth_id; + + MockLocalAuthPlugin plugin(std::make_unique().get(), + std::make_unique().get(), + std::make_unique()); + + // Add mocked local_auth to plugins local_auth list. + plugin.AddLocalAuth(std::move(local_auth)); + + EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); + EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + + EncodableMap args = { + {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("pausePreview", + std::make_unique(EncodableMap(args))), + std::move(initialize_result)); +} + +} // namespace test +} // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp new file mode 100644 index 000000000000..899c1fdaea62 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp @@ -0,0 +1,344 @@ +// 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 "camera.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" + +namespace camera_windows { +using ::testing::_; +using ::testing::Eq; +using ::testing::NiceMock; +using ::testing::Pointee; + +namespace test { + +TEST(Camera, InitCameraCreatesCaptureController) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller_factory = + std::make_unique(); + + EXPECT_CALL(*capture_controller_factory, CreateCaptureController) + .Times(1) + .WillOnce( + []() { return std::make_unique>(); }); + + EXPECT_TRUE(camera->GetCaptureController() == nullptr); + + // Init camera with mock capture controller factory + camera->InitCamera(std::move(capture_controller_factory), + std::make_unique().get(), + std::make_unique().get(), false, + ResolutionPreset::kAuto); + + EXPECT_TRUE(camera->GetCaptureController() != nullptr); +} + +TEST(Camera, AddPendingResultReturnsErrorForDuplicates) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr first_pending_result = + std::make_unique(); + std::unique_ptr second_pending_result = + std::make_unique(); + + EXPECT_CALL(*first_pending_result, ErrorInternal).Times(0); + EXPECT_CALL(*first_pending_result, SuccessInternal); + EXPECT_CALL(*second_pending_result, ErrorInternal).Times(1); + + camera->AddPendingResult(PendingResultType::kCreateCamera, + std::move(first_pending_result)); + + // This should fail + camera->AddPendingResult(PendingResultType::kCreateCamera, + std::move(second_pending_result)); + + // Mark pending result as succeeded + camera->OnCreateCaptureEngineSucceeded(0); +} + +TEST(Camera, OnCreateCaptureEngineSucceededReturnsCameraId) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const int64_t texture_id = 12345; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL( + *result, + SuccessInternal(Pointee(EncodableValue(EncodableMap( + {{EncodableValue("cameraId"), EncodableValue(texture_id)}}))))); + + camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + + camera->OnCreateCaptureEngineSucceeded(texture_id); +} + +TEST(Camera, OnCreateCaptureEngineFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + + camera->OnCreateCaptureEngineFailed(error_text); +} + +TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const int32_t width = 123; + const int32_t height = 456; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL( + *result, + SuccessInternal(Pointee(EncodableValue(EncodableMap({ + {EncodableValue("previewWidth"), EncodableValue((float)width)}, + {EncodableValue("previewHeight"), EncodableValue((float)height)}, + }))))); + + camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + + camera->OnStartPreviewSucceeded(width, height); +} + +TEST(Camera, OnStartPreviewFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + + camera->OnStartPreviewFailed(error_text); +} + +TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(nullptr)); + + camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + + camera->OnPausePreviewSucceeded(); +} + +TEST(Camera, OnPausePreviewFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + + camera->OnPausePreviewFailed(error_text); +} + +TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(nullptr)); + + camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result)); + + camera->OnResumePreviewSucceeded(); +} + +TEST(Camera, OnResumePreviewFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result)); + + camera->OnResumePreviewFailed(error_text); +} + +TEST(Camera, OnStartRecordSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(nullptr)); + + camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + + camera->OnStartRecordSucceeded(); +} + +TEST(Camera, OnStartRecordFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + + camera->OnStartRecordFailed(error_text); +} + +TEST(Camera, OnStopRecordSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string file_path = "C:\temp\filename.mp4"; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); + + camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + + camera->OnStopRecordSucceeded(file_path); +} + +TEST(Camera, OnStopRecordFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + + camera->OnStopRecordFailed(error_text); +} + +TEST(Camera, OnTakePictureSucceededReturnsSuccess) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string file_path = "C:\temp\filename.jpeg"; + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); + + camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + + camera->OnTakePictureSucceeded(file_path); +} + +TEST(Camera, OnTakePictureFailedReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + + camera->OnTakePictureFailed(error_text); +} + +TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller_factory = + std::make_unique(); + + std::unique_ptr binary_messenger = + std::make_unique(); + + std::string file_path = "C:\temp\filename.mp4"; + int64_t camera_id = 12345; + std::string camera_channel = + std::string("plugins.flutter.io/camera_windows/camera") + + std::to_string(camera_id); + int64_t video_duration = 1000000; + + EXPECT_CALL(*capture_controller_factory, CreateCaptureController) + .Times(1) + .WillOnce( + []() { return std::make_unique>(); }); + + // TODO: test binary content. + // First time is video record success message, + // and second is camera closing message. + EXPECT_CALL(*binary_messenger, Send(Eq(camera_channel), _, _, _)).Times(2); + + // Init camera with mock capture controller factory + camera->InitCamera(std::move(capture_controller_factory), + std::make_unique().get(), + binary_messenger.get(), false, ResolutionPreset::kAuto); + + // Pass camera id for camera + camera->OnCreateCaptureEngineSucceeded(camera_id); + + camera->OnVideoRecordSucceeded(file_path, video_duration); + + // Dispose camera before message channel. + camera = nullptr; +} + +} // namespace test +} // namespace camera_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/mocks.h b/packages/local_auth/local_auth_windows/windows/test/mocks.h new file mode 100644 index 000000000000..0781989e94c2 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/mocks.h @@ -0,0 +1,1015 @@ +// 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. + +#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "camera.h" +#include "camera_plugin.h" +#include "capture_controller.h" +#include "capture_controller_listener.h" +#include "capture_engine_listener.h" + +namespace camera_windows { +namespace test { + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; + +class MockMethodResult : public flutter::MethodResult<> { + public: + ~MockMethodResult() = default; + + MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), + (override)); + MOCK_METHOD(void, ErrorInternal, + (const std::string& error_code, const std::string& error_message, + const EncodableValue* details), + (override)); + MOCK_METHOD(void, NotImplementedInternal, (), (override)); +}; + +class MockBinaryMessenger : public flutter::BinaryMessenger { + public: + ~MockBinaryMessenger() = default; + + MOCK_METHOD(void, Send, + (const std::string& channel, const uint8_t* message, + size_t message_size, flutter::BinaryReply reply), + (const)); + + MOCK_METHOD(void, SetMessageHandler, + (const std::string& channel, + flutter::BinaryMessageHandler handler), + ()); +}; + +class MockTextureRegistrar : public flutter::TextureRegistrar { + public: + MockTextureRegistrar() { + ON_CALL(*this, RegisterTexture) + .WillByDefault([this](flutter::TextureVariant* texture) -> int64_t { + EXPECT_TRUE(texture); + this->texture_ = texture; + this->texture_id_ = 1000; + return this->texture_id_; + }); + + ON_CALL(*this, UnregisterTexture) + .WillByDefault([this](int64_t tid) -> bool { + if (tid == this->texture_id_) { + texture_ = nullptr; + this->texture_id_ = -1; + return true; + } + return false; + }); + + ON_CALL(*this, MarkTextureFrameAvailable) + .WillByDefault([this](int64_t tid) -> bool { + if (tid == this->texture_id_) { + return true; + } + return false; + }); + } + + ~MockTextureRegistrar() { texture_ = nullptr; } + + MOCK_METHOD(int64_t, RegisterTexture, (flutter::TextureVariant * texture), + (override)); + + MOCK_METHOD(bool, UnregisterTexture, (int64_t), (override)); + MOCK_METHOD(bool, MarkTextureFrameAvailable, (int64_t), (override)); + + int64_t texture_id_ = -1; + flutter::TextureVariant* texture_ = nullptr; +}; + +class MockCameraFactory : public CameraFactory { + public: + MockCameraFactory() { + ON_CALL(*this, CreateCamera).WillByDefault([this]() { + assert(this->pending_camera_); + return std::move(this->pending_camera_); + }); + } + + ~MockCameraFactory() = default; + + // Disallow copy and move. + MockCameraFactory(const MockCameraFactory&) = delete; + MockCameraFactory& operator=(const MockCameraFactory&) = delete; + + MOCK_METHOD(std::unique_ptr, CreateCamera, + (const std::string& device_id), (override)); + + std::unique_ptr pending_camera_; +}; + +class MockCamera : public Camera { + public: + MockCamera(const std::string& device_id) + : device_id_(device_id), Camera(device_id){}; + + ~MockCamera() = default; + + // Disallow copy and move. + MockCamera(const MockCamera&) = delete; + MockCamera& operator=(const MockCamera&) = delete; + + MOCK_METHOD(void, OnCreateCaptureEngineSucceeded, (int64_t texture_id), + (override)); + MOCK_METHOD(std::unique_ptr>, GetPendingResultByType, + (PendingResultType type)); + MOCK_METHOD(void, OnCreateCaptureEngineFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnStartPreviewSucceeded, (int32_t width, int32_t height), + (override)); + MOCK_METHOD(void, OnStartPreviewFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnResumePreviewSucceeded, (), (override)); + MOCK_METHOD(void, OnResumePreviewFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnPausePreviewSucceeded, (), (override)); + MOCK_METHOD(void, OnPausePreviewFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnStartRecordSucceeded, (), (override)); + MOCK_METHOD(void, OnStartRecordFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnStopRecordSucceeded, (const std::string& file_path), + (override)); + MOCK_METHOD(void, OnStopRecordFailed, (const std::string& error), (override)); + + MOCK_METHOD(void, OnTakePictureSucceeded, (const std::string& file_path), + (override)); + MOCK_METHOD(void, OnTakePictureFailed, (const std::string& error), + (override)); + + MOCK_METHOD(void, OnVideoRecordSucceeded, + (const std::string& file_path, int64_t video_duration), + (override)); + MOCK_METHOD(void, OnVideoRecordFailed, (const std::string& error), + (override)); + MOCK_METHOD(void, OnCaptureError, (const std::string& error), (override)); + + MOCK_METHOD(bool, HasDeviceId, (std::string & device_id), (const override)); + MOCK_METHOD(bool, HasCameraId, (int64_t camera_id), (const override)); + + MOCK_METHOD(bool, AddPendingResult, + (PendingResultType type, std::unique_ptr> result), + (override)); + MOCK_METHOD(bool, HasPendingResultByType, (PendingResultType type), + (const override)); + + MOCK_METHOD(camera_windows::CaptureController*, GetCaptureController, (), + (override)); + + MOCK_METHOD(void, InitCamera, + (flutter::TextureRegistrar * texture_registrar, + flutter::BinaryMessenger* messenger, bool record_audio, + ResolutionPreset resolution_preset), + (override)); + + std::unique_ptr capture_controller_; + std::unique_ptr> pending_result_; + std::string device_id_; + int64_t camera_id_ = -1; +}; + +class MockCaptureControllerFactory : public CaptureControllerFactory { + public: + MockCaptureControllerFactory(){}; + virtual ~MockCaptureControllerFactory() = default; + + // Disallow copy and move. + MockCaptureControllerFactory(const MockCaptureControllerFactory&) = delete; + MockCaptureControllerFactory& operator=(const MockCaptureControllerFactory&) = + delete; + + MOCK_METHOD(std::unique_ptr, CreateCaptureController, + (CaptureControllerListener * listener), (override)); +}; + +class MockCaptureController : public CaptureController { + public: + ~MockCaptureController() = default; + + MOCK_METHOD(void, InitCaptureDevice, + (flutter::TextureRegistrar * texture_registrar, + const std::string& device_id, bool record_audio, + ResolutionPreset resolution_preset), + (override)); + + MOCK_METHOD(uint32_t, GetPreviewWidth, (), (const override)); + MOCK_METHOD(uint32_t, GetPreviewHeight, (), (const override)); + + // Actions + MOCK_METHOD(void, StartPreview, (), (override)); + MOCK_METHOD(void, ResumePreview, (), (override)); + MOCK_METHOD(void, PausePreview, (), (override)); + MOCK_METHOD(void, StartRecord, + (const std::string& file_path, int64_t max_video_duration_ms), + (override)); + MOCK_METHOD(void, StopRecord, (), (override)); + MOCK_METHOD(void, TakePicture, (const std::string& file_path), (override)); +}; + +// MockCameraPlugin extends CameraPlugin behaviour a bit to allow adding cameras +// without creating them first with create message handler and mocking static +// system calls +class MockCameraPlugin : public CameraPlugin { + public: + MockCameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger) + : CameraPlugin(texture_registrar, messenger){}; + + // Creates a plugin instance with the given CameraFactory instance. + // Exists for unit testing with mock implementations. + MockCameraPlugin(flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + std::unique_ptr camera_factory) + : CameraPlugin(texture_registrar, messenger, std::move(camera_factory)){}; + + ~MockCameraPlugin() = default; + + // Disallow copy and move. + MockCameraPlugin(const MockCameraPlugin&) = delete; + MockCameraPlugin& operator=(const MockCameraPlugin&) = delete; + + MOCK_METHOD(bool, EnumerateVideoCaptureDeviceSources, + (IMFActivate * **devices, UINT32* count), (override)); + + // Helper to add camera without creating it via CameraFactory for testing + // purposes + void AddCamera(std::unique_ptr camera) { + cameras_.push_back(std::move(camera)); + } +}; + +class MockCaptureSource : public IMFCaptureSource { + public: + MockCaptureSource(){}; + ~MockCaptureSource() = default; + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureSource) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + MOCK_METHOD(HRESULT, GetCaptureDeviceSource, + (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType, + IMFMediaSource** ppMediaSource)); + MOCK_METHOD(HRESULT, GetCaptureDeviceActivate, + (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType, + IMFActivate** ppActivate)); + MOCK_METHOD(HRESULT, GetService, + (REFIID rguidService, REFIID riid, IUnknown** ppUnknown)); + MOCK_METHOD(HRESULT, AddEffect, + (DWORD dwSourceStreamIndex, IUnknown* pUnknown)); + + MOCK_METHOD(HRESULT, RemoveEffect, + (DWORD dwSourceStreamIndex, IUnknown* pUnknown)); + MOCK_METHOD(HRESULT, RemoveAllEffects, (DWORD dwSourceStreamIndex)); + MOCK_METHOD(HRESULT, GetAvailableDeviceMediaType, + (DWORD dwSourceStreamIndex, DWORD dwMediaTypeIndex, + IMFMediaType** ppMediaType)); + MOCK_METHOD(HRESULT, SetCurrentDeviceMediaType, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType)); + MOCK_METHOD(HRESULT, GetCurrentDeviceMediaType, + (DWORD dwSourceStreamIndex, IMFMediaType** ppMediaType)); + MOCK_METHOD(HRESULT, GetDeviceStreamCount, (DWORD * pdwStreamCount)); + MOCK_METHOD(HRESULT, GetDeviceStreamCategory, + (DWORD dwSourceStreamIndex, + MF_CAPTURE_ENGINE_STREAM_CATEGORY* pStreamCategory)); + MOCK_METHOD(HRESULT, GetMirrorState, + (DWORD dwStreamIndex, BOOL* pfMirrorState)); + MOCK_METHOD(HRESULT, SetMirrorState, + (DWORD dwStreamIndex, BOOL fMirrorState)); + MOCK_METHOD(HRESULT, GetStreamIndexFromFriendlyName, + (UINT32 uifriendlyName, DWORD* pdwActualStreamIndex)); + + private: + volatile ULONG ref_ = 0; +}; + +// Uses IMFMediaSourceEx which has SetD3DManager method. +class MockMediaSource : public IMFMediaSourceEx { + public: + MockMediaSource(){}; + ~MockMediaSource() = default; + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFMediaSource) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + // IMFMediaSource + HRESULT GetCharacteristics(DWORD* dwCharacteristics) override { + return E_NOTIMPL; + } + // IMFMediaSource + HRESULT CreatePresentationDescriptor( + IMFPresentationDescriptor** presentationDescriptor) override { + return E_NOTIMPL; + } + // IMFMediaSource + HRESULT Start(IMFPresentationDescriptor* presentationDescriptor, + const GUID* guidTimeFormat, + const PROPVARIANT* varStartPosition) override { + return E_NOTIMPL; + } + // IMFMediaSource + HRESULT Stop(void) override { return E_NOTIMPL; } + // IMFMediaSource + HRESULT Pause(void) override { return E_NOTIMPL; } + // IMFMediaSource + HRESULT Shutdown(void) override { return E_NOTIMPL; } + + // IMFMediaEventGenerator + HRESULT GetEvent(DWORD dwFlags, IMFMediaEvent** event) override { + return E_NOTIMPL; + } + // IMFMediaEventGenerator + HRESULT BeginGetEvent(IMFAsyncCallback* callback, + IUnknown* unkState) override { + return E_NOTIMPL; + } + // IMFMediaEventGenerator + HRESULT EndGetEvent(IMFAsyncResult* result, IMFMediaEvent** event) override { + return E_NOTIMPL; + } + // IMFMediaEventGenerator + HRESULT QueueEvent(MediaEventType met, REFGUID guidExtendedType, + HRESULT hrStatus, const PROPVARIANT* value) override { + return E_NOTIMPL; + } + + // IMFMediaSourceEx + HRESULT GetSourceAttributes(IMFAttributes** attributes) { return E_NOTIMPL; } + // IMFMediaSourceEx + HRESULT GetStreamAttributes(DWORD stream_id, IMFAttributes** attributes) { + return E_NOTIMPL; + } + // IMFMediaSourceEx + HRESULT SetD3DManager(IUnknown* manager) { return S_OK; } + + private: + volatile ULONG ref_ = 0; +}; + +class MockCapturePreviewSink : public IMFCapturePreviewSink { + public: + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetOutputMediaType, + (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetService, + (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, + IUnknown** ppUnknown)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, AddStream, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, + IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, Prepare, ()); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, RemoveAllStreams, ()); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetRenderHandle, (HANDLE handle)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetRenderSurface, (IUnknown * pSurface)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, UpdateVideo, + (const MFVideoNormalizedRect* pSrc, const RECT* pDst, + const COLORREF* pBorderClr)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetSampleCallback, + (DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, GetMirrorState, (BOOL * pfMirrorState)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetMirrorState, (BOOL fMirrorState)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, GetRotation, + (DWORD dwStreamIndex, DWORD* pdwRotationValue)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetRotation, + (DWORD dwStreamIndex, DWORD dwRotationValue)); + + // IMFCapturePreviewSink + MOCK_METHOD(HRESULT, SetCustomSink, (IMFMediaSink * pMediaSink)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCapturePreviewSink) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + void SendFakeSample(uint8_t* src_buffer, uint32_t size) { + assert(sample_callback_); + ComPtr sample; + ComPtr buffer; + HRESULT hr = MFCreateSample(&sample); + + if (SUCCEEDED(hr)) { + hr = MFCreateMemoryBuffer(size, &buffer); + } + + if (SUCCEEDED(hr)) { + uint8_t* target_data; + if (SUCCEEDED(buffer->Lock(&target_data, nullptr, nullptr))) { + std::copy(src_buffer, src_buffer + size, target_data); + } + hr = buffer->Unlock(); + } + + if (SUCCEEDED(hr)) { + hr = buffer->SetCurrentLength(size); + } + + if (SUCCEEDED(hr)) { + hr = sample->AddBuffer(buffer.Get()); + } + + if (SUCCEEDED(hr)) { + sample_callback_->OnSample(sample.Get()); + } + } + + ComPtr sample_callback_; + + private: + ~MockCapturePreviewSink() = default; + volatile ULONG ref_ = 0; +}; + +class MockCaptureRecordSink : public IMFCaptureRecordSink { + public: + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetOutputMediaType, + (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetService, + (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, + IUnknown** ppUnknown)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, AddStream, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, + IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, Prepare, ()); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, RemoveAllStreams, ()); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetOutputByteStream, + (IMFByteStream * pByteStream, REFGUID guidContainerType)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetOutputFileName, (LPCWSTR fileName)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetSampleCallback, + (DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetCustomSink, (IMFMediaSink * pMediaSink)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, GetRotation, + (DWORD dwStreamIndex, DWORD* pdwRotationValue)); + + // IMFCaptureRecordSink + MOCK_METHOD(HRESULT, SetRotation, + (DWORD dwStreamIndex, DWORD dwRotationValue)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureRecordSink) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + private: + ~MockCaptureRecordSink() = default; + volatile ULONG ref_ = 0; +}; + +class MockCapturePhotoSink : public IMFCapturePhotoSink { + public: + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetOutputMediaType, + (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, GetService, + (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, + IUnknown** ppUnknown)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, AddStream, + (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, + IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, Prepare, ()); + + // IMFCaptureSink + MOCK_METHOD(HRESULT, RemoveAllStreams, ()); + + // IMFCapturePhotoSink + MOCK_METHOD(HRESULT, SetOutputFileName, (LPCWSTR fileName)); + + // IMFCapturePhotoSink + MOCK_METHOD(HRESULT, SetSampleCallback, + (IMFCaptureEngineOnSampleCallback * pCallback)); + + // IMFCapturePhotoSink + MOCK_METHOD(HRESULT, SetOutputByteStream, (IMFByteStream * pByteStream)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCapturePhotoSink) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + private: + ~MockCapturePhotoSink() = default; + volatile ULONG ref_ = 0; +}; + +template +class FakeIMFAttributesBase : public T { + static_assert(std::is_base_of::value, + "I must inherit from IMFAttributes"); + + // IIMFAttributes + HRESULT GetItem(REFGUID guidKey, PROPVARIANT* pValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT CompareItem(REFGUID guidKey, REFPROPVARIANT Value, + BOOL* pbResult) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT Compare(IMFAttributes* pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, + BOOL* pbResult) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetUINT32(REFGUID guidKey, UINT32* punValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetUINT64(REFGUID guidKey, UINT64* punValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetDouble(REFGUID guidKey, double* pfValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetGUID(REFGUID guidKey, GUID* pguidValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetStringLength(REFGUID guidKey, UINT32* pcchLength) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, + UINT32* pcchLength) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, + UINT32* pcchLength) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, + UINT32* pcbBlobSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, + UINT32* pcbSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT GetUnknown(REFGUID guidKey, REFIID riid, + __RPC__deref_out_opt LPVOID* ppv) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetItem(REFGUID guidKey, REFPROPVARIANT Value) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT DeleteItem(REFGUID guidKey) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT DeleteAllItems(void) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT SetUINT32(REFGUID guidKey, UINT32 unValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetUINT64(REFGUID guidKey, UINT64 unValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetDouble(REFGUID guidKey, double fValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetGUID(REFGUID guidKey, REFGUID guidValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetString(REFGUID guidKey, LPCWSTR wszValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetBlob(REFGUID guidKey, const UINT8* pBuf, + UINT32 cbBufSize) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT SetUnknown(REFGUID guidKey, IUnknown* pUnknown) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT LockStore(void) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT UnlockStore(void) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT GetCount(UINT32* pcItems) override { return E_NOTIMPL; } + + // IIMFAttributes + HRESULT GetItemByIndex(UINT32 unIndex, GUID* pguidKey, + PROPVARIANT* pValue) override { + return E_NOTIMPL; + } + + // IIMFAttributes + HRESULT CopyAllItems(IMFAttributes* pDest) override { return E_NOTIMPL; } +}; + +class FakeMediaType : public FakeIMFAttributesBase { + public: + FakeMediaType(GUID major_type, GUID sub_type, int width, int height) + : major_type_(major_type), + sub_type_(sub_type), + width_(width), + height_(height){}; + + // IMFAttributes + HRESULT GetUINT64(REFGUID key, UINT64* value) override { + if (key == MF_MT_FRAME_SIZE) { + *value = (int64_t)width_ << 32 | (int64_t)height_; + return S_OK; + } else if (key == MF_MT_FRAME_RATE) { + *value = (int64_t)frame_rate_ << 32 | 1; + return S_OK; + } + return E_FAIL; + }; + + // IMFAttributes + HRESULT GetGUID(REFGUID key, GUID* value) override { + if (key == MF_MT_MAJOR_TYPE) { + *value = major_type_; + return S_OK; + } else if (key == MF_MT_SUBTYPE) { + *value = sub_type_; + return S_OK; + } + return E_FAIL; + } + + // IIMFAttributes + HRESULT CopyAllItems(IMFAttributes* pDest) override { + pDest->SetUINT64(MF_MT_FRAME_SIZE, + (int64_t)width_ << 32 | (int64_t)height_); + pDest->SetUINT64(MF_MT_FRAME_RATE, (int64_t)frame_rate_ << 32 | 1); + pDest->SetGUID(MF_MT_MAJOR_TYPE, major_type_); + pDest->SetGUID(MF_MT_SUBTYPE, sub_type_); + return S_OK; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE GetMajorType(GUID* pguidMajorType) override { + return E_NOTIMPL; + }; + + // IMFMediaType + HRESULT STDMETHODCALLTYPE IsCompressedFormat(BOOL* pfCompressed) override { + return E_NOTIMPL; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE IsEqual(IMFMediaType* pIMediaType, + DWORD* pdwFlags) override { + return E_NOTIMPL; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE GetRepresentation( + GUID guidRepresentation, LPVOID* ppvRepresentation) override { + return E_NOTIMPL; + } + + // IMFMediaType + HRESULT STDMETHODCALLTYPE FreeRepresentation( + GUID guidRepresentation, LPVOID pvRepresentation) override { + return E_NOTIMPL; + } + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFMediaType) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + private: + ~FakeMediaType() = default; + volatile ULONG ref_ = 0; + const GUID major_type_; + const GUID sub_type_; + const int width_; + const int height_; + const int frame_rate_ = 30; +}; + +class MockCaptureEngine : public IMFCaptureEngine { + public: + MockCaptureEngine() { + ON_CALL(*this, Initialize) + .WillByDefault([this](IMFCaptureEngineOnEventCallback* callback, + IMFAttributes* attributes, IUnknown* audioSource, + IUnknown* videoSource) -> HRESULT { + EXPECT_TRUE(callback); + EXPECT_TRUE(attributes); + EXPECT_TRUE(videoSource); + // audioSource is allowed to be nullptr; + callback_ = callback; + videoSource_ = reinterpret_cast(videoSource); + audioSource_ = reinterpret_cast(audioSource); + initialized_ = true; + return S_OK; + }); + }; + + virtual ~MockCaptureEngine() = default; + + MOCK_METHOD(HRESULT, Initialize, + (IMFCaptureEngineOnEventCallback * callback, + IMFAttributes* attributes, IUnknown* audioSource, + IUnknown* videoSource)); + MOCK_METHOD(HRESULT, StartPreview, ()); + MOCK_METHOD(HRESULT, StopPreview, ()); + MOCK_METHOD(HRESULT, StartRecord, ()); + MOCK_METHOD(HRESULT, StopRecord, + (BOOL finalize, BOOL flushUnprocessedSamples)); + MOCK_METHOD(HRESULT, TakePhoto, ()); + MOCK_METHOD(HRESULT, GetSink, + (MF_CAPTURE_ENGINE_SINK_TYPE type, IMFCaptureSink** sink)); + MOCK_METHOD(HRESULT, GetSource, (IMFCaptureSource * *ppSource)); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } + + // IUnknown + STDMETHODIMP_(ULONG) Release() { + LONG ref = InterlockedDecrement(&ref_); + if (ref == 0) { + delete this; + } + return ref; + } + + // IUnknown + STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { + *ppv = nullptr; + + if (riid == IID_IMFCaptureEngine) { + *ppv = static_cast(this); + ((IUnknown*)*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + void CreateFakeEvent(HRESULT hrStatus, GUID event_type) { + EXPECT_TRUE(initialized_); + ComPtr event; + MFCreateMediaEvent(MEExtendedType, event_type, hrStatus, nullptr, &event); + if (callback_) { + callback_->OnEvent(event.Get()); + } + } + + ComPtr callback_; + ComPtr videoSource_; + ComPtr audioSource_; + volatile ULONG ref_ = 0; + bool initialized_ = false; +}; + +#define MOCK_DEVICE_ID "mock_device_id" +#define MOCK_CAMERA_NAME "mock_camera_name <" MOCK_DEVICE_ID ">" +#define MOCK_INVALID_CAMERA_NAME "invalid_camera_name" + +} // namespace +} // namespace test +} // namespace camera_windows + +#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ From 10326934c41ac3652e716bbf836012430223eaf3 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Mon, 18 Apr 2022 11:40:34 -0700 Subject: [PATCH 09/20] PR Feedback. --- .../local_auth_windows/example/lib/main.dart | 15 ++++++++----- .../lib/types/auth_messages_windows.dart | 22 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart index f799c2cd20cf..d30982bc1cc2 100644 --- a/packages/local_auth/local_auth_windows/example/lib/main.dart +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -30,11 +30,12 @@ class _MyAppState extends State { @override void initState() { super.initState(); - LocalAuthPlatform.instance.isDeviceSupported().then( - (bool isSupported) => setState(() => _supportState = isSupported - ? _SupportState.supported - : _SupportState.unsupported), - ); + LocalAuthPlatform.instance.isDeviceSupported().then((bool isSupported) { + setState(() { + _supportState = + isSupported ? _SupportState.supported : _SupportState.unsupported; + }); + }); } Future _checkBiometrics() async { @@ -104,7 +105,9 @@ class _MyAppState extends State { } setState( - () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + () { + _authorized = authenticated ? 'Authorized' : 'Not Authorized'; + }); } Future _authenticateWithBiometrics() async { diff --git a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart index 23ad024fb5de..cd2df3fdd254 100644 --- a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart +++ b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart @@ -110,17 +110,17 @@ class WindowsAuthMessages extends AuthMessages { signInTitle == other.signInTitle; @override - int get hashCode => - biometricHint.hashCode ^ - biometricNotRecognized.hashCode ^ - biometricRequiredTitle.hashCode ^ - biometricSuccess.hashCode ^ - cancelButton.hashCode ^ - deviceCredentialsRequiredTitle.hashCode ^ - deviceCredentialsSetupDescription.hashCode ^ - goToSettingsButton.hashCode ^ - goToSettingsDescription.hashCode ^ - signInTitle.hashCode; + int get hashCode => Object.hash( + biometricHint, + biometricNotRecognized, + biometricRequiredTitle, + biometricSuccess, + cancelButton, + deviceCredentialsRequiredTitle, + deviceCredentialsSetupDescription, + goToSettingsButton, + goToSettingsDescription, + signInTitle); } // Default strings for WindowsAuthMessages. Currently supports English. From 8c7a87bf43a43790040a8e282de559075a190de8 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Mon, 18 Apr 2022 13:51:47 -0700 Subject: [PATCH 10/20] Fixed tests. --- .../local_auth_windows/example/lib/main.dart | 7 +- .../example/windows/CMakeLists.txt | 2 +- .../local_auth_windows/windows/CMakeLists.txt | 11 +- .../local_auth_windows/windows/local_auth.h | 72 ++ .../windows/local_auth_plugin.cpp | 163 +-- .../windows/local_auth_windows.cpp | 15 + .../windows/test/local_auth_plugin_test.cpp | 973 +---------------- .../windows/test/local_auth_test.cpp | 344 ------ .../local_auth_windows/windows/test/mocks.h | 992 +----------------- 9 files changed, 217 insertions(+), 2362 deletions(-) create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth.h create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp delete mode 100644 packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart index d30982bc1cc2..22e9eaceaf65 100644 --- a/packages/local_auth/local_auth_windows/example/lib/main.dart +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -104,10 +104,9 @@ class _MyAppState extends State { return; } - setState( - () { - _authorized = authenticated ? 'Authorized' : 'Not Authorized'; - }); + setState(() { + _authorized = authenticated ? 'Authorized' : 'Not Authorized'; + }); } Future _authenticateWithBiometrics() async { diff --git a/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt index 6fd0f75e569e..2163be881bd2 100644 --- a/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt +++ b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt @@ -47,7 +47,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) add_subdirectory("runner") # Enable the test target. -set(local_auth_windows_tests TRUE) +set(include_local_auth_windows_tests TRUE) # Provide an alias for the test target using the name expected by repo tooling. add_custom_target(unit_tests DEPENDS local_auth_windows_test) diff --git a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt index 3ef03f5d5e99..173243ee0368 100644 --- a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt +++ b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt @@ -38,9 +38,14 @@ endif() include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) +list(APPEND PLUGIN_SOURCES + "local_auth_plugin.cpp" +) + add_library(${PLUGIN_NAME} SHARED "include/local_auth_windows/local_auth_plugin.h" - "local_auth_plugin.cpp" + "local_auth_windows.cpp" + "local_auth.h" ${PLUGIN_SOURCES} ) apply_standard_settings(${PLUGIN_NAME}) @@ -67,7 +72,6 @@ enable_testing() # TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest # instance rather than downloading for each plugin. This approach makes sense # for a template, but not for a monorepo with many plugins. -include(FetchContent) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/release-1.11.0.zip @@ -84,11 +88,12 @@ FetchContent_MakeAvailable(googletest) add_executable(${TEST_RUNNER} test/mocks.h test/local_auth_plugin_test.cpp - test/local_auth_test.cpp ${PLUGIN_SOURCES} ) apply_standard_settings(${TEST_RUNNER}) target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_compile_features(${TEST_RUNNER} PRIVATE cxx_std_20) +target_compile_options(${TEST_RUNNER} PRIVATE /await) target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) target_link_libraries(${TEST_RUNNER} PRIVATE windowsapp) target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h new file mode 100644 index 000000000000..8d8cb695e48d --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -0,0 +1,72 @@ +// 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 + +#include "include/local_auth_windows/local_auth_plugin.h" + +// This must be included before many other Windows headers. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace local_auth_windows { + +using namespace flutter; +using namespace winrt; + +class UserConsentVerifier { + public: + UserConsentVerifier() {} + virtual ~UserConsentVerifier() = default; + + virtual Windows::Foundation::IAsyncOperation< + Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localizedReason) = 0; + virtual Windows::Foundation::IAsyncOperation< + Windows::Security::Credentials::UI::UserConsentVerifierAvailability> + CheckAvailabilityAsync() = 0; + + // Disallow copy and move. + UserConsentVerifier(const UserConsentVerifier&) = delete; + UserConsentVerifier& operator=(const UserConsentVerifier&) = delete; +}; + +class LocalAuthPlugin : public Plugin { + public: + static void RegisterWithRegistrar(PluginRegistrarWindows* registrar); + + LocalAuthPlugin(std::function window_provider); + LocalAuthPlugin(std::unique_ptr user_consent_verifier); + + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall(const MethodCall& method_call, + std::unique_ptr> result); + + virtual ~LocalAuthPlugin(); + + private: + std::unique_ptr user_consent_verifier_; + + winrt::fire_and_forget Authenticate( + const MethodCall& method_call, + std::unique_ptr> result); + winrt::fire_and_forget GetAvailableBiometrics( + std::unique_ptr> result); + winrt::fire_and_forget IsDeviceSupported( + std::unique_ptr> result); +}; + +} // namespace local_auth_windows \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp index c00959a10efc..8fd2509b370a 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -1,26 +1,9 @@ // 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 "include/local_auth_windows/local_auth_plugin.h" - -// This must be included before many other Windows headers. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace { +#include "local_auth.h" + +namespace local_auth_windows { using namespace flutter; using namespace winrt; @@ -45,37 +28,77 @@ HWND GetRootWindow(flutter::FlutterView* view) { } // Converts the given UTF-8 string to UTF-16. -std::wstring s2ws(const std::string& s) { - int len; - int slength = (int)s.length() + 1; - len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); - std::wstring r(len, L'\0'); - MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, &r[0], len); - return r; +std::wstring Utf16FromUtf8(const std::string& utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), nullptr, 0); + if (target_length == 0) { + return std::wstring(); + } + std::wstring utf16_string; + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), + utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; } -class LocalAuthPlugin : public Plugin { +class UserConsentVerifierImpl : public UserConsentVerifier { public: - static void RegisterWithRegistrar(PluginRegistrarWindows* registrar); + explicit UserConsentVerifierImpl(std::function window_provider) + : get_root_window_(std::move(window_provider)){}; + virtual ~UserConsentVerifierImpl() = default; + + Windows::Foundation::IAsyncOperation< + Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localizedReason) override { + auto userConsentVerifierInterop = winrt::get_activation_factory< + Windows::Security::Credentials::UI::UserConsentVerifier, + IUserConsentVerifierInterop>(); + + auto hWnd = get_root_window_(); + + HSTRING hReason; + if (WindowsCreateString(localizedReason.c_str(), + static_cast(localizedReason.size()), + &hReason) != S_OK) { + return Windows::Security::Credentials::UI::UserConsentVerificationResult:: + Canceled; + } + + auto consentResult = + co_await winrt::capture>( + userConsentVerifierInterop, + &::IUserConsentVerifierInterop::RequestVerificationForWindowAsync, + hWnd, hReason); - LocalAuthPlugin(flutter::PluginRegistrarWindows* registrar); + WindowsDeleteString(hReason); - virtual ~LocalAuthPlugin(); + return consentResult; + } + + Windows::Foundation::IAsyncOperation< + Windows::Security::Credentials::UI::UserConsentVerifierAvailability> + CheckAvailabilityAsync() override { + return Windows::Security::Credentials::UI::UserConsentVerifier:: + CheckAvailabilityAsync(); + } + + // Disallow copy and move. + UserConsentVerifierImpl(const UserConsentVerifierImpl&) = delete; + UserConsentVerifierImpl& operator=(const UserConsentVerifierImpl&) = delete; private: - flutter::PluginRegistrarWindows* registrar_; - - // Called when a method is called on this plugin's channel from Dart. - void HandleMethodCall(const MethodCall& method_call, - std::unique_ptr> result); - - winrt::fire_and_forget Authenticate( - const MethodCall& method_call, - std::unique_ptr> result); - winrt::fire_and_forget GetAvailableBiometrics( - std::unique_ptr> result); - winrt::fire_and_forget IsDeviceSupported( - std::unique_ptr> result); + // The provider for the root window to attach the dialog to. + std::function get_root_window_; }; // static @@ -84,7 +107,8 @@ void LocalAuthPlugin::RegisterWithRegistrar(PluginRegistrarWindows* registrar) { registrar->messenger(), "plugins.flutter.io/local_auth_windows", &StandardMethodCodec::GetInstance()); - auto plugin = std::make_unique(registrar); + auto plugin = std::make_unique( + [registrar]() { return GetRootWindow(registrar->GetView()); }); channel->SetMethodCallHandler( [plugin_pointer = plugin.get()](const auto& call, auto result) { @@ -95,8 +119,13 @@ void LocalAuthPlugin::RegisterWithRegistrar(PluginRegistrarWindows* registrar) { } // Default constructor for LocalAuthPlugin -LocalAuthPlugin::LocalAuthPlugin(flutter::PluginRegistrarWindows* registrar) - : registrar_(registrar) {} +LocalAuthPlugin::LocalAuthPlugin(std::function window_provider) + : user_consent_verifier_(std::make_unique( + std::move(window_provider))) {} + +LocalAuthPlugin::LocalAuthPlugin( + std::unique_ptr user_consent_verifier) + : user_consent_verifier_(std::move(user_consent_verifier)) {} LocalAuthPlugin::~LocalAuthPlugin() {} @@ -118,7 +147,7 @@ void LocalAuthPlugin::HandleMethodCall( winrt::fire_and_forget LocalAuthPlugin::Authenticate( const MethodCall& method_call, std::unique_ptr> result) { - auto reasonW = s2ws(GetArgument( + auto reasonW = Utf16FromUtf8(GetArgument( "localizedReason", method_call.arguments(), std::string())); auto biometricOnly = @@ -129,8 +158,8 @@ winrt::fire_and_forget LocalAuthPlugin::Authenticate( co_return; } - auto ucvAvailability = co_await Windows::Security::Credentials::UI:: - UserConsentVerifier::CheckAvailabilityAsync(); + auto ucvAvailability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); if (ucvAvailability == Windows::Security::Credentials::UI::UserConsentVerifierAvailability:: @@ -149,22 +178,10 @@ winrt::fire_and_forget LocalAuthPlugin::Authenticate( co_return; } - auto userConsentVerifierInterop = winrt::get_activation_factory< - Windows::Security::Credentials::UI::UserConsentVerifier, - IUserConsentVerifierInterop>(); - - auto hWnd = GetRootWindow(registrar_->GetView()); - - HSTRING hReason; - WindowsCreateString(reasonW.c_str(), (uint32_t)reasonW.size(), &hReason); - try { auto consentResult = - co_await winrt::capture>( - userConsentVerifierInterop, - &::IUserConsentVerifierInterop::RequestVerificationForWindowAsync, - hWnd, hReason); + co_await user_consent_verifier_->RequestVerificationForWindowAsync( + reasonW); result->Success(EncodableValue( consentResult == Windows::Security::Credentials::UI:: @@ -172,7 +189,6 @@ winrt::fire_and_forget LocalAuthPlugin::Authenticate( } catch (...) { result->Success(EncodableValue(false)); } - WindowsDeleteString(hReason); } // Returns biometric types available on device @@ -180,8 +196,8 @@ winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( std::unique_ptr> result) { try { flutter::EncodableList biometrics; - auto ucvAvailability = co_await Windows::Security::Credentials::UI:: - UserConsentVerifier::CheckAvailabilityAsync(); + auto ucvAvailability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); if (ucvAvailability == Windows::Security::Credentials::UI:: UserConsentVerifierAvailability::Available) { biometrics.push_back(EncodableValue("fingerprint")); @@ -197,18 +213,11 @@ winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( // Returns whether the device supports Windows Hello or not winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( std::unique_ptr> result) { - auto ucvAvailability = co_await Windows::Security::Credentials::UI:: - UserConsentVerifier::CheckAvailabilityAsync(); + auto ucvAvailability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); result->Success(EncodableValue( ucvAvailability == Windows::Security::Credentials::UI:: UserConsentVerifierAvailability::Available)); } -} // namespace - -void LocalAuthPluginRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar) { - LocalAuthPlugin::RegisterWithRegistrar( - PluginRegistrarManager::GetInstance() - ->GetRegistrar(registrar)); -} +} // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp new file mode 100644 index 000000000000..6e5e6a186afb --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp @@ -0,0 +1,15 @@ +// 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 + +#include "include/local_auth_windows/local_auth_plugin.h" +#include "local_auth.h" + +void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + local_auth_windows::LocalAuthPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp index 76a68f7569bb..512a61f2d503 100644 --- a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "local_auth_plugin.h" +#include "include/local_auth_windows/local_auth_plugin.h" #include #include @@ -31,980 +31,31 @@ using ::testing::Pointee; using ::testing::Return; TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerSuccessIfNoLocalAuths) { - std::unique_ptr texture_registrar_ = - std::make_unique(); - std::unique_ptr messenger_ = - std::make_unique(); - std::unique_ptr local_auth_factory_ = - std::make_unique(); std::unique_ptr result = std::make_unique(); - MockLocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), - std::move(local_auth_factory_)); + std::unique_ptr mockConsentVerifier = + std::make_unique(); - EXPECT_CALL(plugin, EnumerateVideoCaptureDeviceSources) + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([](IMFActivate*** devices, UINT32* count) { - *count = 0U; - *devices = static_cast( - CoTaskMemAlloc(sizeof(IMFActivate*) * (*count))); - return true; + .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; }); + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + EXPECT_CALL(*result, ErrorInternal).Times(0); EXPECT_CALL(*result, SuccessInternal).Times(1); plugin.HandleMethodCall( - flutter::MethodCall("availableLocalAuths", + flutter::MethodCall("isDeviceSupported", std::make_unique()), std::move(result)); } -TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerErrorIfFailsToEnumerateDevices) { - std::unique_ptr texture_registrar_ = - std::make_unique(); - std::unique_ptr messenger_ = - std::make_unique(); - std::unique_ptr local_auth_factory_ = - std::make_unique(); - std::unique_ptr result = - std::make_unique(); - - MockLocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), - std::move(local_auth_factory_)); - - EXPECT_CALL(plugin, EnumerateVideoCaptureDeviceSources) - .Times(1) - .WillOnce([](IMFActivate*** devices, UINT32* count) { return false; }); - - EXPECT_CALL(*result, ErrorInternal).Times(1); - EXPECT_CALL(*result, SuccessInternal).Times(0); - - plugin.HandleMethodCall( - flutter::MethodCall("availableLocalAuths", - std::make_unique()), - std::move(result)); -} - -TEST(LocalAuthPlugin, CreateHandlerCallsInitLocalAuth) { - std::unique_ptr result = - std::make_unique(); - std::unique_ptr texture_registrar_ = - std::make_unique(); - std::unique_ptr messenger_ = - std::make_unique(); - std::unique_ptr local_auth_factory_ = - std::make_unique(); - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kCreateLocalAuth))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, - AddPendingResult(Eq(PendingResultType::kCreateLocalAuth), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - EXPECT_CALL(*local_auth, InitLocalAuth) - .Times(1) - .WillOnce([cam = local_auth.get()]( - flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, bool record_audio, - ResolutionPreset resolution_preset) { - assert(cam->pending_result_); - return cam->pending_result_->Success(EncodableValue(1)); - }); - - // Move mocked local_auth to the factory to be passed - // for plugin with CreateLocalAuth function. - local_auth_factory_->pending_local_auth_ = std::move(local_auth); - - EXPECT_CALL(*local_auth_factory_, CreateLocalAuth(MOCK_DEVICE_ID)); - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(1)))); - - LocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), - std::move(local_auth_factory_)); - EncodableMap args = { - {EncodableValue("local_authName"), EncodableValue(MOCK_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(result)); -} - -TEST(LocalAuthPlugin, CreateHandlerErrorOnInvalidDeviceId) { - std::unique_ptr result = - std::make_unique(); - std::unique_ptr texture_registrar_ = - std::make_unique(); - std::unique_ptr messenger_ = - std::make_unique(); - std::unique_ptr local_auth_factory_ = - std::make_unique(); - - LocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), - std::move(local_auth_factory_)); - EncodableMap args = { - {EncodableValue("local_authName"), EncodableValue(MOCK_INVALID_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - EXPECT_CALL(*result, ErrorInternal).Times(1); - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(result)); -} - -TEST(LocalAuthPlugin, CreateHandlerErrorOnExistingDeviceId) { - std::unique_ptr first_create_result = - std::make_unique(); - std::unique_ptr second_create_result = - std::make_unique(); - std::unique_ptr texture_registrar_ = - std::make_unique(); - std::unique_ptr messenger_ = - std::make_unique(); - std::unique_ptr local_auth_factory_ = - std::make_unique(); - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kCreateLocalAuth))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, - AddPendingResult(Eq(PendingResultType::kCreateLocalAuth), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - EXPECT_CALL(*local_auth, InitLocalAuth) - .Times(1) - .WillOnce([cam = local_auth.get()]( - flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, bool record_audio, - ResolutionPreset resolution_preset) { - assert(cam->pending_result_); - return cam->pending_result_->Success(EncodableValue(1)); - }); - - EXPECT_CALL(*local_auth, HasDeviceId(Eq(MOCK_DEVICE_ID))) - .Times(1) - .WillOnce([cam = local_auth.get()](std::string& device_id) { - return cam->device_id_ == device_id; - }); - - // Move mocked local_auth to the factory to be passed - // for plugin with CreateLocalAuth function. - local_auth_factory_->pending_local_auth_ = std::move(local_auth); - - EXPECT_CALL(*local_auth_factory_, CreateLocalAuth(MOCK_DEVICE_ID)); - - EXPECT_CALL(*first_create_result, ErrorInternal).Times(0); - EXPECT_CALL(*first_create_result, - SuccessInternal(Pointee(EncodableValue(1)))); - - LocalAuthPlugin plugin(texture_registrar_.get(), messenger_.get(), - std::move(local_auth_factory_)); - EncodableMap args = { - {EncodableValue("local_authName"), EncodableValue(MOCK_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(first_create_result)); - - EXPECT_CALL(*second_create_result, ErrorInternal).Times(1); - EXPECT_CALL(*second_create_result, SuccessInternal).Times(0); - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(second_create_result)); -} - -TEST(LocalAuthPlugin, InitializeHandlerCallStartPreview) { - int64_t mock_local_auth_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kInitialize))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kInitialize), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, StartPreview()) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("initialize", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, InitializeHandlerErrorOnInvalidLocalAuthId) { - int64_t mock_local_auth_id = 1234; - int64_t missing_local_auth_id = 5678; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); - EXPECT_CALL(*local_auth, AddPendingResult).Times(0); - EXPECT_CALL(*local_auth, GetCaptureController).Times(0); - EXPECT_CALL(*capture_controller, StartPreview).Times(0); - - local_auth->local_auth_id_ = mock_local_auth_id; - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("initialize", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, TakePictureHandlerCallsTakePictureWithPath) { - int64_t mock_local_auth_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kTakePicture))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kTakePicture), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, TakePicture(EndsWith(".jpeg"))) - .Times(1) - .WillOnce([cam = local_auth.get()](const std::string& file_path) { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("takePicture", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, TakePictureHandlerErrorOnInvalidLocalAuthId) { - int64_t mock_local_auth_id = 1234; - int64_t missing_local_auth_id = 5678; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); - EXPECT_CALL(*local_auth, AddPendingResult).Times(0); - EXPECT_CALL(*local_auth, GetCaptureController).Times(0); - EXPECT_CALL(*capture_controller, TakePicture).Times(0); - - local_auth->local_auth_id_ = mock_local_auth_id; - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("takePicture", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { - int64_t mock_local_auth_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kStartRecord))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, StartRecord(EndsWith(".mp4"), -1)) - .Times(1) - .WillOnce([cam = local_auth.get()](const std::string& file_path, - int64_t max_video_duration_ms) { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("startVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, - StartVideoRecordingHandlerCallsStartRecordWithPathAndCaptureDuration) { - int64_t mock_local_auth_id = 1234; - int32_t mock_video_duration = 100000; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kStartRecord))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, - StartRecord(EndsWith(".mp4"), Eq(mock_video_duration))) - .Times(1) - .WillOnce([cam = local_auth.get()](const std::string& file_path, - int64_t max_video_duration_ms) { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - {EncodableValue("maxVideoDuration"), EncodableValue(mock_video_duration)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("startVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, StartVideoRecordingHandlerErrorOnInvalidLocalAuthId) { - int64_t mock_local_auth_id = 1234; - int64_t missing_local_auth_id = 5678; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); - EXPECT_CALL(*local_auth, AddPendingResult).Times(0); - EXPECT_CALL(*local_auth, GetCaptureController).Times(0); - EXPECT_CALL(*capture_controller, StartRecord(_, -1)).Times(0); - - local_auth->local_auth_id_ = mock_local_auth_id; - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("startVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, StopVideoRecordingHandlerCallsStopRecord) { - int64_t mock_local_auth_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kStopRecord))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, AddPendingResult(Eq(PendingResultType::kStopRecord), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, StopRecord) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("stopVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, StopVideoRecordingHandlerErrorOnInvalidLocalAuthId) { - int64_t mock_local_auth_id = 1234; - int64_t missing_local_auth_id = 5678; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); - EXPECT_CALL(*local_auth, AddPendingResult).Times(0); - EXPECT_CALL(*local_auth, GetCaptureController).Times(0); - EXPECT_CALL(*capture_controller, StopRecord).Times(0); - - local_auth->local_auth_id_ = mock_local_auth_id; - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("stopVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, ResumePreviewHandlerCallsResumePreview) { - int64_t mock_local_auth_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kResumePreview))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, - AddPendingResult(Eq(PendingResultType::kResumePreview), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, ResumePreview) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("resumePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, ResumePreviewHandlerErrorOnInvalidLocalAuthId) { - int64_t mock_local_auth_id = 1234; - int64_t missing_local_auth_id = 5678; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); - EXPECT_CALL(*local_auth, AddPendingResult).Times(0); - EXPECT_CALL(*local_auth, GetCaptureController).Times(0); - EXPECT_CALL(*capture_controller, ResumePreview).Times(0); - - local_auth->local_auth_id_ = mock_local_auth_id; - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("resumePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, PausePreviewHandlerCallsPausePreview) { - int64_t mock_local_auth_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId(Eq(mock_local_auth_id))) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, - HasPendingResultByType(Eq(PendingResultType::kPausePreview))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*local_auth, - AddPendingResult(Eq(PendingResultType::kPausePreview), _)) - .Times(1) - .WillOnce([cam = local_auth.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - - EXPECT_CALL(*local_auth, GetCaptureController) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->capture_controller_.get(); - }); - - EXPECT_CALL(*capture_controller, PausePreview) - .Times(1) - .WillOnce([cam = local_auth.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); - }); - - local_auth->local_auth_id_ = mock_local_auth_id; - local_auth->capture_controller_ = std::move(capture_controller); - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(mock_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("pausePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - -TEST(LocalAuthPlugin, PausePreviewHandlerErrorOnInvalidLocalAuthId) { - int64_t mock_local_auth_id = 1234; - int64_t missing_local_auth_id = 5678; - - std::unique_ptr initialize_result = - std::make_unique(); - - std::unique_ptr local_auth = - std::make_unique(MOCK_DEVICE_ID); - - std::unique_ptr capture_controller = - std::make_unique(); - - EXPECT_CALL(*local_auth, HasLocalAuthId) - .Times(1) - .WillOnce([cam = local_auth.get()](int64_t local_auth_id) { - return cam->local_auth_id_ == local_auth_id; - }); - - EXPECT_CALL(*local_auth, HasPendingResultByType).Times(0); - EXPECT_CALL(*local_auth, AddPendingResult).Times(0); - EXPECT_CALL(*local_auth, GetCaptureController).Times(0); - EXPECT_CALL(*capture_controller, PausePreview).Times(0); - - local_auth->local_auth_id_ = mock_local_auth_id; - - MockLocalAuthPlugin plugin(std::make_unique().get(), - std::make_unique().get(), - std::make_unique()); - - // Add mocked local_auth to plugins local_auth list. - plugin.AddLocalAuth(std::move(local_auth)); - - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); - - EncodableMap args = { - {EncodableValue("local_authId"), EncodableValue(missing_local_auth_id)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("pausePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); -} - } // namespace test } // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp deleted file mode 100644 index 899c1fdaea62..000000000000 --- a/packages/local_auth/local_auth_windows/windows/test/local_auth_test.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// 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 "camera.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mocks.h" - -namespace camera_windows { -using ::testing::_; -using ::testing::Eq; -using ::testing::NiceMock; -using ::testing::Pointee; - -namespace test { - -TEST(Camera, InitCameraCreatesCaptureController) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr capture_controller_factory = - std::make_unique(); - - EXPECT_CALL(*capture_controller_factory, CreateCaptureController) - .Times(1) - .WillOnce( - []() { return std::make_unique>(); }); - - EXPECT_TRUE(camera->GetCaptureController() == nullptr); - - // Init camera with mock capture controller factory - camera->InitCamera(std::move(capture_controller_factory), - std::make_unique().get(), - std::make_unique().get(), false, - ResolutionPreset::kAuto); - - EXPECT_TRUE(camera->GetCaptureController() != nullptr); -} - -TEST(Camera, AddPendingResultReturnsErrorForDuplicates) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr first_pending_result = - std::make_unique(); - std::unique_ptr second_pending_result = - std::make_unique(); - - EXPECT_CALL(*first_pending_result, ErrorInternal).Times(0); - EXPECT_CALL(*first_pending_result, SuccessInternal); - EXPECT_CALL(*second_pending_result, ErrorInternal).Times(1); - - camera->AddPendingResult(PendingResultType::kCreateCamera, - std::move(first_pending_result)); - - // This should fail - camera->AddPendingResult(PendingResultType::kCreateCamera, - std::move(second_pending_result)); - - // Mark pending result as succeeded - camera->OnCreateCaptureEngineSucceeded(0); -} - -TEST(Camera, OnCreateCaptureEngineSucceededReturnsCameraId) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - const int64_t texture_id = 12345; - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL( - *result, - SuccessInternal(Pointee(EncodableValue(EncodableMap( - {{EncodableValue("cameraId"), EncodableValue(texture_id)}}))))); - - camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); - - camera->OnCreateCaptureEngineSucceeded(texture_id); -} - -TEST(Camera, OnCreateCaptureEngineFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); - - camera->OnCreateCaptureEngineFailed(error_text); -} - -TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - const int32_t width = 123; - const int32_t height = 456; - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL( - *result, - SuccessInternal(Pointee(EncodableValue(EncodableMap({ - {EncodableValue("previewWidth"), EncodableValue((float)width)}, - {EncodableValue("previewHeight"), EncodableValue((float)height)}, - }))))); - - camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); - - camera->OnStartPreviewSucceeded(width, height); -} - -TEST(Camera, OnStartPreviewFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); - - camera->OnStartPreviewFailed(error_text); -} - -TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(nullptr)); - - camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); - - camera->OnPausePreviewSucceeded(); -} - -TEST(Camera, OnPausePreviewFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); - - camera->OnPausePreviewFailed(error_text); -} - -TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(nullptr)); - - camera->AddPendingResult(PendingResultType::kResumePreview, - std::move(result)); - - camera->OnResumePreviewSucceeded(); -} - -TEST(Camera, OnResumePreviewFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kResumePreview, - std::move(result)); - - camera->OnResumePreviewFailed(error_text); -} - -TEST(Camera, OnStartRecordSucceededReturnsSuccess) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(nullptr)); - - camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); - - camera->OnStartRecordSucceeded(); -} - -TEST(Camera, OnStartRecordFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); - - camera->OnStartRecordFailed(error_text); -} - -TEST(Camera, OnStopRecordSucceededReturnsSuccess) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string file_path = "C:\temp\filename.mp4"; - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); - - camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); - - camera->OnStopRecordSucceeded(file_path); -} - -TEST(Camera, OnStopRecordFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); - - camera->OnStopRecordFailed(error_text); -} - -TEST(Camera, OnTakePictureSucceededReturnsSuccess) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string file_path = "C:\temp\filename.jpeg"; - - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); - - camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); - - camera->OnTakePictureSucceeded(file_path); -} - -TEST(Camera, OnTakePictureFailedReturnsError) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - - std::string error_text = "error_text"; - - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); - - camera->OnTakePictureFailed(error_text); -} - -TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { - std::unique_ptr camera = - std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr capture_controller_factory = - std::make_unique(); - - std::unique_ptr binary_messenger = - std::make_unique(); - - std::string file_path = "C:\temp\filename.mp4"; - int64_t camera_id = 12345; - std::string camera_channel = - std::string("plugins.flutter.io/camera_windows/camera") + - std::to_string(camera_id); - int64_t video_duration = 1000000; - - EXPECT_CALL(*capture_controller_factory, CreateCaptureController) - .Times(1) - .WillOnce( - []() { return std::make_unique>(); }); - - // TODO: test binary content. - // First time is video record success message, - // and second is camera closing message. - EXPECT_CALL(*binary_messenger, Send(Eq(camera_channel), _, _, _)).Times(2); - - // Init camera with mock capture controller factory - camera->InitCamera(std::move(capture_controller_factory), - std::make_unique().get(), - binary_messenger.get(), false, ResolutionPreset::kAuto); - - // Pass camera id for camera - camera->OnCreateCaptureEngineSucceeded(camera_id); - - camera->OnVideoRecordSucceeded(file_path, video_duration); - - // Dispose camera before message channel. - camera = nullptr; -} - -} // namespace test -} // namespace camera_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/mocks.h b/packages/local_auth/local_auth_windows/windows/test/mocks.h index 0781989e94c2..e86832eb2749 100644 --- a/packages/local_auth/local_auth_windows/windows/test/mocks.h +++ b/packages/local_auth/local_auth_windows/windows/test/mocks.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ -#define PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#ifndef PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#define PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ #include #include @@ -11,15 +11,10 @@ #include #include #include -#include -#include "camera.h" -#include "camera_plugin.h" -#include "capture_controller.h" -#include "capture_controller_listener.h" -#include "capture_engine_listener.h" +#include "../local_auth.h" -namespace camera_windows { +namespace local_auth_windows { namespace test { namespace { @@ -41,975 +36,28 @@ class MockMethodResult : public flutter::MethodResult<> { MOCK_METHOD(void, NotImplementedInternal, (), (override)); }; -class MockBinaryMessenger : public flutter::BinaryMessenger { +class MockUserConsentVerifier : public UserConsentVerifier { public: - ~MockBinaryMessenger() = default; + explicit MockUserConsentVerifier(){}; + virtual ~MockUserConsentVerifier() = default; - MOCK_METHOD(void, Send, - (const std::string& channel, const uint8_t* message, - size_t message_size, flutter::BinaryReply reply), - (const)); - - MOCK_METHOD(void, SetMessageHandler, - (const std::string& channel, - flutter::BinaryMessageHandler handler), - ()); -}; - -class MockTextureRegistrar : public flutter::TextureRegistrar { - public: - MockTextureRegistrar() { - ON_CALL(*this, RegisterTexture) - .WillByDefault([this](flutter::TextureVariant* texture) -> int64_t { - EXPECT_TRUE(texture); - this->texture_ = texture; - this->texture_id_ = 1000; - return this->texture_id_; - }); - - ON_CALL(*this, UnregisterTexture) - .WillByDefault([this](int64_t tid) -> bool { - if (tid == this->texture_id_) { - texture_ = nullptr; - this->texture_id_ = -1; - return true; - } - return false; - }); - - ON_CALL(*this, MarkTextureFrameAvailable) - .WillByDefault([this](int64_t tid) -> bool { - if (tid == this->texture_id_) { - return true; - } - return false; - }); - } - - ~MockTextureRegistrar() { texture_ = nullptr; } - - MOCK_METHOD(int64_t, RegisterTexture, (flutter::TextureVariant * texture), - (override)); - - MOCK_METHOD(bool, UnregisterTexture, (int64_t), (override)); - MOCK_METHOD(bool, MarkTextureFrameAvailable, (int64_t), (override)); - - int64_t texture_id_ = -1; - flutter::TextureVariant* texture_ = nullptr; -}; - -class MockCameraFactory : public CameraFactory { - public: - MockCameraFactory() { - ON_CALL(*this, CreateCamera).WillByDefault([this]() { - assert(this->pending_camera_); - return std::move(this->pending_camera_); - }); - } - - ~MockCameraFactory() = default; - - // Disallow copy and move. - MockCameraFactory(const MockCameraFactory&) = delete; - MockCameraFactory& operator=(const MockCameraFactory&) = delete; - - MOCK_METHOD(std::unique_ptr, CreateCamera, - (const std::string& device_id), (override)); - - std::unique_ptr pending_camera_; -}; - -class MockCamera : public Camera { - public: - MockCamera(const std::string& device_id) - : device_id_(device_id), Camera(device_id){}; - - ~MockCamera() = default; - - // Disallow copy and move. - MockCamera(const MockCamera&) = delete; - MockCamera& operator=(const MockCamera&) = delete; - - MOCK_METHOD(void, OnCreateCaptureEngineSucceeded, (int64_t texture_id), - (override)); - MOCK_METHOD(std::unique_ptr>, GetPendingResultByType, - (PendingResultType type)); - MOCK_METHOD(void, OnCreateCaptureEngineFailed, (const std::string& error), - (override)); - - MOCK_METHOD(void, OnStartPreviewSucceeded, (int32_t width, int32_t height), - (override)); - MOCK_METHOD(void, OnStartPreviewFailed, (const std::string& error), - (override)); - - MOCK_METHOD(void, OnResumePreviewSucceeded, (), (override)); - MOCK_METHOD(void, OnResumePreviewFailed, (const std::string& error), - (override)); - - MOCK_METHOD(void, OnPausePreviewSucceeded, (), (override)); - MOCK_METHOD(void, OnPausePreviewFailed, (const std::string& error), - (override)); - - MOCK_METHOD(void, OnStartRecordSucceeded, (), (override)); - MOCK_METHOD(void, OnStartRecordFailed, (const std::string& error), - (override)); - - MOCK_METHOD(void, OnStopRecordSucceeded, (const std::string& file_path), - (override)); - MOCK_METHOD(void, OnStopRecordFailed, (const std::string& error), (override)); - - MOCK_METHOD(void, OnTakePictureSucceeded, (const std::string& file_path), - (override)); - MOCK_METHOD(void, OnTakePictureFailed, (const std::string& error), - (override)); - - MOCK_METHOD(void, OnVideoRecordSucceeded, - (const std::string& file_path, int64_t video_duration), - (override)); - MOCK_METHOD(void, OnVideoRecordFailed, (const std::string& error), - (override)); - MOCK_METHOD(void, OnCaptureError, (const std::string& error), (override)); - - MOCK_METHOD(bool, HasDeviceId, (std::string & device_id), (const override)); - MOCK_METHOD(bool, HasCameraId, (int64_t camera_id), (const override)); - - MOCK_METHOD(bool, AddPendingResult, - (PendingResultType type, std::unique_ptr> result), - (override)); - MOCK_METHOD(bool, HasPendingResultByType, (PendingResultType type), - (const override)); - - MOCK_METHOD(camera_windows::CaptureController*, GetCaptureController, (), - (override)); - - MOCK_METHOD(void, InitCamera, - (flutter::TextureRegistrar * texture_registrar, - flutter::BinaryMessenger* messenger, bool record_audio, - ResolutionPreset resolution_preset), - (override)); - - std::unique_ptr capture_controller_; - std::unique_ptr> pending_result_; - std::string device_id_; - int64_t camera_id_ = -1; -}; - -class MockCaptureControllerFactory : public CaptureControllerFactory { - public: - MockCaptureControllerFactory(){}; - virtual ~MockCaptureControllerFactory() = default; + MOCK_METHOD( + Windows::Foundation::IAsyncOperation< + Windows::Security::Credentials::UI::UserConsentVerificationResult>, + RequestVerificationForWindowAsync, (std::wstring localizedReason), + (override)); + MOCK_METHOD( + Windows::Foundation::IAsyncOperation< + Windows::Security::Credentials::UI::UserConsentVerifierAvailability>, + CheckAvailabilityAsync, (), (override)); // Disallow copy and move. - MockCaptureControllerFactory(const MockCaptureControllerFactory&) = delete; - MockCaptureControllerFactory& operator=(const MockCaptureControllerFactory&) = - delete; - - MOCK_METHOD(std::unique_ptr, CreateCaptureController, - (CaptureControllerListener * listener), (override)); -}; - -class MockCaptureController : public CaptureController { - public: - ~MockCaptureController() = default; - - MOCK_METHOD(void, InitCaptureDevice, - (flutter::TextureRegistrar * texture_registrar, - const std::string& device_id, bool record_audio, - ResolutionPreset resolution_preset), - (override)); - - MOCK_METHOD(uint32_t, GetPreviewWidth, (), (const override)); - MOCK_METHOD(uint32_t, GetPreviewHeight, (), (const override)); - - // Actions - MOCK_METHOD(void, StartPreview, (), (override)); - MOCK_METHOD(void, ResumePreview, (), (override)); - MOCK_METHOD(void, PausePreview, (), (override)); - MOCK_METHOD(void, StartRecord, - (const std::string& file_path, int64_t max_video_duration_ms), - (override)); - MOCK_METHOD(void, StopRecord, (), (override)); - MOCK_METHOD(void, TakePicture, (const std::string& file_path), (override)); -}; - -// MockCameraPlugin extends CameraPlugin behaviour a bit to allow adding cameras -// without creating them first with create message handler and mocking static -// system calls -class MockCameraPlugin : public CameraPlugin { - public: - MockCameraPlugin(flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger) - : CameraPlugin(texture_registrar, messenger){}; - - // Creates a plugin instance with the given CameraFactory instance. - // Exists for unit testing with mock implementations. - MockCameraPlugin(flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, - std::unique_ptr camera_factory) - : CameraPlugin(texture_registrar, messenger, std::move(camera_factory)){}; - - ~MockCameraPlugin() = default; - - // Disallow copy and move. - MockCameraPlugin(const MockCameraPlugin&) = delete; - MockCameraPlugin& operator=(const MockCameraPlugin&) = delete; - - MOCK_METHOD(bool, EnumerateVideoCaptureDeviceSources, - (IMFActivate * **devices, UINT32* count), (override)); - - // Helper to add camera without creating it via CameraFactory for testing - // purposes - void AddCamera(std::unique_ptr camera) { - cameras_.push_back(std::move(camera)); - } -}; - -class MockCaptureSource : public IMFCaptureSource { - public: - MockCaptureSource(){}; - ~MockCaptureSource() = default; - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFCaptureSource) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - MOCK_METHOD(HRESULT, GetCaptureDeviceSource, - (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType, - IMFMediaSource** ppMediaSource)); - MOCK_METHOD(HRESULT, GetCaptureDeviceActivate, - (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType, - IMFActivate** ppActivate)); - MOCK_METHOD(HRESULT, GetService, - (REFIID rguidService, REFIID riid, IUnknown** ppUnknown)); - MOCK_METHOD(HRESULT, AddEffect, - (DWORD dwSourceStreamIndex, IUnknown* pUnknown)); - - MOCK_METHOD(HRESULT, RemoveEffect, - (DWORD dwSourceStreamIndex, IUnknown* pUnknown)); - MOCK_METHOD(HRESULT, RemoveAllEffects, (DWORD dwSourceStreamIndex)); - MOCK_METHOD(HRESULT, GetAvailableDeviceMediaType, - (DWORD dwSourceStreamIndex, DWORD dwMediaTypeIndex, - IMFMediaType** ppMediaType)); - MOCK_METHOD(HRESULT, SetCurrentDeviceMediaType, - (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType)); - MOCK_METHOD(HRESULT, GetCurrentDeviceMediaType, - (DWORD dwSourceStreamIndex, IMFMediaType** ppMediaType)); - MOCK_METHOD(HRESULT, GetDeviceStreamCount, (DWORD * pdwStreamCount)); - MOCK_METHOD(HRESULT, GetDeviceStreamCategory, - (DWORD dwSourceStreamIndex, - MF_CAPTURE_ENGINE_STREAM_CATEGORY* pStreamCategory)); - MOCK_METHOD(HRESULT, GetMirrorState, - (DWORD dwStreamIndex, BOOL* pfMirrorState)); - MOCK_METHOD(HRESULT, SetMirrorState, - (DWORD dwStreamIndex, BOOL fMirrorState)); - MOCK_METHOD(HRESULT, GetStreamIndexFromFriendlyName, - (UINT32 uifriendlyName, DWORD* pdwActualStreamIndex)); - - private: - volatile ULONG ref_ = 0; -}; - -// Uses IMFMediaSourceEx which has SetD3DManager method. -class MockMediaSource : public IMFMediaSourceEx { - public: - MockMediaSource(){}; - ~MockMediaSource() = default; - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFMediaSource) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - // IMFMediaSource - HRESULT GetCharacteristics(DWORD* dwCharacteristics) override { - return E_NOTIMPL; - } - // IMFMediaSource - HRESULT CreatePresentationDescriptor( - IMFPresentationDescriptor** presentationDescriptor) override { - return E_NOTIMPL; - } - // IMFMediaSource - HRESULT Start(IMFPresentationDescriptor* presentationDescriptor, - const GUID* guidTimeFormat, - const PROPVARIANT* varStartPosition) override { - return E_NOTIMPL; - } - // IMFMediaSource - HRESULT Stop(void) override { return E_NOTIMPL; } - // IMFMediaSource - HRESULT Pause(void) override { return E_NOTIMPL; } - // IMFMediaSource - HRESULT Shutdown(void) override { return E_NOTIMPL; } - - // IMFMediaEventGenerator - HRESULT GetEvent(DWORD dwFlags, IMFMediaEvent** event) override { - return E_NOTIMPL; - } - // IMFMediaEventGenerator - HRESULT BeginGetEvent(IMFAsyncCallback* callback, - IUnknown* unkState) override { - return E_NOTIMPL; - } - // IMFMediaEventGenerator - HRESULT EndGetEvent(IMFAsyncResult* result, IMFMediaEvent** event) override { - return E_NOTIMPL; - } - // IMFMediaEventGenerator - HRESULT QueueEvent(MediaEventType met, REFGUID guidExtendedType, - HRESULT hrStatus, const PROPVARIANT* value) override { - return E_NOTIMPL; - } - - // IMFMediaSourceEx - HRESULT GetSourceAttributes(IMFAttributes** attributes) { return E_NOTIMPL; } - // IMFMediaSourceEx - HRESULT GetStreamAttributes(DWORD stream_id, IMFAttributes** attributes) { - return E_NOTIMPL; - } - // IMFMediaSourceEx - HRESULT SetD3DManager(IUnknown* manager) { return S_OK; } - - private: - volatile ULONG ref_ = 0; + MockUserConsentVerifier(const MockUserConsentVerifier&) = delete; + MockUserConsentVerifier& operator=(const MockUserConsentVerifier&) = delete; }; -class MockCapturePreviewSink : public IMFCapturePreviewSink { - public: - // IMFCaptureSink - MOCK_METHOD(HRESULT, GetOutputMediaType, - (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, GetService, - (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, - IUnknown** ppUnknown)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, AddStream, - (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, - IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, Prepare, ()); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, RemoveAllStreams, ()); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, SetRenderHandle, (HANDLE handle)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, SetRenderSurface, (IUnknown * pSurface)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, UpdateVideo, - (const MFVideoNormalizedRect* pSrc, const RECT* pDst, - const COLORREF* pBorderClr)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, SetSampleCallback, - (DWORD dwStreamSinkIndex, - IMFCaptureEngineOnSampleCallback* pCallback)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, GetMirrorState, (BOOL * pfMirrorState)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, SetMirrorState, (BOOL fMirrorState)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, GetRotation, - (DWORD dwStreamIndex, DWORD* pdwRotationValue)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, SetRotation, - (DWORD dwStreamIndex, DWORD dwRotationValue)); - - // IMFCapturePreviewSink - MOCK_METHOD(HRESULT, SetCustomSink, (IMFMediaSink * pMediaSink)); - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFCapturePreviewSink) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - void SendFakeSample(uint8_t* src_buffer, uint32_t size) { - assert(sample_callback_); - ComPtr sample; - ComPtr buffer; - HRESULT hr = MFCreateSample(&sample); - - if (SUCCEEDED(hr)) { - hr = MFCreateMemoryBuffer(size, &buffer); - } - - if (SUCCEEDED(hr)) { - uint8_t* target_data; - if (SUCCEEDED(buffer->Lock(&target_data, nullptr, nullptr))) { - std::copy(src_buffer, src_buffer + size, target_data); - } - hr = buffer->Unlock(); - } - - if (SUCCEEDED(hr)) { - hr = buffer->SetCurrentLength(size); - } - - if (SUCCEEDED(hr)) { - hr = sample->AddBuffer(buffer.Get()); - } - - if (SUCCEEDED(hr)) { - sample_callback_->OnSample(sample.Get()); - } - } - - ComPtr sample_callback_; - - private: - ~MockCapturePreviewSink() = default; - volatile ULONG ref_ = 0; -}; - -class MockCaptureRecordSink : public IMFCaptureRecordSink { - public: - // IMFCaptureSink - MOCK_METHOD(HRESULT, GetOutputMediaType, - (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, GetService, - (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, - IUnknown** ppUnknown)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, AddStream, - (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, - IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, Prepare, ()); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, RemoveAllStreams, ()); - - // IMFCaptureRecordSink - MOCK_METHOD(HRESULT, SetOutputByteStream, - (IMFByteStream * pByteStream, REFGUID guidContainerType)); - - // IMFCaptureRecordSink - MOCK_METHOD(HRESULT, SetOutputFileName, (LPCWSTR fileName)); - - // IMFCaptureRecordSink - MOCK_METHOD(HRESULT, SetSampleCallback, - (DWORD dwStreamSinkIndex, - IMFCaptureEngineOnSampleCallback* pCallback)); - - // IMFCaptureRecordSink - MOCK_METHOD(HRESULT, SetCustomSink, (IMFMediaSink * pMediaSink)); - - // IMFCaptureRecordSink - MOCK_METHOD(HRESULT, GetRotation, - (DWORD dwStreamIndex, DWORD* pdwRotationValue)); - - // IMFCaptureRecordSink - MOCK_METHOD(HRESULT, SetRotation, - (DWORD dwStreamIndex, DWORD dwRotationValue)); - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFCaptureRecordSink) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - private: - ~MockCaptureRecordSink() = default; - volatile ULONG ref_ = 0; -}; - -class MockCapturePhotoSink : public IMFCapturePhotoSink { - public: - // IMFCaptureSink - MOCK_METHOD(HRESULT, GetOutputMediaType, - (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, GetService, - (DWORD dwSinkStreamIndex, REFGUID rguidService, REFIID riid, - IUnknown** ppUnknown)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, AddStream, - (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType, - IMFAttributes* pAttributes, DWORD* pdwSinkStreamIndex)); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, Prepare, ()); - - // IMFCaptureSink - MOCK_METHOD(HRESULT, RemoveAllStreams, ()); - - // IMFCapturePhotoSink - MOCK_METHOD(HRESULT, SetOutputFileName, (LPCWSTR fileName)); - - // IMFCapturePhotoSink - MOCK_METHOD(HRESULT, SetSampleCallback, - (IMFCaptureEngineOnSampleCallback * pCallback)); - - // IMFCapturePhotoSink - MOCK_METHOD(HRESULT, SetOutputByteStream, (IMFByteStream * pByteStream)); - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFCapturePhotoSink) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - private: - ~MockCapturePhotoSink() = default; - volatile ULONG ref_ = 0; -}; - -template -class FakeIMFAttributesBase : public T { - static_assert(std::is_base_of::value, - "I must inherit from IMFAttributes"); - - // IIMFAttributes - HRESULT GetItem(REFGUID guidKey, PROPVARIANT* pValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT CompareItem(REFGUID guidKey, REFPROPVARIANT Value, - BOOL* pbResult) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT Compare(IMFAttributes* pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, - BOOL* pbResult) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetUINT32(REFGUID guidKey, UINT32* punValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetUINT64(REFGUID guidKey, UINT64* punValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetDouble(REFGUID guidKey, double* pfValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetGUID(REFGUID guidKey, GUID* pguidValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetStringLength(REFGUID guidKey, UINT32* pcchLength) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, - UINT32* pcchLength) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, - UINT32* pcchLength) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, - UINT32* pcbBlobSize) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, - UINT32* pcbSize) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT GetUnknown(REFGUID guidKey, REFIID riid, - __RPC__deref_out_opt LPVOID* ppv) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetItem(REFGUID guidKey, REFPROPVARIANT Value) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT DeleteItem(REFGUID guidKey) override { return E_NOTIMPL; } - - // IIMFAttributes - HRESULT DeleteAllItems(void) override { return E_NOTIMPL; } - - // IIMFAttributes - HRESULT SetUINT32(REFGUID guidKey, UINT32 unValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetUINT64(REFGUID guidKey, UINT64 unValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetDouble(REFGUID guidKey, double fValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetGUID(REFGUID guidKey, REFGUID guidValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetString(REFGUID guidKey, LPCWSTR wszValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetBlob(REFGUID guidKey, const UINT8* pBuf, - UINT32 cbBufSize) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT SetUnknown(REFGUID guidKey, IUnknown* pUnknown) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT LockStore(void) override { return E_NOTIMPL; } - - // IIMFAttributes - HRESULT UnlockStore(void) override { return E_NOTIMPL; } - - // IIMFAttributes - HRESULT GetCount(UINT32* pcItems) override { return E_NOTIMPL; } - - // IIMFAttributes - HRESULT GetItemByIndex(UINT32 unIndex, GUID* pguidKey, - PROPVARIANT* pValue) override { - return E_NOTIMPL; - } - - // IIMFAttributes - HRESULT CopyAllItems(IMFAttributes* pDest) override { return E_NOTIMPL; } -}; - -class FakeMediaType : public FakeIMFAttributesBase { - public: - FakeMediaType(GUID major_type, GUID sub_type, int width, int height) - : major_type_(major_type), - sub_type_(sub_type), - width_(width), - height_(height){}; - - // IMFAttributes - HRESULT GetUINT64(REFGUID key, UINT64* value) override { - if (key == MF_MT_FRAME_SIZE) { - *value = (int64_t)width_ << 32 | (int64_t)height_; - return S_OK; - } else if (key == MF_MT_FRAME_RATE) { - *value = (int64_t)frame_rate_ << 32 | 1; - return S_OK; - } - return E_FAIL; - }; - - // IMFAttributes - HRESULT GetGUID(REFGUID key, GUID* value) override { - if (key == MF_MT_MAJOR_TYPE) { - *value = major_type_; - return S_OK; - } else if (key == MF_MT_SUBTYPE) { - *value = sub_type_; - return S_OK; - } - return E_FAIL; - } - - // IIMFAttributes - HRESULT CopyAllItems(IMFAttributes* pDest) override { - pDest->SetUINT64(MF_MT_FRAME_SIZE, - (int64_t)width_ << 32 | (int64_t)height_); - pDest->SetUINT64(MF_MT_FRAME_RATE, (int64_t)frame_rate_ << 32 | 1); - pDest->SetGUID(MF_MT_MAJOR_TYPE, major_type_); - pDest->SetGUID(MF_MT_SUBTYPE, sub_type_); - return S_OK; - } - - // IMFMediaType - HRESULT STDMETHODCALLTYPE GetMajorType(GUID* pguidMajorType) override { - return E_NOTIMPL; - }; - - // IMFMediaType - HRESULT STDMETHODCALLTYPE IsCompressedFormat(BOOL* pfCompressed) override { - return E_NOTIMPL; - } - - // IMFMediaType - HRESULT STDMETHODCALLTYPE IsEqual(IMFMediaType* pIMediaType, - DWORD* pdwFlags) override { - return E_NOTIMPL; - } - - // IMFMediaType - HRESULT STDMETHODCALLTYPE GetRepresentation( - GUID guidRepresentation, LPVOID* ppvRepresentation) override { - return E_NOTIMPL; - } - - // IMFMediaType - HRESULT STDMETHODCALLTYPE FreeRepresentation( - GUID guidRepresentation, LPVOID pvRepresentation) override { - return E_NOTIMPL; - } - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFMediaType) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - private: - ~FakeMediaType() = default; - volatile ULONG ref_ = 0; - const GUID major_type_; - const GUID sub_type_; - const int width_; - const int height_; - const int frame_rate_ = 30; -}; - -class MockCaptureEngine : public IMFCaptureEngine { - public: - MockCaptureEngine() { - ON_CALL(*this, Initialize) - .WillByDefault([this](IMFCaptureEngineOnEventCallback* callback, - IMFAttributes* attributes, IUnknown* audioSource, - IUnknown* videoSource) -> HRESULT { - EXPECT_TRUE(callback); - EXPECT_TRUE(attributes); - EXPECT_TRUE(videoSource); - // audioSource is allowed to be nullptr; - callback_ = callback; - videoSource_ = reinterpret_cast(videoSource); - audioSource_ = reinterpret_cast(audioSource); - initialized_ = true; - return S_OK; - }); - }; - - virtual ~MockCaptureEngine() = default; - - MOCK_METHOD(HRESULT, Initialize, - (IMFCaptureEngineOnEventCallback * callback, - IMFAttributes* attributes, IUnknown* audioSource, - IUnknown* videoSource)); - MOCK_METHOD(HRESULT, StartPreview, ()); - MOCK_METHOD(HRESULT, StopPreview, ()); - MOCK_METHOD(HRESULT, StartRecord, ()); - MOCK_METHOD(HRESULT, StopRecord, - (BOOL finalize, BOOL flushUnprocessedSamples)); - MOCK_METHOD(HRESULT, TakePhoto, ()); - MOCK_METHOD(HRESULT, GetSink, - (MF_CAPTURE_ENGINE_SINK_TYPE type, IMFCaptureSink** sink)); - MOCK_METHOD(HRESULT, GetSource, (IMFCaptureSource * *ppSource)); - - // IUnknown - STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref_); } - - // IUnknown - STDMETHODIMP_(ULONG) Release() { - LONG ref = InterlockedDecrement(&ref_); - if (ref == 0) { - delete this; - } - return ref; - } - - // IUnknown - STDMETHODIMP_(HRESULT) QueryInterface(const IID& riid, void** ppv) { - *ppv = nullptr; - - if (riid == IID_IMFCaptureEngine) { - *ppv = static_cast(this); - ((IUnknown*)*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - - void CreateFakeEvent(HRESULT hrStatus, GUID event_type) { - EXPECT_TRUE(initialized_); - ComPtr event; - MFCreateMediaEvent(MEExtendedType, event_type, hrStatus, nullptr, &event); - if (callback_) { - callback_->OnEvent(event.Get()); - } - } - - ComPtr callback_; - ComPtr videoSource_; - ComPtr audioSource_; - volatile ULONG ref_ = 0; - bool initialized_ = false; -}; - -#define MOCK_DEVICE_ID "mock_device_id" -#define MOCK_CAMERA_NAME "mock_camera_name <" MOCK_DEVICE_ID ">" -#define MOCK_INVALID_CAMERA_NAME "invalid_camera_name" - } // namespace } // namespace test -} // namespace camera_windows +} // namespace local_auth_windows -#endif // PACKAGES_CAMERA_CAMERA_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#endif // PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ From bf47529c79af4b2bc27edbe8775fcf101a90f5db Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Mon, 18 Apr 2022 14:51:25 -0700 Subject: [PATCH 11/20] Added more tests. --- .../windows/test/local_auth_plugin_test.cpp | 195 +++++++++++++++++- 1 file changed, 193 insertions(+), 2 deletions(-) diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp index 512a61f2d503..fa5cdacb3bec 100644 --- a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp @@ -30,7 +30,7 @@ using ::testing::Eq; using ::testing::Pointee; using ::testing::Return; -TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerSuccessIfNoLocalAuths) { +TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierAvailable) { std::unique_ptr result = std::make_unique(); @@ -49,7 +49,7 @@ TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerSuccessIfNoLocalAuths) { LocalAuthPlugin plugin(std::move(mockConsentVerifier)); EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal).Times(1); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); plugin.HandleMethodCall( flutter::MethodCall("isDeviceSupported", @@ -57,5 +57,196 @@ TEST(LocalAuthPlugin, AvailableLocalAuthsHandlerSuccessIfNoLocalAuths) { std::move(result)); } +TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierNotAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); + + plugin.HandleMethodCall( + flutter::MethodCall("isDeviceSupported", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, + GetAvailableBiometricsHandlerReturnEmptyListIfVerifierNotAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableList()))); + + plugin.HandleMethodCall( + flutter::MethodCall("getAvailableBiometrics", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, + GetAvailableBiometricsHandlerReturnNonEmptyListIfVerifierAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableList( + {EncodableValue("fingerprint"), + EncodableValue("face"), EncodableValue("iris")})))); + + plugin.HandleMethodCall( + flutter::MethodCall("getAvailableBiometrics", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerDoesNotSupportBiometricOnly) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(1); + EXPECT_CALL(*result, SuccessInternal).Times(0); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(true)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenAuthorized) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) + .Times(1) + .WillOnce([](std::wstring localizedReason) + -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult> { + EXPECT_EQ(localizedReason, L"My Reason"); + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(false)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenNotAuthorized) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) + .Times(1) + .WillOnce([](std::wstring localizedReason) + -> Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult> { + EXPECT_EQ(localizedReason, L"My Reason"); + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Canceled; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(false)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + } // namespace test } // namespace local_auth_windows From 81439839d07d99c2f545f8aee60b2e839ebf9a0d Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Tue, 19 Apr 2022 11:05:21 -0700 Subject: [PATCH 12/20] Revert changes in local_auth. --- .../local_auth/example/windows/CMakeLists.txt | 95 ------- .../example/windows/flutter/CMakeLists.txt | 103 -------- .../windows/flutter/generated_plugins.cmake | 15 -- .../example/windows/runner/CMakeLists.txt | 17 -- .../example/windows/runner/Runner.rc | 121 --------- .../example/windows/runner/flutter_window.cpp | 64 ----- .../example/windows/runner/flutter_window.h | 36 --- .../example/windows/runner/main.cpp | 45 ---- .../example/windows/runner/resource.h | 16 -- .../windows/runner/resources/app_icon.ico | Bin 33772 -> 0 bytes .../windows/runner/runner.exe.manifest | 20 -- .../example/windows/runner/utils.cpp | 66 ----- .../local_auth/example/windows/runner/utils.h | 22 -- .../example/windows/runner/win32_window.cpp | 240 ------------------ .../example/windows/runner/win32_window.h | 98 ------- .../local_auth/lib/src/types/error_codes.dart | 3 - packages/local_auth/local_auth/pubspec.yaml | 5 - 17 files changed, 966 deletions(-) delete mode 100644 packages/local_auth/local_auth/example/windows/CMakeLists.txt delete mode 100644 packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt delete mode 100644 packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake delete mode 100644 packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt delete mode 100644 packages/local_auth/local_auth/example/windows/runner/Runner.rc delete mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp delete mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.h delete mode 100644 packages/local_auth/local_auth/example/windows/runner/main.cpp delete mode 100644 packages/local_auth/local_auth/example/windows/runner/resource.h delete mode 100644 packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico delete mode 100644 packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest delete mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.cpp delete mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.h delete mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.cpp delete mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.h diff --git a/packages/local_auth/local_auth/example/windows/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/CMakeLists.txt deleted file mode 100644 index 1633297a0c7c..000000000000 --- a/packages/local_auth/local_auth/example/windows/CMakeLists.txt +++ /dev/null @@ -1,95 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(example LANGUAGES CXX) - -set(BINARY_NAME "example") - -cmake_policy(SET CMP0063 NEW) - -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() - -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt deleted file mode 100644 index b2e4bd8d658b..000000000000 --- a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,103 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 4d10c2518654..000000000000 --- a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) diff --git a/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt deleted file mode 100644 index de2d8916b72b..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) -apply_standard_settings(${BINARY_NAME}) -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/local_auth/local_auth/example/windows/runner/Runner.rc b/packages/local_auth/local_auth/example/windows/runner/Runner.rc deleted file mode 100644 index 7e35b9f56a22..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER -#else -#define VERSION_AS_NUMBER 1,0,0 -#endif - -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "io.flutter.plugins" "\0" - VALUE "FileDescription", "example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 io.flutter.plugins. All rights reserved." "\0" - VALUE "OriginalFilename", "example.exe" "\0" - VALUE "ProductName", "example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp deleted file mode 100644 index 217bf9b69e67..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// 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_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h deleted file mode 100644 index 7cbf3d3ebbb2..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/flutter_window.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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. -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/main.cpp b/packages/local_auth/local_auth/example/windows/runner/main.cpp deleted file mode 100644 index 1285aabf714a..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// 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 -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t* command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/packages/local_auth/local_auth/example/windows/runner/resource.h b/packages/local_auth/local_auth/example/windows/runner/resource.h deleted file mode 100644 index d5d958dc4257..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico b/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6370ebb9253ad831cc31de4a9c965f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 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 diff --git a/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest deleted file mode 100644 index c977c4a42589..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.cpp b/packages/local_auth/local_auth/example/windows/runner/utils.cpp deleted file mode 100644 index 8b8eaa54539a..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/utils.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// 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 "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE* unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = - ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, - nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } - std::string utf8_string; - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.h b/packages/local_auth/local_auth/example/windows/runner/utils.h deleted file mode 100644 index 6d1cc48f0426..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/utils.h +++ /dev/null @@ -1,22 +0,0 @@ -// 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. -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp deleted file mode 100644 index 34738de2d35b..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp +++ /dev/null @@ -1,240 +0,0 @@ -// 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 "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { ++g_active_window_count; } - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { return window_handle_; } - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.h b/packages/local_auth/local_auth/example/windows/runner/win32_window.h deleted file mode 100644 index 0f8bd1b7f920..000000000000 --- a/packages/local_auth/local_auth/example/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -// 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. -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/local_auth/local_auth/lib/src/types/error_codes.dart b/packages/local_auth/local_auth/lib/src/types/error_codes.dart index 61c7709ffad5..3426099bacbd 100644 --- a/packages/local_auth/local_auth/lib/src/types/error_codes.dart +++ b/packages/local_auth/local_auth/lib/src/types/error_codes.dart @@ -24,6 +24,3 @@ const String lockedOut = 'LockedOut'; /// Indicates the API being disabled due to too many lock outs. /// Strong authentication like PIN/Pattern/Password is required to unlock. const String permanentlyLockedOut = 'PermanentlyLockedOut'; - -/// Indicates that the biometricOnly parameter can't be true on Windows -const String biometricOnlyNotSupported = 'biometricOnlyNotSupported'; diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 4f36572d11d3..fa055fab17f8 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -16,9 +16,6 @@ flutter: default_package: local_auth_android ios: default_package: local_auth_ios - # Uncomment once the PR #4806 is merged. - # windows: - # default_package: local_auth_windows dependencies: flutter: @@ -26,8 +23,6 @@ dependencies: intl: ^0.17.0 local_auth_android: ^1.0.0 local_auth_ios: ^1.0.1 - # Uncomment once the PR #4806 is merged. - # local_auth_windows: ^1.0.0 local_auth_platform_interface: ^1.0.1 dev_dependencies: From e9ab4efddcddec00eeee1d20853bebc48cacae91 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Tue, 10 May 2022 14:26:51 -0700 Subject: [PATCH 13/20] [local_auth_windows] Fixed authors list. --- .../local_auth/local_auth_windows/AUTHORS | 62 +------------------ 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/packages/local_auth/local_auth_windows/AUTHORS b/packages/local_auth/local_auth_windows/AUTHORS index d5694690c247..5db3d584e6bc 100644 --- a/packages/local_auth/local_auth_windows/AUTHORS +++ b/packages/local_auth/local_auth_windows/AUTHORS @@ -4,64 +4,4 @@ # Name/Organization Google Inc. -The Chromium Authors -German Saprykin -Benjamin Sauer -larsenthomasj@gmail.com -Ali Bitek -Pol Batlló -Anatoly Pulyaevskiy -Hayden Flinner -Stefano Rodriguez -Salvatore Giordano -Brian Armstrong -Paul DeMarco -Fabricio Nogueira -Simon Lightfoot -Ashton Thomas -Thomas Danner -Diego Velásquez -Hajime Nakamura -Tuyển Vũ Xuân -Miguel Ruivo -Sarthak Verma -Mike Diarmid -Invertase -Elliot Hesp -Vince Varga -Aawaz Gyawali -EUI Limited -Katarina Sheremet -Thomas Stockx -Sarbagya Dhaubanjar -Ozkan Eksi -Rishab Nayak -ko2ic -Jonathan Younger -Jose Sanchez -Debkanchan Samadder -Audrius Karosevicius -Lukasz Piliszczuk -SoundReply Solutions GmbH -Rafal Wachol -Pau Picas -Christian Weder -Alexandru Tuca -Christian Weder -Rhodes Davis Jr. -Luigi Agosti -Quentin Le Guennec -Koushik Ravikumar -Nissim Dsilva -Giancarlo Rocha -Ryo Miyake -Théo Champion -Kazuki Yamaguchi -Eitan Schwartz -Chris Rutkowski -Juan Alvarez -Aleksandr Yurkovskiy -Anton Borries -Alex Li -Rahul Raj <64.rahulraj@gmail.com> -Bodhi Mulders +Alexandre Zollinger Chohfi \ No newline at end of file From 30f095803c8861a03f8d3224996a17c2271d2e1b Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Tue, 10 May 2022 14:27:45 -0700 Subject: [PATCH 14/20] [local_auth_windows] Fixed changelog. --- packages/local_auth/local_auth_windows/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md index 7f198f2d66c0..7cf171f305de 100644 --- a/packages/local_auth/local_auth_windows/CHANGELOG.md +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -1,3 +1,3 @@ ## 1.0.0 -* Initial release from migration to federated architecture. +* Initial release of Windows support. From 97e1ee92c97689dc36a6d73f2184f2fa6137edb7 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 12 May 2022 16:55:10 -0700 Subject: [PATCH 15/20] [local_auth_windows] PR feedback. --- .../lib/local_auth_windows.dart | 21 +-- .../test/local_auth_test.dart | 19 +- .../local_auth_windows/windows/CMakeLists.txt | 13 +- .../local_auth_windows/windows/local_auth.h | 42 ++--- .../windows/local_auth_plugin.cpp | 170 +++++++++--------- .../windows/test/local_auth_plugin_test.cpp | 31 ++-- .../local_auth_windows/windows/test/mocks.h | 18 +- 7 files changed, 161 insertions(+), 153 deletions(-) diff --git a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart index cfecbc95f75b..1d65e81050f1 100644 --- a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart +++ b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart @@ -4,9 +4,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; import 'package:local_auth_windows/types/auth_messages_windows.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; @@ -49,28 +46,24 @@ class LocalAuthWindows extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; } @override Future> getEnrolledBiometrics() async { final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', + 'getEnrolledBiometrics', )) ?? []; final List biometrics = []; for (final String value in result) { switch (value) { - case 'face': - biometrics.add(BiometricType.face); + case 'weak': + biometrics.add(BiometricType.weak); break; - case 'fingerprint': - biometrics.add(BiometricType.fingerprint); - break; - case 'iris': - biometrics.add(BiometricType.iris); - break; - case 'undefined': + case 'strong': + biometrics.add(BiometricType.strong); break; } } diff --git a/packages/local_auth/local_auth_windows/test/local_auth_test.dart b/packages/local_auth/local_auth_windows/test/local_auth_test.dart index ba125745079d..0b0e188686bb 100644 --- a/packages/local_auth/local_auth_windows/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_windows/test/local_auth_test.dart @@ -2,11 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; import 'package:local_auth_windows/local_auth_windows.dart'; void main() { @@ -24,9 +21,8 @@ void main() { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); switch (methodCall.method) { - case 'getAvailableBiometrics': - return Future>.value( - ['face', 'fingerprint', 'iris', 'undefined']); + case 'getEnrolledBiometrics': + return Future>.value(['weak', 'strong']); default: return Future.value(true); } @@ -35,13 +31,13 @@ void main() { log.clear(); }); - test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + test('deviceSupportsBiometrics calls platform', () async { final bool result = await localAuthentication.deviceSupportsBiometrics(); expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('deviceSupportsBiometrics', arguments: null), ], ); expect(result, true); @@ -54,13 +50,12 @@ void main() { expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('getEnrolledBiometrics', arguments: null), ], ); expect(result, [ - BiometricType.face, - BiometricType.fingerprint, - BiometricType.iris + BiometricType.weak, + BiometricType.strong, ]); }); diff --git a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt index 173243ee0368..bcf59bb827c7 100644 --- a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt +++ b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.15) set(PROJECT_NAME "local_auth_windows") -set(CPPWINRT_VERSION "2.0.210806.1") +set(WIL_VERSION "1.0.220201.1") +set(CPPWINRT_VERSION "2.0.220418.1") project(${PROJECT_NAME} LANGUAGES CXX) include(FetchContent) @@ -19,6 +20,14 @@ if (NOT NUGET) set(NUGET ${nuget_SOURCE_DIR}/nuget.exe) endif() +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.ImplementationLibrary -Version ${WIL_VERSION} -OutputDirectory ${CMAKE_BINARY_DIR}/packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}") +endif() + execute_process(COMMAND ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages WORKING_DIRECTORY ${CMAKE_BINARY_DIR} @@ -55,6 +64,7 @@ target_compile_options(${PLUGIN_NAME} PRIVATE /await) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}/build/native/Microsoft.Windows.ImplementationLibrary.targets) target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin windowsapp) # List of absolute paths to libraries that should be bundled with the plugin @@ -94,6 +104,7 @@ apply_standard_settings(${TEST_RUNNER}) target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") target_compile_features(${TEST_RUNNER} PRIVATE cxx_std_20) target_compile_options(${TEST_RUNNER} PRIVATE /await) +target_link_libraries(${TEST_RUNNER} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}/build/native/Microsoft.Windows.ImplementationLibrary.targets) target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) target_link_libraries(${TEST_RUNNER} PRIVATE windowsapp) target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h index 8d8cb695e48d..126a037393df 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth.h +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -3,16 +3,17 @@ // found in the LICENSE file. #include - -#include "include/local_auth_windows/local_auth_plugin.h" - -// This must be included before many other Windows headers. #include #include #include #include #include #include + +#include "include/local_auth_windows/local_auth_plugin.h" + +// Include prior to C++/WinRT Headers +#include #include #include #include @@ -24,19 +25,17 @@ namespace local_auth_windows { -using namespace flutter; -using namespace winrt; - class UserConsentVerifier { public: UserConsentVerifier() {} virtual ~UserConsentVerifier() = default; - virtual Windows::Foundation::IAsyncOperation< - Windows::Security::Credentials::UI::UserConsentVerificationResult> - RequestVerificationForWindowAsync(std::wstring localizedReason) = 0; - virtual Windows::Foundation::IAsyncOperation< - Windows::Security::Credentials::UI::UserConsentVerifierAvailability> + virtual winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localized_reason) = 0; + virtual winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> CheckAvailabilityAsync() = 0; // Disallow copy and move. @@ -44,16 +43,17 @@ class UserConsentVerifier { UserConsentVerifier& operator=(const UserConsentVerifier&) = delete; }; -class LocalAuthPlugin : public Plugin { +class LocalAuthPlugin : public flutter::Plugin { public: - static void RegisterWithRegistrar(PluginRegistrarWindows* registrar); + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); LocalAuthPlugin(std::function window_provider); LocalAuthPlugin(std::unique_ptr user_consent_verifier); // Called when a method is called on this plugin's channel from Dart. - void HandleMethodCall(const MethodCall& method_call, - std::unique_ptr> result); + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); virtual ~LocalAuthPlugin(); @@ -61,12 +61,12 @@ class LocalAuthPlugin : public Plugin { std::unique_ptr user_consent_verifier_; winrt::fire_and_forget Authenticate( - const MethodCall& method_call, - std::unique_ptr> result); - winrt::fire_and_forget GetAvailableBiometrics( - std::unique_ptr> result); + const flutter::MethodCall& method_call, + std::unique_ptr> result); + winrt::fire_and_forget GetEnrolledBiometrics( + std::unique_ptr> result); winrt::fire_and_forget IsDeviceSupported( - std::unique_ptr> result); + std::unique_ptr> result); }; } // namespace local_auth_windows \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp index 8fd2509b370a..26190cdc623d 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -1,20 +1,20 @@ // 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 "local_auth.h" +#include -namespace local_auth_windows { +#include "local_auth.h" -using namespace flutter; -using namespace winrt; +namespace { template // Helper method for getting an argument from an EncodableValue -T GetArgument(const std::string arg, const EncodableValue* args, T fallback) { +T GetArgument(const std::string arg, const flutter::EncodableValue* args, + T fallback) { T result{fallback}; - const auto* arguments = std::get_if(args); + const auto* arguments = std::get_if(args); if (arguments) { - auto result_it = arguments->find(EncodableValue(arg)); + auto result_it = arguments->find(flutter::EncodableValue(arg)); if (result_it != arguments->end()) { result = std::get(result_it->second); } @@ -50,45 +50,46 @@ std::wstring Utf16FromUtf8(const std::string& utf8_string) { return utf16_string; } +} // namespace + +namespace local_auth_windows { + class UserConsentVerifierImpl : public UserConsentVerifier { public: explicit UserConsentVerifierImpl(std::function window_provider) : get_root_window_(std::move(window_provider)){}; virtual ~UserConsentVerifierImpl() = default; - Windows::Foundation::IAsyncOperation< - Windows::Security::Credentials::UI::UserConsentVerificationResult> - RequestVerificationForWindowAsync(std::wstring localizedReason) override { - auto userConsentVerifierInterop = winrt::get_activation_factory< - Windows::Security::Credentials::UI::UserConsentVerifier, - IUserConsentVerifierInterop>(); - - auto hWnd = get_root_window_(); - - HSTRING hReason; - if (WindowsCreateString(localizedReason.c_str(), - static_cast(localizedReason.size()), - &hReason) != S_OK) { - return Windows::Security::Credentials::UI::UserConsentVerificationResult:: - Canceled; - } - - auto consentResult = - co_await winrt::capture>( - userConsentVerifierInterop, + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localized_reason) override { + winrt::impl::com_ref + user_consent_verifier_interop = winrt::get_activation_factory< + winrt::Windows::Security::Credentials::UI::UserConsentVerifier, + IUserConsentVerifierInterop>(); + + HWND root_window_handle = get_root_window_(); + + auto reason = wil::make_unique_string( + localized_reason.c_str(), localized_reason.size()); + + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult + consent_result = co_await winrt::capture< + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult>>( + user_consent_verifier_interop, &::IUserConsentVerifierInterop::RequestVerificationForWindowAsync, - hWnd, hReason); + root_window_handle, reason.get()); - WindowsDeleteString(hReason); - - return consentResult; + return consent_result; } - Windows::Foundation::IAsyncOperation< - Windows::Security::Credentials::UI::UserConsentVerifierAvailability> + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> CheckAvailabilityAsync() override { - return Windows::Security::Credentials::UI::UserConsentVerifier:: + return winrt::Windows::Security::Credentials::UI::UserConsentVerifier:: CheckAvailabilityAsync(); } @@ -102,10 +103,12 @@ class UserConsentVerifierImpl : public UserConsentVerifier { }; // static -void LocalAuthPlugin::RegisterWithRegistrar(PluginRegistrarWindows* registrar) { - auto channel = std::make_unique>( - registrar->messenger(), "plugins.flutter.io/local_auth_windows", - &StandardMethodCodec::GetInstance()); +void LocalAuthPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "plugins.flutter.io/local_auth_windows", + &flutter::StandardMethodCodec::GetInstance()); auto plugin = std::make_unique( [registrar]() { return GetRootWindow(registrar->GetView()); }); @@ -130,13 +133,15 @@ LocalAuthPlugin::LocalAuthPlugin( LocalAuthPlugin::~LocalAuthPlugin() {} void LocalAuthPlugin::HandleMethodCall( - const MethodCall& method_call, - std::unique_ptr> result) { + const flutter::MethodCall& method_call, + std::unique_ptr> result) { if (method_call.method_name().compare("authenticate") == 0) { Authenticate(method_call, std::move(result)); - } else if (method_call.method_name().compare("getAvailableBiometrics") == 0) { - GetAvailableBiometrics(std::move(result)); - } else if (method_call.method_name().compare("isDeviceSupported") == 0) { + } else if (method_call.method_name().compare("getEnrolledBiometrics") == 0) { + GetEnrolledBiometrics(std::move(result)); + } else if (method_call.method_name().compare("isDeviceSupported") == 0 || + method_call.method_name().compare("deviceSupportsBiometrics") == + 0) { IsDeviceSupported(std::move(result)); } else { result->NotImplemented(); @@ -145,64 +150,66 @@ void LocalAuthPlugin::HandleMethodCall( // Starts authentication process winrt::fire_and_forget LocalAuthPlugin::Authenticate( - const MethodCall& method_call, - std::unique_ptr> result) { - auto reasonW = Utf16FromUtf8(GetArgument( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + std::wstring reason = Utf16FromUtf8(GetArgument( "localizedReason", method_call.arguments(), std::string())); - auto biometricOnly = + bool biometric_only = GetArgument("biometricOnly", method_call.arguments(), false); - if (biometricOnly) { + if (biometric_only) { result->Error("biometricOnlyNotSupported", "Windows doesn't support the biometricOnly parameter."); co_return; } - auto ucvAvailability = - co_await user_consent_verifier_->CheckAvailabilityAsync(); + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); - if (ucvAvailability == - Windows::Security::Credentials::UI::UserConsentVerifierAvailability:: - DeviceNotPresent) { + if (ucv_availability == + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent) { result->Error("NoHardware", "No biometric hardware found"); co_return; - } else if (ucvAvailability == - Windows::Security::Credentials::UI:: + } else if (ucv_availability == + winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability::NotConfiguredForUser) { result->Error("NotEnrolled", "No biometrics enrolled on this device."); co_return; - } else if (ucvAvailability != - Windows::Security::Credentials::UI:: + } else if (ucv_availability != + winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability::Available) { result->Error("NotAvailable", "Required security features not enabled"); co_return; } try { - auto consentResult = - co_await user_consent_verifier_->RequestVerificationForWindowAsync( - reasonW); - - result->Success(EncodableValue( - consentResult == Windows::Security::Credentials::UI:: - UserConsentVerificationResult::Verified)); + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult + consent_result = + co_await user_consent_verifier_->RequestVerificationForWindowAsync( + reason); + + result->Success(flutter::EncodableValue( + consent_result == winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified)); } catch (...) { - result->Success(EncodableValue(false)); + result->Success(flutter::EncodableValue(false)); } } // Returns biometric types available on device -winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( - std::unique_ptr> result) { +winrt::fire_and_forget LocalAuthPlugin::GetEnrolledBiometrics( + std::unique_ptr> result) { try { flutter::EncodableList biometrics; - auto ucvAvailability = - co_await user_consent_verifier_->CheckAvailabilityAsync(); - if (ucvAvailability == Windows::Security::Credentials::UI:: - UserConsentVerifierAvailability::Available) { - biometrics.push_back(EncodableValue("fingerprint")); - biometrics.push_back(EncodableValue("face")); - biometrics.push_back(EncodableValue("iris")); + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + if (ucv_availability == winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + biometrics.push_back(flutter::EncodableValue("weak")); + biometrics.push_back(flutter::EncodableValue("strong")); } result->Success(biometrics); } catch (const std::exception& e) { @@ -212,12 +219,13 @@ winrt::fire_and_forget LocalAuthPlugin::GetAvailableBiometrics( // Returns whether the device supports Windows Hello or not winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( - std::unique_ptr> result) { - auto ucvAvailability = - co_await user_consent_verifier_->CheckAvailabilityAsync(); - result->Success(EncodableValue( - ucvAvailability == Windows::Security::Credentials::UI:: - UserConsentVerifierAvailability::Available)); + std::unique_ptr> result) { + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + result->Success(flutter::EncodableValue( + ucv_availability == winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available)); } } // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp index fa5cdacb3bec..3828b05eef07 100644 --- a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp @@ -21,6 +21,7 @@ namespace local_auth_windows { namespace test { +using flutter::EncodableList; using flutter::EncodableMap; using flutter::EncodableValue; using ::testing::_; @@ -39,7 +40,7 @@ TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierAvailable) { EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> { co_return winrt::Windows::Security::Credentials::UI:: @@ -66,7 +67,7 @@ TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierNotAvailable) { EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> { co_return winrt::Windows::Security::Credentials::UI:: @@ -85,7 +86,7 @@ TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierNotAvailable) { } TEST(LocalAuthPlugin, - GetAvailableBiometricsHandlerReturnEmptyListIfVerifierNotAvailable) { + GetEnrolledBiometricsHandlerReturnEmptyListIfVerifierNotAvailable) { std::unique_ptr result = std::make_unique(); @@ -94,7 +95,7 @@ TEST(LocalAuthPlugin, EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> { co_return winrt::Windows::Security::Credentials::UI:: @@ -107,13 +108,13 @@ TEST(LocalAuthPlugin, EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableList()))); plugin.HandleMethodCall( - flutter::MethodCall("getAvailableBiometrics", + flutter::MethodCall("getEnrolledBiometrics", std::make_unique()), std::move(result)); } TEST(LocalAuthPlugin, - GetAvailableBiometricsHandlerReturnNonEmptyListIfVerifierAvailable) { + GetEnrolledBiometricsHandlerReturnNonEmptyListIfVerifierAvailable) { std::unique_ptr result = std::make_unique(); @@ -122,7 +123,7 @@ TEST(LocalAuthPlugin, EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> { co_return winrt::Windows::Security::Credentials::UI:: @@ -132,12 +133,12 @@ TEST(LocalAuthPlugin, LocalAuthPlugin plugin(std::move(mockConsentVerifier)); EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableList( - {EncodableValue("fingerprint"), - EncodableValue("face"), EncodableValue("iris")})))); + EXPECT_CALL(*result, + SuccessInternal(Pointee(EncodableList( + {EncodableValue("weak"), EncodableValue("strong")})))); plugin.HandleMethodCall( - flutter::MethodCall("getAvailableBiometrics", + flutter::MethodCall("getEnrolledBiometrics", std::make_unique()), std::move(result)); } @@ -173,7 +174,7 @@ TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenAuthorized) { EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> { co_return winrt::Windows::Security::Credentials::UI:: @@ -183,7 +184,7 @@ TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenAuthorized) { EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) .Times(1) .WillOnce([](std::wstring localizedReason) - -> Windows::Foundation::IAsyncOperation< + -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerificationResult> { EXPECT_EQ(localizedReason, L"My Reason"); @@ -215,7 +216,7 @@ TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenNotAuthorized) { EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) .Times(1) - .WillOnce([]() -> Windows::Foundation::IAsyncOperation< + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> { co_return winrt::Windows::Security::Credentials::UI:: @@ -225,7 +226,7 @@ TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenNotAuthorized) { EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) .Times(1) .WillOnce([](std::wstring localizedReason) - -> Windows::Foundation::IAsyncOperation< + -> winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerificationResult> { EXPECT_EQ(localizedReason, L"My Reason"); diff --git a/packages/local_auth/local_auth_windows/windows/test/mocks.h b/packages/local_auth/local_auth_windows/windows/test/mocks.h index e86832eb2749..d82ae801b4b9 100644 --- a/packages/local_auth/local_auth_windows/windows/test/mocks.h +++ b/packages/local_auth/local_auth_windows/windows/test/mocks.h @@ -41,15 +41,15 @@ class MockUserConsentVerifier : public UserConsentVerifier { explicit MockUserConsentVerifier(){}; virtual ~MockUserConsentVerifier() = default; - MOCK_METHOD( - Windows::Foundation::IAsyncOperation< - Windows::Security::Credentials::UI::UserConsentVerificationResult>, - RequestVerificationForWindowAsync, (std::wstring localizedReason), - (override)); - MOCK_METHOD( - Windows::Foundation::IAsyncOperation< - Windows::Security::Credentials::UI::UserConsentVerifierAvailability>, - CheckAvailabilityAsync, (), (override)); + MOCK_METHOD(winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult>, + RequestVerificationForWindowAsync, (std::wstring localizedReason), + (override)); + MOCK_METHOD(winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability>, + CheckAvailabilityAsync, (), (override)); // Disallow copy and move. MockUserConsentVerifier(const MockUserConsentVerifier&) = delete; From 6813b7b1024ece0784cb88daf609c138d9a7e86b Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 12 May 2022 19:04:21 -0700 Subject: [PATCH 16/20] Fixed build --- .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ .../local_auth/local_auth_windows/windows/local_auth.h | 2 +- .../local_auth_windows/windows/local_auth_plugin.cpp | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake index 8dfe35a3460a..ef187dcae56f 100644 --- a/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake +++ b/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST local_auth_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h index 126a037393df..e575a851a26b 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth.h +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -14,10 +14,10 @@ // Include prior to C++/WinRT Headers #include +#include #include #include #include -#include #include #include diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp index 26190cdc623d..b14cd36e7cdf 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -1,7 +1,7 @@ // 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 +#include #include "local_auth.h" From 47823c6dfa683c7d7b56e9979a0ff61a4fdd7a3b Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 12 May 2022 19:15:37 -0700 Subject: [PATCH 17/20] Fixed format. --- .../local_auth_windows/example/lib/main.dart | 51 ++++++++++--------- .../lib/types/auth_messages_windows.dart | 3 +- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart index 22e9eaceaf65..ef26ec5545c5 100644 --- a/packages/local_auth/local_auth_windows/example/lib/main.dart +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -12,39 +12,40 @@ import 'package:local_auth_platform_interface/local_auth_platform_interface.dart import 'package:local_auth_windows/local_auth_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { _SupportState _supportState = _SupportState.unknown; - bool? _canCheckBiometrics; - List? _availableBiometrics; + bool? _deviceSupportsBiometrics; + List? _enrolledBiometrics; String _authorized = 'Not Authorized'; bool _isAuthenticating = false; @override void initState() { super.initState(); - LocalAuthPlatform.instance.isDeviceSupported().then((bool isSupported) { - setState(() { - _supportState = - isSupported ? _SupportState.supported : _SupportState.unsupported; - }); - }); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); } Future _checkBiometrics() async { - late bool canCheckBiometrics; + late bool deviceSupportsBiometrics; try { - canCheckBiometrics = - (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); } on PlatformException catch (e) { - canCheckBiometrics = false; + deviceSupportsBiometrics = false; print(e); } if (!mounted) { @@ -52,7 +53,7 @@ class _MyAppState extends State { } setState(() { - _canCheckBiometrics = canCheckBiometrics; + _deviceSupportsBiometrics = deviceSupportsBiometrics; }); } @@ -70,7 +71,7 @@ class _MyAppState extends State { } setState(() { - _availableBiometrics = availableBiometrics; + _enrolledBiometrics = availableBiometrics; }); } @@ -104,9 +105,8 @@ class _MyAppState extends State { return; } - setState(() { - _authorized = authenticated ? 'Authorized' : 'Not Authorized'; - }); + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); } Future _authenticateWithBiometrics() async { @@ -173,16 +173,17 @@ class _MyAppState extends State { else const Text('This device is not supported'), const Divider(height: 100), - Text('Can check biometrics: $_canCheckBiometrics\n'), + Text( + 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), - Text('Available biometrics: $_availableBiometrics\n'), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -201,6 +202,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -208,9 +210,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -220,7 +222,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart index cd2df3fdd254..2f8b2bc00975 100644 --- a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart +++ b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart @@ -111,6 +111,7 @@ class WindowsAuthMessages extends AuthMessages { @override int get hashCode => Object.hash( + super.hashCode, biometricHint, biometricNotRecognized, biometricRequiredTitle, @@ -186,6 +187,6 @@ String get windowsDeviceCredentialsSetupDescription => /// biometric on their device. String get windowsGoToSettingsDescription => Intl.message( 'Biometric authentication is not set up on your device. Go to ' - '\'Settings > Security\' to add biometric authentication.', + "'Settings > Security' to add biometric authentication.", desc: 'Message advising the user to go to the settings and configure ' 'biometric on their device.'); From 271a49e734514504680efdd62d0eb3ee551dbbb4 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 13 May 2022 14:40:23 -0700 Subject: [PATCH 18/20] Applied more PR feedback. --- .../local_auth_windows/example/README.md | 7 +- .../lib/types/auth_messages_windows.dart | 178 +----------------- .../local_auth_windows/pubspec.yaml | 1 - .../test/local_auth_test.dart | 154 +++------------ 4 files changed, 36 insertions(+), 304 deletions(-) diff --git a/packages/local_auth/local_auth_windows/example/README.md b/packages/local_auth/local_auth_windows/example/README.md index a4a6091c9ba6..8f48b8563cad 100644 --- a/packages/local_auth/local_auth_windows/example/README.md +++ b/packages/local_auth/local_auth_windows/example/README.md @@ -1,8 +1,3 @@ # local_auth_example -Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). +Demonstrates how to use the local_auth plugin. \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart index 2f8b2bc00975..e47e8737153c 100644 --- a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart +++ b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart @@ -3,190 +3,20 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; -import 'package:intl/intl.dart'; import 'package:local_auth_platform_interface/types/auth_messages.dart'; /// Windows side authentication messages. /// /// Provides default values for all messages. +/// +/// Currently unused. @immutable class WindowsAuthMessages extends AuthMessages { /// Constructs a new instance. - const WindowsAuthMessages({ - this.biometricHint, - this.biometricNotRecognized, - this.biometricRequiredTitle, - this.biometricSuccess, - this.cancelButton, - this.deviceCredentialsRequiredTitle, - this.deviceCredentialsSetupDescription, - this.goToSettingsButton, - this.goToSettingsDescription, - this.signInTitle, - }); - - /// Hint message advising the user how to authenticate with biometrics. - /// Maximum 60 characters. - final String? biometricHint; - - /// Message to let the user know that authentication was failed. - /// Maximum 60 characters. - final String? biometricNotRecognized; - - /// Message shown as a title in a dialog which indicates the user - /// has not set up biometric authentication on their device. - /// Maximum 60 characters. - final String? biometricRequiredTitle; - - /// Message to let the user know that authentication was successful. - /// Maximum 60 characters - final String? biometricSuccess; - - /// Message shown on a button that the user can click to leave the - /// current dialog. - /// Maximum 30 characters. - final String? cancelButton; - - /// Message shown as a title in a dialog which indicates the user - /// has not set up credentials authentication on their device. - /// Maximum 60 characters. - final String? deviceCredentialsRequiredTitle; - - /// Message advising the user to go to the settings and configure - /// device credentials on their device. - final String? deviceCredentialsSetupDescription; - - /// Message shown on a button that the user can click to go to settings pages - /// from the current dialog. - /// Maximum 30 characters. - final String? goToSettingsButton; - - /// Message advising the user to go to the settings and configure - /// biometric on their device. - final String? goToSettingsDescription; - - /// Message shown as a title in a dialog which indicates the user - /// that they need to scan biometric to continue. - /// Maximum 60 characters. - final String? signInTitle; + const WindowsAuthMessages(); @override Map get args { - return { - 'biometricHint': biometricHint ?? windowsBiometricHint, - 'biometricNotRecognized': - biometricNotRecognized ?? windowsBiometricNotRecognized, - 'biometricSuccess': biometricSuccess ?? windowsBiometricSuccess, - 'biometricRequired': - biometricRequiredTitle ?? windowsBiometricRequiredTitle, - 'cancelButton': cancelButton ?? windowsCancelButton, - 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ?? - windowsDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ?? - windowsDeviceCredentialsSetupDescription, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescription': - goToSettingsDescription ?? windowsGoToSettingsDescription, - 'signInTitle': signInTitle ?? windowsSignInTitle, - }; + return {}; } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is WindowsAuthMessages && - runtimeType == other.runtimeType && - biometricHint == other.biometricHint && - biometricNotRecognized == other.biometricNotRecognized && - biometricRequiredTitle == other.biometricRequiredTitle && - biometricSuccess == other.biometricSuccess && - cancelButton == other.cancelButton && - deviceCredentialsRequiredTitle == - other.deviceCredentialsRequiredTitle && - deviceCredentialsSetupDescription == - other.deviceCredentialsSetupDescription && - goToSettingsButton == other.goToSettingsButton && - goToSettingsDescription == other.goToSettingsDescription && - signInTitle == other.signInTitle; - - @override - int get hashCode => Object.hash( - super.hashCode, - biometricHint, - biometricNotRecognized, - biometricRequiredTitle, - biometricSuccess, - cancelButton, - deviceCredentialsRequiredTitle, - deviceCredentialsSetupDescription, - goToSettingsButton, - goToSettingsDescription, - signInTitle); } - -// Default strings for WindowsAuthMessages. Currently supports English. -// Intl.message must be string literals. - -/// Message shown on a button that the user can click to go to settings pages -/// from the current dialog. -String get goToSettings => Intl.message('Go to settings', - desc: 'Message shown on a button that the user can click to go to ' - 'settings pages from the current dialog. Maximum 30 characters.'); - -/// Hint message advising the user how to authenticate with biometrics. -String get windowsBiometricHint => Intl.message('Verify identity', - desc: 'Hint message advising the user how to authenticate with biometrics. ' - 'Maximum 60 characters.'); - -/// Message to let the user know that authentication was failed. -String get windowsBiometricNotRecognized => - Intl.message('Not recognized. Try again.', - desc: 'Message to let the user know that authentication was failed. ' - 'Maximum 60 characters.'); - -/// Message to let the user know that authentication was successful. It -String get windowsBiometricSuccess => Intl.message('Success', - desc: 'Message to let the user know that authentication was successful. ' - 'Maximum 60 characters.'); - -/// Message shown on a button that the user can click to leave the -/// current dialog. -String get windowsCancelButton => Intl.message('Cancel', - desc: 'Message shown on a button that the user can click to leave the ' - 'current dialog. Maximum 30 characters.'); - -/// Message shown as a title in a dialog which indicates the user -/// that they need to scan biometric to continue. -String get windowsSignInTitle => Intl.message('Authentication required', - desc: 'Message shown as a title in a dialog which indicates the user ' - 'that they need to scan biometric to continue. Maximum 60 characters.'); - -/// Message shown as a title in a dialog which indicates the user -/// has not set up biometric authentication on their device. -String get windowsBiometricRequiredTitle => Intl.message('Biometric required', - desc: 'Message shown as a title in a dialog which indicates the user ' - 'has not set up biometric authentication on their device. ' - 'Maximum 60 characters.'); - -/// Message shown as a title in a dialog which indicates the user -/// has not set up credentials authentication on their device. -String get windowsDeviceCredentialsRequiredTitle => - Intl.message('Device credentials required', - desc: 'Message shown as a title in a dialog which indicates the user ' - 'has not set up credentials authentication on their device. ' - 'Maximum 60 characters.'); - -/// Message advising the user to go to the settings and configure -/// device credentials on their device. -String get windowsDeviceCredentialsSetupDescription => - Intl.message('Device credentials required', - desc: 'Message advising the user to go to the settings and configure ' - 'device credentials on their device.'); - -/// Message advising the user to go to the settings and configure -/// biometric on their device. -String get windowsGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Go to ' - "'Settings > Security' to add biometric authentication.", - desc: 'Message advising the user to go to the settings and configure ' - 'biometric on their device.'); diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml index 6787f8f94d87..9edeffbb6530 100644 --- a/packages/local_auth/local_auth_windows/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -19,7 +19,6 @@ flutter: dependencies: flutter: sdk: flutter - intl: ^0.17.0 local_auth_platform_interface: ^1.0.0 dev_dependencies: diff --git a/packages/local_auth/local_auth_windows/test/local_auth_test.dart b/packages/local_auth/local_auth_windows/test/local_auth_test.dart index 0b0e188686bb..b11c19e7b339 100644 --- a/packages/local_auth/local_auth_windows/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_windows/test/local_auth_test.dart @@ -9,7 +9,7 @@ import 'package:local_auth_windows/local_auth_windows.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('LocalAuth', () { + group('authenticate', () { const MethodChannel channel = MethodChannel( 'plugins.flutter.io/local_auth_windows', ); @@ -31,141 +31,49 @@ void main() { log.clear(); }); - test('deviceSupportsBiometrics calls platform', () async { - final bool result = await localAuthentication.deviceSupportsBiometrics(); - + test('authenticate with no arguments passes expected defaults', () async { + await localAuthentication.authenticate( + authMessages: [const WindowsAuthMessages()], + localizedReason: 'My localized reason'); expect( log, [ - isMethodCall('deviceSupportsBiometrics', arguments: null), + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'My localized reason', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const WindowsAuthMessages().args)), ], ); - expect(result, true); }); - test('getEnrolledBiometrics calls platform', () async { - final List result = - await localAuthentication.getEnrolledBiometrics(); - - expect( - log, - [ - isMethodCall('getEnrolledBiometrics', arguments: null), - ], + test('authenticate passes all options.', () async { + await localAuthentication.authenticate( + authMessages: [const WindowsAuthMessages()], + localizedReason: 'My localized reason', + options: const AuthenticationOptions( + useErrorDialogs: false, + stickyAuth: true, + sensitiveTransaction: false, + biometricOnly: true, + ), ); - expect(result, [ - BiometricType.weak, - BiometricType.strong, - ]); - }); - - test('isDeviceSupported calls platform', () async { - await localAuthentication.isDeviceSupported(); expect( log, [ - isMethodCall('isDeviceSupported', arguments: null), + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'My localized reason', + 'useErrorDialogs': false, + 'stickyAuth': true, + 'sensitiveTransaction': false, + 'biometricOnly': true, + }..addAll(const WindowsAuthMessages().args)), ], ); }); - - test('stopAuthentication returns false', () async { - final bool result = await localAuthentication.stopAuthentication(); - expect(result, false); - }); - - group('With device auth fail over', () { - test('authenticate with no args.', () async { - await localAuthentication.authenticate( - authMessages: [const WindowsAuthMessages()], - localizedReason: 'Needs secure', - options: const AuthenticationOptions(biometricOnly: true), - ); - expect( - log, - [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - }..addAll(const WindowsAuthMessages().args)), - ], - ); - }); - - test('authenticate with no sensitive transaction.', () async { - await localAuthentication.authenticate( - authMessages: [const WindowsAuthMessages()], - localizedReason: 'Insecure', - options: const AuthenticationOptions( - sensitiveTransaction: false, - useErrorDialogs: false, - biometricOnly: true, - ), - ); - expect( - log, - [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': true, - }..addAll(const WindowsAuthMessages().args)), - ], - ); - }); - }); - - group('With biometrics only', () { - test('authenticate with no args.', () async { - await localAuthentication.authenticate( - authMessages: [const WindowsAuthMessages()], - localizedReason: 'Needs secure', - ); - expect( - log, - [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - }..addAll(const WindowsAuthMessages().args)), - ], - ); - }); - - test('authenticate with no sensitive transaction.', () async { - await localAuthentication.authenticate( - authMessages: [const WindowsAuthMessages()], - localizedReason: 'Insecure', - options: const AuthenticationOptions( - sensitiveTransaction: false, - useErrorDialogs: false, - ), - ); - expect( - log, - [ - isMethodCall('authenticate', - arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': false, - }..addAll(const WindowsAuthMessages().args)), - ], - ); - }); - }); }); } From e1e24a6891810429b1fadfe249eeeddc6634c98f Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 13 May 2022 15:13:40 -0700 Subject: [PATCH 19/20] Added C++ comments. --- .../local_auth_windows/windows/local_auth.h | 17 +++++++++++++++++ .../windows/local_auth_plugin.cpp | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h index e575a851a26b..211419b4beda 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth.h +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -25,14 +25,21 @@ namespace local_auth_windows { +// Abstract class that is used to determine whether a user +// has given consent to a particular action, and if the system +// supports asking this question. class UserConsentVerifier { public: UserConsentVerifier() {} virtual ~UserConsentVerifier() = default; + // Abstract method that request the user's verification + // given the provided reason. virtual winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> RequestVerificationForWindowAsync(std::wstring localized_reason) = 0; + + // Abstract method that returns weather the system supports Windows Hello. virtual winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> @@ -47,7 +54,12 @@ class LocalAuthPlugin : public flutter::Plugin { public: static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + // Real implementation that creates a plugin instance that will create the + // dialog and associate it with the HWND returned from the provided function LocalAuthPlugin(std::function window_provider); + + // Creates a plugin instance with the given UserConsentVerifier instance. + // Exists for unit testing with mock implementations. LocalAuthPlugin(std::unique_ptr user_consent_verifier); // Called when a method is called on this plugin's channel from Dart. @@ -60,11 +72,16 @@ class LocalAuthPlugin : public flutter::Plugin { private: std::unique_ptr user_consent_verifier_; + // Starts authentication process winrt::fire_and_forget Authenticate( const flutter::MethodCall& method_call, std::unique_ptr> result); + + // Returns enrolled biometric types available on device. winrt::fire_and_forget GetEnrolledBiometrics( std::unique_ptr> result); + + // Returns whether the system supports Windows Hello. winrt::fire_and_forget IsDeviceSupported( std::unique_ptr> result); }; diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp index b14cd36e7cdf..1de483b2c2c4 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -54,12 +54,16 @@ std::wstring Utf16FromUtf8(const std::string& utf8_string) { namespace local_auth_windows { +// Real implementation of the UserConsentVerifier that +// calls the native Windows APIs to get the user's consent class UserConsentVerifierImpl : public UserConsentVerifier { public: explicit UserConsentVerifierImpl(std::function window_provider) : get_root_window_(std::move(window_provider)){}; virtual ~UserConsentVerifierImpl() = default; + // Calls the native Windows API to get the user's consent + // with the provided reason. winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> RequestVerificationForWindowAsync(std::wstring localized_reason) override { @@ -85,6 +89,7 @@ class UserConsentVerifierImpl : public UserConsentVerifier { return consent_result; } + // Calls the native Windows API to check for the Windows Hello availability winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> From cabc43ed60c0b0f8e7db6f7d59b681414533d4b5 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Fri, 13 May 2022 15:36:30 -0700 Subject: [PATCH 20/20] Small doc fixes. --- .../local_auth_windows/windows/local_auth.h | 8 ++++---- .../windows/local_auth_plugin.cpp | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h index 211419b4beda..94b91f88345a 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth.h +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -54,15 +54,15 @@ class LocalAuthPlugin : public flutter::Plugin { public: static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); - // Real implementation that creates a plugin instance that will create the - // dialog and associate it with the HWND returned from the provided function + // Creates a plugin instance that will create the dialog and associate + // it with the HWND returned from the provided function. LocalAuthPlugin(std::function window_provider); // Creates a plugin instance with the given UserConsentVerifier instance. // Exists for unit testing with mock implementations. LocalAuthPlugin(std::unique_ptr user_consent_verifier); - // Called when a method is called on this plugin's channel from Dart. + // Handles method calls from Dart on this plugin's channel. void HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result); @@ -72,7 +72,7 @@ class LocalAuthPlugin : public flutter::Plugin { private: std::unique_ptr user_consent_verifier_; - // Starts authentication process + // Starts authentication process. winrt::fire_and_forget Authenticate( const flutter::MethodCall& method_call, std::unique_ptr> result); diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp index 1de483b2c2c4..7a25abb53010 100644 --- a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -8,7 +8,7 @@ namespace { template -// Helper method for getting an argument from an EncodableValue +// Helper method for getting an argument from an EncodableValue. T GetArgument(const std::string arg, const flutter::EncodableValue* args, T fallback) { T result{fallback}; @@ -22,7 +22,7 @@ T GetArgument(const std::string arg, const flutter::EncodableValue* args, return result; } -// Returns the window's HWND for a given FlutterView +// Returns the window's HWND for a given FlutterView. HWND GetRootWindow(flutter::FlutterView* view) { return ::GetAncestor(view->GetNativeWindow(), GA_ROOT); } @@ -54,8 +54,8 @@ std::wstring Utf16FromUtf8(const std::string& utf8_string) { namespace local_auth_windows { -// Real implementation of the UserConsentVerifier that -// calls the native Windows APIs to get the user's consent +// Creates an instance of the UserConsentVerifier that +// calls the native Windows APIs to get the user's consent. class UserConsentVerifierImpl : public UserConsentVerifier { public: explicit UserConsentVerifierImpl(std::function window_provider) @@ -89,7 +89,7 @@ class UserConsentVerifierImpl : public UserConsentVerifier { return consent_result; } - // Calls the native Windows API to check for the Windows Hello availability + // Calls the native Windows API to check for the Windows Hello availability. winrt::Windows::Foundation::IAsyncOperation< winrt::Windows::Security::Credentials::UI:: UserConsentVerifierAvailability> @@ -126,7 +126,7 @@ void LocalAuthPlugin::RegisterWithRegistrar( registrar->AddPlugin(std::move(plugin)); } -// Default constructor for LocalAuthPlugin +// Default constructor for LocalAuthPlugin. LocalAuthPlugin::LocalAuthPlugin(std::function window_provider) : user_consent_verifier_(std::make_unique( std::move(window_provider))) {} @@ -153,7 +153,7 @@ void LocalAuthPlugin::HandleMethodCall( } } -// Starts authentication process +// Starts authentication process. winrt::fire_and_forget LocalAuthPlugin::Authenticate( const flutter::MethodCall& method_call, std::unique_ptr> result) { @@ -203,7 +203,7 @@ winrt::fire_and_forget LocalAuthPlugin::Authenticate( } } -// Returns biometric types available on device +// Returns biometric types available on device. winrt::fire_and_forget LocalAuthPlugin::GetEnrolledBiometrics( std::unique_ptr> result) { try { @@ -222,7 +222,7 @@ winrt::fire_and_forget LocalAuthPlugin::GetEnrolledBiometrics( } } -// Returns whether the device supports Windows Hello or not +// Returns whether the device supports Windows Hello or not. winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( std::unique_ptr> result) { winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability