tor-browser

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

TestGetFilesHelper.cpp (10851B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include <thread>
      7 
      8 #include "SpecialSystemDirectory.h"
      9 #include "gtest/gtest.h"
     10 #include "mozilla/ErrorResult.h"
     11 #include "mozilla/SpinEventLoopUntil.h"
     12 #include "mozilla/dom/BlobImpl.h"
     13 #include "mozilla/dom/Directory.h"
     14 #include "mozilla/dom/File.h"
     15 #include "mozilla/dom/GetFilesHelper.h"
     16 #include "mozilla/dom/UnionTypes.h"
     17 #include "mozilla/gtest/MozAssertions.h"
     18 #include "mozilla/media/MediaUtils.h"
     19 #include "nsIFile.h"
     20 
     21 using namespace mozilla;
     22 using namespace mozilla::dom;
     23 
     24 nsCOMPtr<nsIFile> MakeFileFromPathSegments(
     25    const nsTArray<const char*>& aPathSegments) {
     26  nsCOMPtr<nsIFile> file;
     27  MOZ_ALWAYS_SUCCEEDS(GetSpecialSystemDirectory(OS_CurrentWorkingDirectory,
     28                                                getter_AddRefs(file)));
     29  for (const auto* pathSegment : aPathSegments) {
     30    MOZ_ALWAYS_SUCCEEDS(
     31        file->AppendRelativePath(NS_ConvertASCIItoUTF16(pathSegment)));
     32  }
     33  return file;
     34 }
     35 
     36 nsresult AppendFileOrDirectory(nsTArray<OwningFileOrDirectory>& aDirectories,
     37                               const nsTArray<const char*>& aPathSegments) {
     38  bool exists = false;
     39  int retryCount = 5;
     40  nsCOMPtr<nsIFile> file = MakeFileFromPathSegments(aPathSegments);
     41  while (retryCount--) {
     42    if (NS_SUCCEEDED(file->Exists(&exists))) {
     43      break;
     44    }
     45    // May require retrying (bug 1963029)
     46    std::this_thread::sleep_for(std::chrono::milliseconds(100));
     47  }
     48  NS_ENSURE_TRUE(exists, NS_ERROR_FILE_NOT_FOUND);
     49  bool isDir;
     50  MOZ_ALWAYS_SUCCEEDS(file->IsDirectory(&isDir));
     51  if (isDir) {
     52    // We just need to iterate over the directory, so use the junk scope
     53    RefPtr<Directory> directory =
     54        Directory::Create(xpc::NativeGlobal(xpc::PrivilegedJunkScope()), file);
     55    NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
     56 
     57    OwningFileOrDirectory* owningDirectory = aDirectories.EmplaceBack();
     58    owningDirectory->SetAsDirectory() = directory;
     59  } else {
     60    OwningFileOrDirectory* owningDirectory = aDirectories.EmplaceBack();
     61    RefPtr<File> fileObject = File::CreateFromFile(
     62        xpc::NativeGlobal(xpc::PrivilegedJunkScope()), file);
     63    owningDirectory->SetAsFile() = fileObject;
     64  }
     65  return NS_OK;
     66 }
     67 
     68 struct BoolStruct {
     69  bool mValue = false;
     70 };
     71 
     72 class FilesCallback : public GetFilesCallback {
     73 public:
     74  FilesCallback(std::atomic<bool>& aGotResponse,
     75                const nsTArray<nsString>& aExpectedPaths)
     76      : mGotResponse(aGotResponse), mExpectedPaths(aExpectedPaths.Clone()) {}
     77 
     78  // -------------------
     79  // GetFilesCallback
     80  // -------------------
     81  void Callback(nsresult aStatus,
     82                const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override {
     83    EXPECT_EQ(aBlobImpls.Length(), mExpectedPaths.Length());
     84    for (const auto& blob : aBlobImpls) {
     85      nsString path;
     86      ErrorResult error;
     87      blob->GetMozFullPathInternal(path, error);
     88      ASSERT_EQ(error.StealNSResult(), NS_OK);
     89      ASSERT_TRUE(mExpectedPaths.Contains(path));
     90    }
     91    mGotResponse = true;
     92  }
     93 
     94 private:
     95  std::atomic<bool>& mGotResponse;
     96  nsTArray<nsString> mExpectedPaths;
     97 };
     98 
     99 nsTArray<nsString> GetExpectedPaths(
    100    const nsTArray<nsTArray<const char*>>& aPathSegmentsArray) {
    101  nsTArray<nsString> expectedPaths(aPathSegmentsArray.Length());
    102  for (const auto& pathSegments : aPathSegmentsArray) {
    103    auto file = MakeFileFromPathSegments(pathSegments);
    104    nsString expectedPath;
    105    MOZ_ALWAYS_SUCCEEDS(file->GetPath(expectedPath));
    106    expectedPaths.AppendElement(expectedPath);
    107  }
    108  return expectedPaths;
    109 }
    110 
    111 void ExpectGetFilesHelperResponse(
    112    RefPtr<GetFilesHelper> aHelper,
    113    const nsTArray<nsTArray<const char*>>& aPathSegmentsArray) {
    114  nsTArray<nsString> expectedPaths = GetExpectedPaths(aPathSegmentsArray);
    115 
    116  std::atomic<bool> gotCallbackResponse = false;
    117  std::atomic<bool> gotMozPromiseResponse = false;
    118  RefPtr<FilesCallback> callback =
    119      MakeRefPtr<FilesCallback>(gotCallbackResponse, expectedPaths);
    120  aHelper->AddCallback(callback);
    121  auto mozPromise = MakeRefPtr<GetFilesHelper::MozPromiseType>(__func__);
    122  aHelper->AddMozPromise(mozPromise,
    123                         xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
    124  mozPromise->Then(
    125      GetMainThreadSerialEventTarget(), __func__,
    126      [&gotMozPromiseResponse,
    127       &expectedPaths](const nsTArray<RefPtr<mozilla::dom::File>>& aFiles) {
    128        EXPECT_EQ(aFiles.Length(), expectedPaths.Length());
    129        for (const auto& file : aFiles) {
    130          nsString path;
    131          ErrorResult error;
    132          file->GetMozFullPathInternal(path, error);
    133          ASSERT_EQ(error.StealNSResult(), NS_OK);
    134          ASSERT_TRUE(expectedPaths.Contains(path));
    135        }
    136        gotMozPromiseResponse = true;
    137      },
    138      []() { FAIL() << "MozPromise got rejected!"; });
    139  // Make timedOut a RefPtr so if we get a response after this function
    140  // has finished we can safely check that (and don't start accessing stack
    141  // values that don't exist anymore)
    142  RefPtr timedOut = MakeRefPtr<media::Refcountable<BoolStruct>>();
    143 
    144  RefPtr<CancelableRunnable> timer =
    145      NS_NewCancelableRunnableFunction("GetFilesHelper timeout", [&] {
    146        if (!gotCallbackResponse.load() || !gotMozPromiseResponse.load()) {
    147          timedOut->mValue = true;
    148        }
    149      });
    150  constexpr uint32_t kTimeout = 10000;
    151  NS_DelayedDispatchToCurrentThread(do_AddRef(timer), kTimeout);
    152  mozilla::SpinEventLoopUntil(
    153      "Waiting for GetFilesHelper result"_ns, [&, timedOut]() {
    154        return (gotCallbackResponse.load() && gotMozPromiseResponse.load()) ||
    155               timedOut->mValue;
    156      });
    157  timer->Cancel();
    158  EXPECT_TRUE(gotCallbackResponse);
    159  EXPECT_TRUE(gotMozPromiseResponse);
    160  EXPECT_FALSE(timedOut->mValue);
    161 }
    162 
    163 TEST(GetFilesHelper, TestSingleDirectory)
    164 {
    165  nsTArray<OwningFileOrDirectory> directories;
    166  ASSERT_NS_SUCCEEDED(
    167      AppendFileOrDirectory(directories, {"getfiles", "inner2"}));
    168 
    169  ErrorResult error;
    170  RefPtr<GetFilesHelper> helper =
    171      GetFilesHelper::Create(directories, true, error);
    172  ASSERT_EQ(error.StealNSResult(), NS_OK);
    173 
    174  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    175  pathSegmentsArray.AppendElement(
    176      nsTArray{"getfiles", "inner2", "fileinner2.txt"});
    177  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    178 }
    179 
    180 TEST(GetFilesHelper, TestSingleNestedDirectory)
    181 {
    182  nsTArray<OwningFileOrDirectory> directories;
    183  ASSERT_NS_SUCCEEDED(
    184      AppendFileOrDirectory(directories, {"getfiles", "inner1"}));
    185 
    186  ErrorResult error;
    187  RefPtr<GetFilesHelper> helper =
    188      GetFilesHelper::Create(directories, true, error);
    189  ASSERT_EQ(error.StealNSResult(), NS_OK);
    190 
    191  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    192  pathSegmentsArray.AppendElement(
    193      nsTArray{"getfiles", "inner1", "fileinner1.txt"});
    194  pathSegmentsArray.AppendElement(
    195      nsTArray{"getfiles", "inner1", "inner", "fileinnerinner1.txt"});
    196  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    197 }
    198 
    199 TEST(GetFilesHelper, TestSingleNestedDirectoryNoRecursion)
    200 {
    201  nsTArray<OwningFileOrDirectory> directories;
    202  ASSERT_NS_SUCCEEDED(
    203      AppendFileOrDirectory(directories, {"getfiles", "inner1"}));
    204 
    205  ErrorResult error;
    206  RefPtr<GetFilesHelper> helper =
    207      GetFilesHelper::Create(directories, false, error);
    208  ASSERT_EQ(error.StealNSResult(), NS_OK);
    209 
    210  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    211  pathSegmentsArray.AppendElement(
    212      nsTArray{"getfiles", "inner1", "fileinner1.txt"});
    213  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    214 }
    215 
    216 TEST(GetFilesHelper, TestSingleDirectoryWithMultipleNestedChildren)
    217 {
    218  nsTArray<OwningFileOrDirectory> directories;
    219  ASSERT_NS_SUCCEEDED(AppendFileOrDirectory(directories, {"getfiles"}));
    220 
    221  ErrorResult error;
    222  RefPtr<GetFilesHelper> helper =
    223      GetFilesHelper::Create(directories, true, error);
    224  ASSERT_EQ(error.StealNSResult(), NS_OK);
    225 
    226  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    227  pathSegmentsArray.AppendElement(nsTArray{"getfiles", "file1.txt"});
    228  pathSegmentsArray.AppendElement(nsTArray{"getfiles", "file2.txt"});
    229  pathSegmentsArray.AppendElement(
    230      nsTArray{"getfiles", "inner1", "fileinner1.txt"});
    231  pathSegmentsArray.AppendElement(
    232      nsTArray{"getfiles", "inner1", "inner", "fileinnerinner1.txt"});
    233  pathSegmentsArray.AppendElement(
    234      nsTArray{"getfiles", "inner2", "fileinner2.txt"});
    235  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    236 }
    237 
    238 TEST(GetFilesHelper, TestSingleFile)
    239 {
    240  nsTArray<OwningFileOrDirectory> directories;
    241  ASSERT_NS_SUCCEEDED(AppendFileOrDirectory(
    242      directories, {"getfiles", "inner1", "fileinner1.txt"}));
    243 
    244  ErrorResult error;
    245  RefPtr<GetFilesHelper> helper =
    246      GetFilesHelper::Create(directories, true, error);
    247  ASSERT_EQ(error.StealNSResult(), NS_OK);
    248 
    249  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    250  pathSegmentsArray.AppendElement(
    251      nsTArray{"getfiles", "inner1", "fileinner1.txt"});
    252  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    253 }
    254 
    255 TEST(GetFilesHelper, TestMultipleFiles)
    256 {
    257  nsTArray<OwningFileOrDirectory> directories;
    258  ASSERT_NS_SUCCEEDED(AppendFileOrDirectory(
    259      directories, {"getfiles", "inner1", "fileinner1.txt"}));
    260  ASSERT_NS_SUCCEEDED(AppendFileOrDirectory(
    261      directories, {"getfiles", "inner2", "fileinner2.txt"}));
    262 
    263  ErrorResult error;
    264  RefPtr<GetFilesHelper> helper =
    265      GetFilesHelper::Create(directories, true, error);
    266  ASSERT_EQ(error.StealNSResult(), NS_OK);
    267 
    268  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    269  pathSegmentsArray.AppendElement(
    270      nsTArray{"getfiles", "inner1", "fileinner1.txt"});
    271  pathSegmentsArray.AppendElement(
    272      nsTArray{"getfiles", "inner2", "fileinner2.txt"});
    273  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    274 }
    275 
    276 // Content Analysis can use GetFilesHelper with multiple directories,
    277 // so make sure that works.
    278 TEST(GetFilesHelper, TestMultipleDirectories)
    279 {
    280  nsTArray<OwningFileOrDirectory> directories;
    281  ASSERT_NS_SUCCEEDED(
    282      AppendFileOrDirectory(directories, {"getfiles", "inner1"}));
    283  ASSERT_NS_SUCCEEDED(
    284      AppendFileOrDirectory(directories, {"getfiles", "inner2"}));
    285 
    286  ErrorResult error;
    287  RefPtr<GetFilesHelper> helper =
    288      GetFilesHelper::Create(directories, true, error);
    289  ASSERT_EQ(error.StealNSResult(), NS_OK);
    290 
    291  nsTArray<nsTArray<const char*>> pathSegmentsArray;
    292  pathSegmentsArray.AppendElement(
    293      nsTArray{"getfiles", "inner1", "fileinner1.txt"});
    294  pathSegmentsArray.AppendElement(
    295      nsTArray{"getfiles", "inner1", "inner", "fileinnerinner1.txt"});
    296  pathSegmentsArray.AppendElement(
    297      nsTArray{"getfiles", "inner2", "fileinner2.txt"});
    298  ExpectGetFilesHelperResponse(helper, pathSegmentsArray);
    299 }