tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DesktopLauncherTest.cpp (5282B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 #include "gtest/gtest.h"
      5 #include <windows.h>
      6 #include <string>
      7 #include <optional>
      8 #include <shellapi.h>
      9 
     10 #include "find_firefox.h"
     11 #include "tempfile_name.h"
     12 #include "download_firefox.h"
     13 #include "data_sink.h"
     14 #include "file_sink.h"
     15 
     16 static std::optional<std::wstring> get_value_from_key(
     17    const wchar_t* key_path, const wchar_t* value_path) {
     18  // First we need to get the current version of Firefox
     19  wchar_t buffer[MAX_PATH]{};
     20  DWORD buffer_size = sizeof buffer;
     21  LSTATUS status = RegGetValueW(HKEY_CURRENT_USER, key_path, value_path,
     22                                RRF_RT_REG_SZ, nullptr, buffer, &buffer_size);
     23  if (status != ERROR_SUCCESS) {
     24    return std::nullopt;
     25  }
     26  return std::wstring(buffer);
     27 }
     28 
     29 static bool set_value_for_key(const wchar_t* key_path,
     30                              const wchar_t* value_path,
     31                              const std::wstring& value) {
     32  LSTATUS status = RegSetKeyValueW(HKEY_CURRENT_USER, key_path, value_path,
     33                                   REG_SZ, value.c_str(), value.size() * 2 + 2);
     34  return status == ERROR_SUCCESS;
     35 }
     36 
     37 static bool clear_value_for_key(const wchar_t* key_path,
     38                                const wchar_t* value_path) {
     39  LSTATUS status = RegDeleteKeyValueW(HKEY_CURRENT_USER, key_path, value_path);
     40  return status == ERROR_SUCCESS;
     41 }
     42 
     43 static bool delete_key(const wchar_t* key_path) {
     44  LSTATUS status = RegDeleteKeyW(HKEY_CURRENT_USER, key_path);
     45  return status == ERROR_SUCCESS;
     46 }
     47 
     48 class DesktopLauncherTest : public ::testing::Test {
     49 protected:
     50  HANDLE serverProcessHandle = nullptr;
     51  std::optional<std::wstring> savedFirefoxVersion = std::nullopt;
     52  std::optional<std::wstring> savedFirefoxPath = std::nullopt;
     53  std::wstring base_key = getFirefoxRegistryBranding();
     54  std::wstring test_version = L"test.0.0.0.0";
     55  std::wstring test_path = L"This is a test";
     56  std::wstring test_base = base_key + L"\\" + test_version;
     57  std::wstring test_subkey = test_base + L"\\Main";
     58 
     59  void SetUp() override {
     60    savedFirefoxVersion =
     61        get_value_from_key(base_key.c_str(), L"CurrentVersion");
     62    if (savedFirefoxVersion.has_value()) {
     63      std::wstring subkey = base_key + L"\\" + savedFirefoxVersion.value();
     64      savedFirefoxPath = get_value_from_key(subkey.c_str(), L"PathToExe");
     65    }
     66    set_value_for_key(base_key.c_str(), L"CurrentVersion", test_version);
     67    set_value_for_key(test_subkey.c_str(), L"PathToExe", test_path);
     68    // Override values
     69  }
     70 
     71  void TearDown() override {
     72    if (savedFirefoxVersion.has_value()) {
     73      set_value_for_key(base_key.c_str(), L"CurrentVersion",
     74                        savedFirefoxVersion.value());
     75    } else {
     76      clear_value_for_key(base_key.c_str(), L"CurrentVersion");
     77    }
     78    delete_key(test_subkey.c_str());
     79    delete_key(test_base.c_str());
     80  }
     81 };
     82 
     83 TEST_F(DesktopLauncherTest, FirefoxPathTest) {
     84  std::optional<std::wstring> path = lookupFirefoxPath();
     85  ASSERT_TRUE(path.has_value());
     86  ASSERT_EQ(path.value(), test_path);
     87 }
     88 
     89 TEST_F(DesktopLauncherTest, TempFileNameTest) {
     90  std::optional<std::wstring> path = get_tempfile_name();
     91  ASSERT_TRUE(path.has_value());
     92  ASSERT_TRUE(path.value().find(std::wstring(L".exe")) > 0);
     93  ASSERT_TRUE(path.value().find(std::wstring(L":\\")) > 0);
     94 }
     95 
     96 TEST_F(DesktopLauncherTest, TestGetObjectName) {
     97  std::optional<std::wstring> objectName = get_object_name();
     98  ASSERT_TRUE(objectName.has_value());
     99  // The object name is a path, not a URL.
    100  ASSERT_EQ(std::wstring::npos,
    101            objectName.value().find(L"https://download.mozilla.org/"));
    102  ASSERT_NE(std::wstring::npos, objectName.value().find(L"lang="));
    103  ASSERT_NE(std::wstring::npos, objectName.value().find(L"product="));
    104 }
    105 
    106 TEST_F(DesktopLauncherTest, TestTempFileNamesAreDifferent) {
    107  std::wstring temp1 = get_tempfile_name().value();
    108  std::wstring temp2 = get_tempfile_name().value();
    109  ASSERT_NE(temp1, temp2);
    110 }
    111 
    112 TEST_F(DesktopLauncherTest, TestFileSinkFreeze) {
    113  std::wstring path = get_tempfile_name().value();
    114 
    115  auto delete_expecting_error = [](const wchar_t* path, DWORD expected) {
    116    SetLastError(ERROR_SUCCESS);
    117    BOOL result = DeleteFileW(path);
    118    DWORD lastError = GetLastError();
    119    ASSERT_EQ(result, expected == ERROR_SUCCESS);
    120    ASSERT_EQ(lastError, expected);
    121  };
    122 
    123  char data[] = "important data";
    124 
    125  {
    126    FileSink sink;
    127    delete_expecting_error(path.c_str(), ERROR_FILE_NOT_FOUND);
    128    ASSERT_TRUE(sink.open(path));
    129    delete_expecting_error(path.c_str(), ERROR_SHARING_VIOLATION);
    130    ASSERT_TRUE(sink.accept(data, sizeof(data) - 1));
    131    delete_expecting_error(path.c_str(), ERROR_SHARING_VIOLATION);
    132    ASSERT_TRUE(sink.freeze());
    133    delete_expecting_error(path.c_str(), ERROR_SHARING_VIOLATION);
    134    ASSERT_FALSE(sink.accept(data, sizeof(data) - 1));
    135 
    136    // TODO: Automatically test that the path can be run with ShellExecuteEx
    137    // or similar.
    138    //
    139    // To test this manually, run the launcher without Firefox installed. You
    140    // should see a prompt to install Firefox; if so, then it was executable.
    141  }
    142  delete_expecting_error(path.c_str(), ERROR_SUCCESS);
    143 }