1 /* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under both the BSD-style license (found in the 6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 * in the COPYING file in the root directory of this source tree). 8 */ 9 #include "Options.h" 10 11 #include <array> 12 #include <gtest/gtest.h> 13 14 using namespace pzstd; 15 16 namespace pzstd { 17 bool operator==(const Options &lhs, const Options &rhs) { 18 return lhs.numThreads == rhs.numThreads && 19 lhs.maxWindowLog == rhs.maxWindowLog && 20 lhs.compressionLevel == rhs.compressionLevel && 21 lhs.decompress == rhs.decompress && lhs.inputFiles == rhs.inputFiles && 22 lhs.outputFile == rhs.outputFile && lhs.overwrite == rhs.overwrite && 23 lhs.keepSource == rhs.keepSource && lhs.writeMode == rhs.writeMode && 24 lhs.checksum == rhs.checksum && lhs.verbosity == rhs.verbosity; 25 } 26 27 std::ostream &operator<<(std::ostream &out, const Options &opt) { 28 out << "{"; 29 { 30 out << "\n\t" 31 << "numThreads: " << opt.numThreads; 32 out << ",\n\t" 33 << "maxWindowLog: " << opt.maxWindowLog; 34 out << ",\n\t" 35 << "compressionLevel: " << opt.compressionLevel; 36 out << ",\n\t" 37 << "decompress: " << opt.decompress; 38 out << ",\n\t" 39 << "inputFiles: {"; 40 { 41 bool first = true; 42 for (const auto &file : opt.inputFiles) { 43 if (!first) { 44 out << ","; 45 } 46 first = false; 47 out << "\n\t\t" << file; 48 } 49 } 50 out << "\n\t}"; 51 out << ",\n\t" 52 << "outputFile: " << opt.outputFile; 53 out << ",\n\t" 54 << "overwrite: " << opt.overwrite; 55 out << ",\n\t" 56 << "keepSource: " << opt.keepSource; 57 out << ",\n\t" 58 << "writeMode: " << static_cast<int>(opt.writeMode); 59 out << ",\n\t" 60 << "checksum: " << opt.checksum; 61 out << ",\n\t" 62 << "verbosity: " << opt.verbosity; 63 } 64 out << "\n}"; 65 return out; 66 } 67 } 68 69 namespace { 70 #ifdef _WIN32 71 const char nullOutput[] = "nul"; 72 #else 73 const char nullOutput[] = "/dev/null"; 74 #endif 75 76 constexpr auto autoMode = Options::WriteMode::Auto; 77 } // anonymous namespace 78 79 #define EXPECT_SUCCESS(...) EXPECT_EQ(Options::Status::Success, __VA_ARGS__) 80 #define EXPECT_FAILURE(...) EXPECT_EQ(Options::Status::Failure, __VA_ARGS__) 81 #define EXPECT_MESSAGE(...) EXPECT_EQ(Options::Status::Message, __VA_ARGS__) 82 83 template <typename... Args> 84 std::array<const char *, sizeof...(Args) + 1> makeArray(Args... args) { 85 return {{nullptr, args...}}; 86 } 87 88 TEST(Options, ValidInputs) { 89 { 90 Options options; 91 auto args = makeArray("--processes", "5", "-o", "x", "y", "-f"); 92 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 93 Options expected = {5, 23, 3, false, {"y"}, "x", 94 true, true, autoMode, true, 2}; 95 EXPECT_EQ(expected, options); 96 } 97 { 98 Options options; 99 auto args = makeArray("-p", "1", "input", "-19"); 100 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 101 Options expected = {1, 23, 19, false, {"input"}, "", 102 false, true, autoMode, true, 2}; 103 EXPECT_EQ(expected, options); 104 } 105 { 106 Options options; 107 auto args = 108 makeArray("--ultra", "-22", "-p", "1", "-o", "x", "-d", "x.zst", "-f"); 109 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 110 Options expected = {1, 0, 22, true, {"x.zst"}, "x", 111 true, true, autoMode, true, 2}; 112 EXPECT_EQ(expected, options); 113 } 114 { 115 Options options; 116 auto args = makeArray("--processes", "100", "hello.zst", "--decompress", 117 "--force"); 118 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 119 Options expected = {100, 23, 3, true, {"hello.zst"}, "", true, 120 true, autoMode, true, 2}; 121 EXPECT_EQ(expected, options); 122 } 123 { 124 Options options; 125 auto args = makeArray("x", "-dp", "1", "-c"); 126 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 127 Options expected = {1, 23, 3, true, {"x"}, "-", 128 false, true, autoMode, true, 2}; 129 EXPECT_EQ(expected, options); 130 } 131 { 132 Options options; 133 auto args = makeArray("x", "-dp", "1", "--stdout"); 134 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 135 Options expected = {1, 23, 3, true, {"x"}, "-", 136 false, true, autoMode, true, 2}; 137 EXPECT_EQ(expected, options); 138 } 139 { 140 Options options; 141 auto args = makeArray("-p", "1", "x", "-5", "-fo", "-", "--ultra", "-d"); 142 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 143 Options expected = {1, 0, 5, true, {"x"}, "-", 144 true, true, autoMode, true, 2}; 145 EXPECT_EQ(expected, options); 146 } 147 { 148 Options options; 149 auto args = makeArray("silesia.tar", "-o", "silesia.tar.pzstd", "-p", "2"); 150 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 151 Options expected = {2, 152 23, 153 3, 154 false, 155 {"silesia.tar"}, 156 "silesia.tar.pzstd", 157 false, 158 true, 159 autoMode, 160 true, 161 2}; 162 EXPECT_EQ(expected, options); 163 } 164 { 165 Options options; 166 auto args = makeArray("x", "-p", "1"); 167 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 168 } 169 { 170 Options options; 171 auto args = makeArray("x", "-p", "1"); 172 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 173 } 174 } 175 176 TEST(Options, GetOutputFile) { 177 { 178 Options options; 179 auto args = makeArray("x"); 180 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 181 EXPECT_EQ("x.zst", options.getOutputFile(options.inputFiles[0])); 182 } 183 { 184 Options options; 185 auto args = makeArray("x", "y", "-o", nullOutput); 186 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 187 EXPECT_EQ(nullOutput, options.getOutputFile(options.inputFiles[0])); 188 } 189 { 190 Options options; 191 auto args = makeArray("x.zst", "-do", nullOutput); 192 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 193 EXPECT_EQ(nullOutput, options.getOutputFile(options.inputFiles[0])); 194 } 195 { 196 Options options; 197 auto args = makeArray("x.zst", "-d"); 198 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 199 EXPECT_EQ("x", options.getOutputFile(options.inputFiles[0])); 200 } 201 { 202 Options options; 203 auto args = makeArray("xzst", "-d"); 204 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 205 EXPECT_EQ("", options.getOutputFile(options.inputFiles[0])); 206 } 207 { 208 Options options; 209 auto args = makeArray("xzst", "-doxx"); 210 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 211 EXPECT_EQ("xx", options.getOutputFile(options.inputFiles[0])); 212 } 213 } 214 215 TEST(Options, MultipleFiles) { 216 { 217 Options options; 218 auto args = makeArray("x", "y", "z"); 219 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 220 Options expected; 221 expected.inputFiles = {"x", "y", "z"}; 222 expected.verbosity = 1; 223 EXPECT_EQ(expected, options); 224 } 225 { 226 Options options; 227 auto args = makeArray("x", "y", "z", "-o", nullOutput); 228 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 229 Options expected; 230 expected.inputFiles = {"x", "y", "z"}; 231 expected.outputFile = nullOutput; 232 expected.verbosity = 1; 233 EXPECT_EQ(expected, options); 234 } 235 { 236 Options options; 237 auto args = makeArray("x", "y", "-o-"); 238 EXPECT_FAILURE(options.parse(args.size(), args.data())); 239 } 240 { 241 Options options; 242 auto args = makeArray("x", "y", "-o", "file"); 243 EXPECT_FAILURE(options.parse(args.size(), args.data())); 244 } 245 { 246 Options options; 247 auto args = makeArray("-qqvd12qp4", "-f", "x", "--", "--rm", "-c"); 248 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 249 Options expected = {4, 23, 12, true, {"x", "--rm", "-c"}, 250 "", true, true, autoMode, true, 251 0}; 252 EXPECT_EQ(expected, options); 253 } 254 } 255 256 TEST(Options, NumThreads) { 257 { 258 Options options; 259 auto args = makeArray("x", "-dfo", "-"); 260 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 261 } 262 { 263 Options options; 264 auto args = makeArray("x", "-p", "0", "-fo", "-"); 265 EXPECT_FAILURE(options.parse(args.size(), args.data())); 266 } 267 { 268 Options options; 269 auto args = makeArray("-f", "-p", "-o", "-"); 270 EXPECT_FAILURE(options.parse(args.size(), args.data())); 271 } 272 } 273 274 TEST(Options, BadCompressionLevel) { 275 { 276 Options options; 277 auto args = makeArray("x", "-20"); 278 EXPECT_FAILURE(options.parse(args.size(), args.data())); 279 } 280 { 281 Options options; 282 auto args = makeArray("x", "--ultra", "-23"); 283 EXPECT_FAILURE(options.parse(args.size(), args.data())); 284 } 285 { 286 Options options; 287 auto args = makeArray("x", "--1"); // negative 1? 288 EXPECT_FAILURE(options.parse(args.size(), args.data())); 289 } 290 } 291 292 TEST(Options, InvalidOption) { 293 { 294 Options options; 295 auto args = makeArray("x", "-x"); 296 EXPECT_FAILURE(options.parse(args.size(), args.data())); 297 } 298 } 299 300 TEST(Options, BadOutputFile) { 301 { 302 Options options; 303 auto args = makeArray("notzst", "-d", "-p", "1"); 304 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 305 EXPECT_EQ("", options.getOutputFile(options.inputFiles.front())); 306 } 307 } 308 309 TEST(Options, BadOptionsWithArguments) { 310 { 311 Options options; 312 auto args = makeArray("x", "-pf"); 313 EXPECT_FAILURE(options.parse(args.size(), args.data())); 314 } 315 { 316 Options options; 317 auto args = makeArray("x", "-p", "10f"); 318 EXPECT_FAILURE(options.parse(args.size(), args.data())); 319 } 320 { 321 Options options; 322 auto args = makeArray("x", "-p"); 323 EXPECT_FAILURE(options.parse(args.size(), args.data())); 324 } 325 { 326 Options options; 327 auto args = makeArray("x", "-o"); 328 EXPECT_FAILURE(options.parse(args.size(), args.data())); 329 } 330 { 331 Options options; 332 auto args = makeArray("x", "-o"); 333 EXPECT_FAILURE(options.parse(args.size(), args.data())); 334 } 335 } 336 337 TEST(Options, KeepSource) { 338 { 339 Options options; 340 auto args = makeArray("x", "--rm", "-k"); 341 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 342 EXPECT_EQ(true, options.keepSource); 343 } 344 { 345 Options options; 346 auto args = makeArray("x", "--rm", "--keep"); 347 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 348 EXPECT_EQ(true, options.keepSource); 349 } 350 { 351 Options options; 352 auto args = makeArray("x"); 353 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 354 EXPECT_EQ(true, options.keepSource); 355 } 356 { 357 Options options; 358 auto args = makeArray("x", "--rm"); 359 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 360 EXPECT_EQ(false, options.keepSource); 361 } 362 } 363 364 TEST(Options, Verbosity) { 365 { 366 Options options; 367 auto args = makeArray("x"); 368 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 369 EXPECT_EQ(2, options.verbosity); 370 } 371 { 372 Options options; 373 auto args = makeArray("--quiet", "-qq", "x"); 374 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 375 EXPECT_EQ(-1, options.verbosity); 376 } 377 { 378 Options options; 379 auto args = makeArray("x", "y"); 380 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 381 EXPECT_EQ(1, options.verbosity); 382 } 383 { 384 Options options; 385 auto args = makeArray("--", "x", "y"); 386 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 387 EXPECT_EQ(1, options.verbosity); 388 } 389 { 390 Options options; 391 auto args = makeArray("-qv", "x", "y"); 392 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 393 EXPECT_EQ(1, options.verbosity); 394 } 395 { 396 Options options; 397 auto args = makeArray("-v", "x", "y"); 398 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 399 EXPECT_EQ(3, options.verbosity); 400 } 401 { 402 Options options; 403 auto args = makeArray("-v", "x"); 404 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 405 EXPECT_EQ(3, options.verbosity); 406 } 407 } 408 409 TEST(Options, TestMode) { 410 { 411 Options options; 412 auto args = makeArray("x", "-t"); 413 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 414 EXPECT_EQ(true, options.keepSource); 415 EXPECT_EQ(true, options.decompress); 416 EXPECT_EQ(nullOutput, options.outputFile); 417 } 418 { 419 Options options; 420 auto args = makeArray("x", "--test", "--rm", "-ohello"); 421 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 422 EXPECT_EQ(true, options.keepSource); 423 EXPECT_EQ(true, options.decompress); 424 EXPECT_EQ(nullOutput, options.outputFile); 425 } 426 } 427 428 TEST(Options, Checksum) { 429 { 430 Options options; 431 auto args = makeArray("x.zst", "--no-check", "-Cd"); 432 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 433 EXPECT_EQ(true, options.checksum); 434 } 435 { 436 Options options; 437 auto args = makeArray("x"); 438 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 439 EXPECT_EQ(true, options.checksum); 440 } 441 { 442 Options options; 443 auto args = makeArray("x", "--no-check", "--check"); 444 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 445 EXPECT_EQ(true, options.checksum); 446 } 447 { 448 Options options; 449 auto args = makeArray("x", "--no-check"); 450 EXPECT_SUCCESS(options.parse(args.size(), args.data())); 451 EXPECT_EQ(false, options.checksum); 452 } 453 } 454 455 TEST(Options, InputFiles) { 456 { 457 Options options; 458 auto args = makeArray("-cd"); 459 options.parse(args.size(), args.data()); 460 EXPECT_EQ(1, options.inputFiles.size()); 461 EXPECT_EQ("-", options.inputFiles[0]); 462 EXPECT_EQ("-", options.outputFile); 463 } 464 { 465 Options options; 466 auto args = makeArray(); 467 options.parse(args.size(), args.data()); 468 EXPECT_EQ(1, options.inputFiles.size()); 469 EXPECT_EQ("-", options.inputFiles[0]); 470 EXPECT_EQ("-", options.outputFile); 471 } 472 { 473 Options options; 474 auto args = makeArray("-d"); 475 options.parse(args.size(), args.data()); 476 EXPECT_EQ(1, options.inputFiles.size()); 477 EXPECT_EQ("-", options.inputFiles[0]); 478 EXPECT_EQ("-", options.outputFile); 479 } 480 { 481 Options options; 482 auto args = makeArray("x", "-"); 483 EXPECT_FAILURE(options.parse(args.size(), args.data())); 484 } 485 } 486 487 TEST(Options, InvalidOptions) { 488 { 489 Options options; 490 auto args = makeArray("-ibasdf"); 491 EXPECT_FAILURE(options.parse(args.size(), args.data())); 492 } 493 { 494 Options options; 495 auto args = makeArray("- "); 496 EXPECT_FAILURE(options.parse(args.size(), args.data())); 497 } 498 { 499 Options options; 500 auto args = makeArray("-n15"); 501 EXPECT_FAILURE(options.parse(args.size(), args.data())); 502 } 503 { 504 Options options; 505 auto args = makeArray("-0", "x"); 506 EXPECT_FAILURE(options.parse(args.size(), args.data())); 507 } 508 } 509 510 TEST(Options, Extras) { 511 { 512 Options options; 513 auto args = makeArray("-h"); 514 EXPECT_MESSAGE(options.parse(args.size(), args.data())); 515 } 516 { 517 Options options; 518 auto args = makeArray("-H"); 519 EXPECT_MESSAGE(options.parse(args.size(), args.data())); 520 } 521 { 522 Options options; 523 auto args = makeArray("-V"); 524 EXPECT_MESSAGE(options.parse(args.size(), args.data())); 525 } 526 { 527 Options options; 528 auto args = makeArray("--help"); 529 EXPECT_MESSAGE(options.parse(args.size(), args.data())); 530 } 531 { 532 Options options; 533 auto args = makeArray("--version"); 534 EXPECT_MESSAGE(options.parse(args.size(), args.data())); 535 } 536 } 537