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 }