MultiDiskAdaptorTest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. #include "MultiDiskAdaptor.h"
  2. #include <string>
  3. #include <cerrno>
  4. #include <cstring>
  5. #include <cppunit/extensions/HelperMacros.h>
  6. #include "FileEntry.h"
  7. #include "Exception.h"
  8. #include "a2io.h"
  9. #include "array_fun.h"
  10. #include "TestUtil.h"
  11. #include "DiskWriter.h"
  12. #include "WrDiskCacheEntry.h"
  13. namespace aria2 {
  14. class MultiDiskAdaptorTest : public CppUnit::TestFixture {
  15. CPPUNIT_TEST_SUITE(MultiDiskAdaptorTest);
  16. CPPUNIT_TEST(testWriteData);
  17. CPPUNIT_TEST(testReadData);
  18. CPPUNIT_TEST(testCutTrailingGarbage);
  19. CPPUNIT_TEST(testSize);
  20. CPPUNIT_TEST(testUtime);
  21. CPPUNIT_TEST(testResetDiskWriterEntries);
  22. CPPUNIT_TEST(testWriteCache);
  23. CPPUNIT_TEST_SUITE_END();
  24. private:
  25. std::unique_ptr<MultiDiskAdaptor> adaptor;
  26. public:
  27. void setUp()
  28. {
  29. adaptor = make_unique<MultiDiskAdaptor>();
  30. adaptor->setPieceLength(2);
  31. }
  32. void testWriteData();
  33. void testReadData();
  34. void testCutTrailingGarbage();
  35. void testSize();
  36. void testUtime();
  37. void testResetDiskWriterEntries();
  38. void testWriteCache();
  39. };
  40. CPPUNIT_TEST_SUITE_REGISTRATION(MultiDiskAdaptorTest);
  41. std::vector<std::shared_ptr<FileEntry>> createEntries()
  42. {
  43. std::vector<std::shared_ptr<FileEntry>> entries{
  44. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file0.txt", 0, 0),
  45. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file1.txt", 15, 0),
  46. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file2.txt", 7, 15),
  47. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file3.txt", 0, 22),
  48. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file4.txt", 2, 22),
  49. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file5.txt", 0, 24),
  50. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file6.txt", 3, 24),
  51. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file7.txt", 0, 27),
  52. std::make_shared<FileEntry>(A2_TEST_OUT_DIR "/file8.txt", 2, 27),
  53. };
  54. // 1 1 2 2 3
  55. // 0....5....0....5....0....5....0
  56. // ++--++--++--++--++--++--++--++--
  57. // | file0
  58. // *************** file1
  59. // ******* file2
  60. // | file3
  61. // ** flie4
  62. // | file5
  63. // *** file6
  64. // |file7
  65. // ** file8
  66. for (const auto& i : entries) {
  67. File(i->getPath()).remove();
  68. }
  69. return entries;
  70. }
  71. void MultiDiskAdaptorTest::testResetDiskWriterEntries()
  72. {
  73. {
  74. auto fileEntries = createEntries();
  75. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  76. // In openFile(), resetDiskWriterEntries() are called.
  77. adaptor->openFile();
  78. auto& entries = adaptor->getDiskWriterEntries();
  79. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  80. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  81. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  82. CPPUNIT_ASSERT(entries[3]->getDiskWriter());
  83. CPPUNIT_ASSERT(entries[4]->getDiskWriter());
  84. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  85. adaptor->closeFile();
  86. }
  87. {
  88. auto fileEntries = createEntries();
  89. fileEntries[0]->setRequested(false);
  90. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  91. // In openFile(), resetDiskWriterEntries() are called.
  92. adaptor->openFile();
  93. auto& entries = adaptor->getDiskWriterEntries();
  94. // Because entries[1] spans entries[0]
  95. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  96. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  97. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  98. CPPUNIT_ASSERT(entries[3]->getDiskWriter());
  99. CPPUNIT_ASSERT(entries[4]->getDiskWriter());
  100. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  101. adaptor->closeFile();
  102. }
  103. {
  104. auto fileEntries = createEntries();
  105. fileEntries[0]->setRequested(false);
  106. fileEntries[1]->setRequested(false);
  107. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  108. // In openFile(), resetDiskWriterEntries() are called.
  109. adaptor->openFile();
  110. auto& entries = adaptor->getDiskWriterEntries();
  111. CPPUNIT_ASSERT(!entries[0]->getDiskWriter());
  112. // Because entries[2] spans entries[1]
  113. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  114. CPPUNIT_ASSERT(entries[1]->needsFileAllocation());
  115. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  116. CPPUNIT_ASSERT(entries[3]->getDiskWriter());
  117. CPPUNIT_ASSERT(entries[4]->getDiskWriter());
  118. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  119. adaptor->closeFile();
  120. }
  121. {
  122. auto fileEntries = createEntries();
  123. fileEntries[3]->setRequested(false);
  124. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  125. // In openFile(), resetDiskWriterEntries() are called.
  126. adaptor->openFile();
  127. auto& entries = adaptor->getDiskWriterEntries();
  128. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  129. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  130. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  131. // Because entries[4] spans entries[3]
  132. CPPUNIT_ASSERT(entries[3]->getDiskWriter());
  133. CPPUNIT_ASSERT(entries[3]->needsFileAllocation());
  134. CPPUNIT_ASSERT(entries[4]->getDiskWriter());
  135. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  136. adaptor->closeFile();
  137. }
  138. {
  139. auto fileEntries = createEntries();
  140. fileEntries[4]->setRequested(false);
  141. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  142. // In openFile(), resetDiskWriterEntries() are called.
  143. adaptor->openFile();
  144. auto& entries = adaptor->getDiskWriterEntries();
  145. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  146. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  147. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  148. CPPUNIT_ASSERT(entries[3]->getDiskWriter());
  149. // entries[3] is 0 length. No overrap with entries[4]
  150. CPPUNIT_ASSERT(!entries[4]->getDiskWriter());
  151. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  152. adaptor->closeFile();
  153. }
  154. {
  155. auto fileEntries = createEntries();
  156. fileEntries[3]->setRequested(false);
  157. fileEntries[4]->setRequested(false);
  158. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  159. // In openFile(), resetDiskWriterEntries() are called.
  160. adaptor->openFile();
  161. auto& entries = adaptor->getDiskWriterEntries();
  162. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  163. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  164. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  165. CPPUNIT_ASSERT(!entries[3]->getDiskWriter());
  166. CPPUNIT_ASSERT(!entries[4]->getDiskWriter());
  167. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  168. adaptor->closeFile();
  169. }
  170. {
  171. auto fileEntries = createEntries();
  172. for (size_t i = 5; i < 9; ++i) {
  173. fileEntries[i]->setRequested(false);
  174. }
  175. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  176. // In openFile(), resetDiskWriterEntries() are called.
  177. adaptor->openFile();
  178. auto& entries = adaptor->getDiskWriterEntries();
  179. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  180. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  181. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  182. CPPUNIT_ASSERT(entries[3]->getDiskWriter());
  183. CPPUNIT_ASSERT(entries[4]->getDiskWriter());
  184. CPPUNIT_ASSERT(!entries[5]->getDiskWriter());
  185. adaptor->closeFile();
  186. }
  187. {
  188. auto fileEntries = createEntries();
  189. for (size_t i = 1; i < 9; ++i) {
  190. fileEntries[i]->setRequested(false);
  191. }
  192. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  193. adaptor->openFile();
  194. auto& entries = adaptor->getDiskWriterEntries();
  195. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  196. CPPUNIT_ASSERT(!entries[1]->getDiskWriter());
  197. CPPUNIT_ASSERT(!entries[2]->getDiskWriter());
  198. CPPUNIT_ASSERT(!entries[3]->getDiskWriter());
  199. CPPUNIT_ASSERT(!entries[4]->getDiskWriter());
  200. CPPUNIT_ASSERT(!entries[5]->getDiskWriter());
  201. adaptor->closeFile();
  202. }
  203. {
  204. auto fileEntries = createEntries();
  205. for (size_t i = 2; i < 9; ++i) {
  206. fileEntries[i]->setRequested(false);
  207. }
  208. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  209. adaptor->openFile();
  210. auto& entries = adaptor->getDiskWriterEntries();
  211. CPPUNIT_ASSERT(entries[0]->getDiskWriter());
  212. CPPUNIT_ASSERT(entries[1]->getDiskWriter());
  213. // entries[1] spans entries[2]
  214. CPPUNIT_ASSERT(entries[2]->getDiskWriter());
  215. CPPUNIT_ASSERT(!entries[2]->needsFileAllocation());
  216. CPPUNIT_ASSERT(!entries[3]->getDiskWriter());
  217. CPPUNIT_ASSERT(!entries[4]->getDiskWriter());
  218. CPPUNIT_ASSERT(!entries[5]->getDiskWriter());
  219. adaptor->closeFile();
  220. }
  221. {
  222. auto fileEntries = createEntries();
  223. for (size_t i = 0; i < 6; ++i) {
  224. fileEntries[i]->setRequested(false);
  225. }
  226. fileEntries[8]->setRequested(false);
  227. adaptor->setFileEntries(fileEntries.begin(), fileEntries.end());
  228. adaptor->openFile();
  229. auto& entries = adaptor->getDiskWriterEntries();
  230. CPPUNIT_ASSERT(!entries[0]->getDiskWriter());
  231. CPPUNIT_ASSERT(!entries[1]->getDiskWriter());
  232. CPPUNIT_ASSERT(!entries[2]->getDiskWriter());
  233. CPPUNIT_ASSERT(!entries[3]->getDiskWriter());
  234. CPPUNIT_ASSERT(!entries[4]->getDiskWriter());
  235. // entries[6] spans entries[5] in the current implementation.
  236. CPPUNIT_ASSERT(entries[5]->getDiskWriter());
  237. CPPUNIT_ASSERT(entries[6]->getDiskWriter());
  238. CPPUNIT_ASSERT(entries[7]->getDiskWriter());
  239. // entries[6] spans entries[8]
  240. CPPUNIT_ASSERT(entries[8]->getDiskWriter());
  241. adaptor->closeFile();
  242. }
  243. }
  244. namespace {
  245. void readFile(const std::string& filename, char* buf, int bufLength)
  246. {
  247. FILE* f = fopen(filename.c_str(), "r");
  248. if (f == nullptr) {
  249. CPPUNIT_FAIL(strerror(errno));
  250. }
  251. int retval = fread(buf, 1, bufLength, f);
  252. fclose(f);
  253. if (retval != bufLength) {
  254. CPPUNIT_FAIL("return value is not 1");
  255. }
  256. }
  257. } // namespace
  258. void MultiDiskAdaptorTest::testWriteData()
  259. {
  260. auto fileEntries = createEntries();
  261. adaptor->setFileEntries(std::begin(fileEntries), std::end(fileEntries));
  262. adaptor->openFile();
  263. std::string msg = "12345";
  264. adaptor->writeData((const unsigned char*)msg.c_str(), msg.size(), 0);
  265. adaptor->closeFile();
  266. CPPUNIT_ASSERT(File(A2_TEST_OUT_DIR "/file0.txt").isFile());
  267. char buf[128];
  268. readFile(A2_TEST_OUT_DIR "/file1.txt", buf, 5);
  269. buf[5] = '\0';
  270. CPPUNIT_ASSERT_EQUAL(msg, std::string(buf));
  271. adaptor->openFile();
  272. std::string msg2 = "67890ABCDEF";
  273. adaptor->writeData((const unsigned char*)msg2.c_str(), msg2.size(), 5);
  274. adaptor->closeFile();
  275. readFile(A2_TEST_OUT_DIR "/file1.txt", buf, 15);
  276. buf[15] = '\0';
  277. CPPUNIT_ASSERT_EQUAL(std::string("1234567890ABCDE"), std::string(buf));
  278. readFile(A2_TEST_OUT_DIR "/file2.txt", buf, 1);
  279. buf[1] = '\0';
  280. CPPUNIT_ASSERT_EQUAL(std::string("F"), std::string(buf));
  281. adaptor->openFile();
  282. std::string msg3 = "12345123456712";
  283. adaptor->writeData((const unsigned char*)msg3.c_str(), msg3.size(), 10);
  284. adaptor->closeFile();
  285. readFile(A2_TEST_OUT_DIR "/file1.txt", buf, 15);
  286. buf[15] = '\0';
  287. CPPUNIT_ASSERT_EQUAL(std::string("123456789012345"), std::string(buf));
  288. readFile(A2_TEST_OUT_DIR "/file2.txt", buf, 7);
  289. buf[7] = '\0';
  290. CPPUNIT_ASSERT_EQUAL(std::string("1234567"), std::string(buf));
  291. CPPUNIT_ASSERT(File(A2_TEST_OUT_DIR "/file3.txt").isFile());
  292. readFile(A2_TEST_OUT_DIR "/file4.txt", buf, 2);
  293. buf[2] = '\0';
  294. CPPUNIT_ASSERT_EQUAL(std::string("12"), std::string(buf));
  295. CPPUNIT_ASSERT(File(A2_TEST_OUT_DIR "/file5.txt").isFile());
  296. }
  297. void MultiDiskAdaptorTest::testReadData()
  298. {
  299. auto entries = std::vector<std::shared_ptr<FileEntry>>{
  300. std::make_shared<FileEntry>(A2_TEST_DIR "/file1r.txt", 15, 0),
  301. std::make_shared<FileEntry>(A2_TEST_DIR "/file2r.txt", 7, 15),
  302. std::make_shared<FileEntry>(A2_TEST_DIR "/file3r.txt", 3, 22)};
  303. adaptor->setFileEntries(std::begin(entries), std::end(entries));
  304. adaptor->enableReadOnly();
  305. adaptor->openFile();
  306. unsigned char buf[128];
  307. adaptor->readData(buf, 15, 0);
  308. buf[15] = '\0';
  309. CPPUNIT_ASSERT_EQUAL(std::string("1234567890ABCDE"), std::string((char*)buf));
  310. adaptor->readData(buf, 10, 6);
  311. buf[10] = '\0';
  312. CPPUNIT_ASSERT_EQUAL(std::string("7890ABCDEF"), std::string((char*)buf));
  313. adaptor->readData(buf, 4, 20);
  314. buf[4] = '\0';
  315. CPPUNIT_ASSERT_EQUAL(std::string("KLMN"), std::string((char*)buf));
  316. adaptor->readData(buf, 25, 0);
  317. buf[25] = '\0';
  318. CPPUNIT_ASSERT_EQUAL(std::string("1234567890ABCDEFGHIJKLMNO"),
  319. std::string((char*)buf));
  320. }
  321. void MultiDiskAdaptorTest::testCutTrailingGarbage()
  322. {
  323. std::string dir = A2_TEST_OUT_DIR;
  324. std::string prefix = "aria2_MultiDiskAdaptorTest_testCutTrailingGarbage_";
  325. auto fileEntries = std::vector<std::shared_ptr<FileEntry>>{
  326. std::make_shared<FileEntry>(dir + "/" + prefix + "1", 256, 0),
  327. std::make_shared<FileEntry>(dir + "/" + prefix + "2", 512, 256)};
  328. for (const auto& i : fileEntries) {
  329. createFile(i->getPath(), i->getLength() + 100);
  330. }
  331. MultiDiskAdaptor adaptor;
  332. adaptor.setFileEntries(std::begin(fileEntries), std::end(fileEntries));
  333. adaptor.setPieceLength(1);
  334. adaptor.openFile();
  335. adaptor.cutTrailingGarbage();
  336. CPPUNIT_ASSERT_EQUAL((int64_t)256, File(fileEntries[0]->getPath()).size());
  337. CPPUNIT_ASSERT_EQUAL((int64_t)512, File(fileEntries[1]->getPath()).size());
  338. }
  339. void MultiDiskAdaptorTest::testSize()
  340. {
  341. std::string dir = A2_TEST_OUT_DIR;
  342. std::string prefix = "aria2_MultiDiskAdaptorTest_testSize_";
  343. auto fileEntries = std::vector<std::shared_ptr<FileEntry>>{
  344. std::make_shared<FileEntry>(dir + "/" + prefix + "1", 1, 0),
  345. std::make_shared<FileEntry>(dir + "/" + prefix + "2", 1, 1)};
  346. for (const auto& i : fileEntries) {
  347. createFile(i->getPath(), i->getLength());
  348. }
  349. MultiDiskAdaptor adaptor;
  350. adaptor.setFileEntries(std::begin(fileEntries), std::end(fileEntries));
  351. adaptor.setPieceLength(1);
  352. adaptor.openFile();
  353. CPPUNIT_ASSERT_EQUAL((int64_t)2, adaptor.size());
  354. }
  355. void MultiDiskAdaptorTest::testUtime()
  356. {
  357. std::string storeDir =
  358. A2_TEST_OUT_DIR "/aria2_MultiDiskAdaptorTest_testUtime";
  359. auto entries = std::vector<std::shared_ptr<FileEntry>>{
  360. std::make_shared<FileEntry>(storeDir + "/requested", 0, 0),
  361. std::make_shared<FileEntry>(storeDir + "/notFound", 0, 0),
  362. std::make_shared<FileEntry>(storeDir + "/notRequested", 0, 0),
  363. std::make_shared<FileEntry>(storeDir + "/anotherRequested", 0, 0),
  364. };
  365. createFile(entries[0]->getPath(), entries[0]->getLength());
  366. File(entries[1]->getPath()).remove();
  367. createFile(entries[2]->getPath(), entries[2]->getLength());
  368. createFile(entries[3]->getPath(), entries[3]->getLength());
  369. entries[2]->setRequested(false);
  370. MultiDiskAdaptor adaptor;
  371. adaptor.setFileEntries(std::begin(entries), std::end(entries));
  372. time_t atime = (time_t)100000;
  373. time_t mtime = (time_t)200000;
  374. CPPUNIT_ASSERT_EQUAL((size_t)2, adaptor.utime(Time(atime), Time(mtime)));
  375. CPPUNIT_ASSERT_EQUAL(
  376. (time_t)mtime,
  377. File(entries[0]->getPath()).getModifiedTime().getTimeFromEpoch());
  378. CPPUNIT_ASSERT_EQUAL(
  379. (time_t)mtime,
  380. File(entries[3]->getPath()).getModifiedTime().getTimeFromEpoch());
  381. CPPUNIT_ASSERT(
  382. (time_t)mtime !=
  383. File(entries[2]->getPath()).getModifiedTime().getTimeFromEpoch());
  384. }
  385. void MultiDiskAdaptorTest::testWriteCache()
  386. {
  387. std::string storeDir =
  388. A2_TEST_OUT_DIR "/aria2_MultiDiskAdaptorTest_testWriteCache";
  389. auto entries = std::vector<std::shared_ptr<FileEntry>>{
  390. std::make_shared<FileEntry>(storeDir + "/file1", 16385, 0),
  391. std::make_shared<FileEntry>(storeDir + "/file2", 4098, 16385)};
  392. for (const auto& i : entries) {
  393. File(i->getPath()).remove();
  394. }
  395. auto adaptor = std::make_shared<MultiDiskAdaptor>();
  396. adaptor->setFileEntries(std::begin(entries), std::end(entries));
  397. WrDiskCacheEntry cache{adaptor};
  398. std::string data1(16383, '1'), data2(100, '2'), data3(4000, '3');
  399. cache.cacheData(createDataCell(0, data1.c_str()));
  400. cache.cacheData(createDataCell(data1.size(), data2.c_str()));
  401. cache.cacheData(createDataCell(data1.size() + data2.size(), data3.c_str()));
  402. adaptor->openFile();
  403. adaptor->writeCache(&cache);
  404. for (int i = 0; i < 2; ++i) {
  405. CPPUNIT_ASSERT_EQUAL(entries[i]->getLength(),
  406. File(entries[i]->getPath()).size());
  407. }
  408. CPPUNIT_ASSERT_EQUAL(data1 + data2.substr(0, 2),
  409. readFile(entries[0]->getPath()));
  410. CPPUNIT_ASSERT_EQUAL(data2.substr(2) + data3,
  411. readFile(entries[1]->getPath()));
  412. adaptor->closeFile();
  413. for (int i = 0; i < 2; ++i) {
  414. File(entries[i]->getPath()).remove();
  415. }
  416. cache.clear();
  417. cache.cacheData(createDataCell(123, data2.c_str()));
  418. adaptor->openFile();
  419. adaptor->writeCache(&cache);
  420. CPPUNIT_ASSERT_EQUAL((int64_t)(123 + data2.size()),
  421. File(entries[0]->getPath()).size());
  422. CPPUNIT_ASSERT_EQUAL(data2, readFile(entries[0]->getPath()).substr(123));
  423. }
  424. } // namespace aria2