github.com/bodgit/sevenzip@v1.5.1/reader_test.go (about) 1 package sevenzip_test 2 3 import ( 4 "errors" 5 "fmt" 6 "hash" 7 "hash/crc32" 8 "io" 9 "path/filepath" 10 "runtime" 11 "testing" 12 "testing/fstest" 13 "testing/iotest" 14 15 "github.com/bodgit/sevenzip" 16 "github.com/bodgit/sevenzip/internal/util" 17 "github.com/stretchr/testify/assert" 18 "golang.org/x/sync/errgroup" 19 ) 20 21 func reader(r io.Reader) io.Reader { 22 return r 23 } 24 25 func extractFile(tb testing.TB, r io.Reader, h hash.Hash, f *sevenzip.File) error { 26 tb.Helper() 27 28 h.Reset() 29 30 if _, err := io.Copy(h, r); err != nil { 31 return err 32 } 33 34 if f.UncompressedSize > 0 && f.CRC32 == 0 { 35 tb.Log("archive member", f.Name, "has no CRC") 36 37 return nil 38 } 39 40 if !util.CRC32Equal(h.Sum(nil), f.CRC32) { 41 return errors.New("CRC doesn't match") 42 } 43 44 return nil 45 } 46 47 //nolint:lll 48 func extractArchive(tb testing.TB, r *sevenzip.ReadCloser, stream int, h hash.Hash, fn func(io.Reader) io.Reader, optimised bool) error { 49 tb.Helper() 50 51 for _, f := range r.File { 52 if stream >= 0 && f.Stream != stream { 53 continue 54 } 55 56 rc, err := f.Open() 57 if err != nil { 58 return err 59 } 60 defer rc.Close() 61 62 if err = extractFile(tb, fn(rc), h, f); err != nil { 63 return err 64 } 65 66 if optimised { 67 rc.Close() 68 } 69 } 70 71 return nil 72 } 73 74 //nolint:funlen 75 func TestOpenReader(t *testing.T) { 76 t.Parallel() 77 78 tables := []struct { 79 name, file string 80 volumes []string 81 err error 82 }{ 83 { 84 name: "no header compression", 85 file: "t0.7z", 86 }, 87 { 88 name: "with header compression", 89 file: "t1.7z", 90 }, 91 { 92 name: "multiple volume", 93 file: "multi.7z.001", 94 volumes: []string{ 95 "multi.7z.001", 96 "multi.7z.002", 97 "multi.7z.003", 98 "multi.7z.004", 99 "multi.7z.005", 100 "multi.7z.006", 101 }, 102 }, 103 { 104 name: "empty streams and files", 105 file: "empty.7z", 106 }, 107 { 108 name: "bcj2", 109 file: "bcj2.7z", 110 }, 111 { 112 name: "bzip2", 113 file: "bzip2.7z", 114 }, 115 { 116 name: "copy", 117 file: "copy.7z", 118 }, 119 { 120 name: "deflate", 121 file: "deflate.7z", 122 }, 123 { 124 name: "delta", 125 file: "delta.7z", 126 }, 127 { 128 name: "lzma", 129 file: "lzma.7z", 130 }, 131 { 132 name: "lzma2", 133 file: "lzma2.7z", 134 }, 135 { 136 name: "complex", 137 file: "lzma1900.7z", 138 }, 139 { 140 name: "lz4", 141 file: "lz4.7z", 142 }, 143 { 144 name: "brotli", 145 file: "brotli.7z", 146 }, 147 { 148 name: "zstd", 149 file: "zstd.7z", 150 }, 151 { 152 name: "sfx", 153 file: "sfx.exe", 154 }, 155 { 156 name: "bcj", 157 file: "bcj.7z", 158 }, 159 { 160 name: "ppc", 161 file: "ppc.7z", 162 }, 163 { 164 name: "arm", 165 file: "arm.7z", 166 }, 167 { 168 name: "sparc", 169 file: "sparc.7z", 170 }, 171 { 172 name: "issue 87", 173 file: "issue87.7z", 174 }, 175 { 176 name: "issue 112", 177 file: "file_and_empty.7z", 178 }, 179 { 180 name: "issue 113", 181 file: "COMPRESS-492.7z", 182 err: sevenzip.ErrMissingUnpackInfo, 183 }, 184 } 185 186 for _, table := range tables { 187 table := table 188 189 t.Run(table.name, func(t *testing.T) { 190 t.Parallel() 191 192 r, err := sevenzip.OpenReader(filepath.Join("testdata", table.file)) 193 if err != nil { 194 assert.ErrorIs(t, err, table.err) 195 196 return 197 } 198 defer r.Close() 199 200 volumes := []string{} 201 202 if table.volumes != nil { 203 for _, v := range table.volumes { 204 volumes = append(volumes, filepath.Join("testdata", v)) 205 } 206 } else { 207 volumes = append(volumes, filepath.Join("testdata", table.file)) 208 } 209 210 assert.Equal(t, volumes, r.Volumes()) 211 212 if err := extractArchive(t, r, -1, crc32.NewIEEE(), iotest.OneByteReader, true); err != nil { 213 t.Fatal(err) 214 } 215 }) 216 } 217 } 218 219 func TestOpenReaderWithPassword(t *testing.T) { 220 t.Parallel() 221 222 tables := []struct { 223 name, file, password string 224 }{ 225 { 226 name: "no header compression", 227 file: "t2.7z", 228 password: "password", 229 }, 230 { 231 name: "with header compression", 232 file: "t3.7z", 233 password: "password", 234 }, 235 { 236 name: "issue 75", 237 file: "7zcracker.7z", 238 password: "876", 239 }, 240 } 241 242 for _, table := range tables { 243 table := table 244 245 t.Run(table.name, func(t *testing.T) { 246 t.Parallel() 247 248 r, err := sevenzip.OpenReaderWithPassword(filepath.Join("testdata", table.file), table.password) 249 if err != nil { 250 t.Fatal(err) 251 } 252 defer r.Close() 253 254 if err := extractArchive(t, r, -1, crc32.NewIEEE(), iotest.OneByteReader, true); err != nil { 255 t.Fatal(err) 256 } 257 }) 258 } 259 } 260 261 func TestFS(t *testing.T) { 262 t.Parallel() 263 264 r, err := sevenzip.OpenReader(filepath.Join("testdata", "lzma1900.7z")) 265 if err != nil { 266 t.Fatal(err) 267 } 268 defer r.Close() 269 270 if err := fstest.TestFS(r, "Asm/arm/7zCrcOpt.asm", "bin/x64/7zr.exe"); err != nil { 271 t.Fatal(err) 272 } 273 } 274 275 func ExampleOpenReader() { 276 r, err := sevenzip.OpenReader(filepath.Join("testdata", "multi.7z.001")) 277 if err != nil { 278 panic(err) 279 } 280 defer r.Close() 281 282 for _, file := range r.File { 283 fmt.Println(file.Name) 284 } 285 // Output: 01 286 // 02 287 // 03 288 // 04 289 // 05 290 // 06 291 // 07 292 // 08 293 // 09 294 // 10 295 } 296 297 func benchmarkArchiveParallel(b *testing.B, file string) { 298 b.Helper() 299 300 for n := 0; n < b.N; n++ { 301 r, err := sevenzip.OpenReader(filepath.Join("testdata", file)) 302 if err != nil { 303 b.Fatal(err) 304 } 305 defer r.Close() 306 307 streams := make(map[int]struct{}, len(r.File)) 308 309 for _, f := range r.File { 310 streams[f.Stream] = struct{}{} 311 } 312 313 eg := new(errgroup.Group) 314 eg.SetLimit(runtime.NumCPU()) 315 316 for stream := range streams { 317 stream := stream 318 319 eg.Go(func() error { 320 return extractArchive(b, r, stream, crc32.NewIEEE(), reader, true) 321 }) 322 } 323 324 if err := eg.Wait(); err != nil { 325 b.Fatal(err) 326 } 327 328 r.Close() 329 } 330 } 331 332 func benchmarkArchiveNaiveParallel(b *testing.B, file string, workers int) { 333 b.Helper() 334 335 for n := 0; n < b.N; n++ { 336 r, err := sevenzip.OpenReader(filepath.Join("testdata", file)) 337 if err != nil { 338 b.Fatal(err) 339 } 340 defer r.Close() 341 342 eg := new(errgroup.Group) 343 eg.SetLimit(workers) 344 345 for _, f := range r.File { 346 f := f 347 348 eg.Go(func() error { 349 rc, err := f.Open() 350 if err != nil { 351 return err 352 } 353 defer rc.Close() 354 355 return extractFile(b, rc, crc32.NewIEEE(), f) 356 }) 357 } 358 359 if err := eg.Wait(); err != nil { 360 b.Fatal(err) 361 } 362 363 r.Close() 364 } 365 } 366 367 func benchmarkArchive(b *testing.B, file, password string, optimised bool) { 368 b.Helper() 369 370 h := crc32.NewIEEE() 371 372 for n := 0; n < b.N; n++ { 373 r, err := sevenzip.OpenReaderWithPassword(filepath.Join("testdata", file), password) 374 if err != nil { 375 b.Fatal(err) 376 } 377 defer r.Close() 378 379 if err := extractArchive(b, r, -1, h, reader, optimised); err != nil { 380 b.Fatal(err) 381 } 382 383 r.Close() 384 } 385 } 386 387 func BenchmarkAES7z(b *testing.B) { 388 benchmarkArchive(b, "aes7z.7z", "password", true) 389 } 390 391 func BenchmarkBzip2(b *testing.B) { 392 benchmarkArchive(b, "bzip2.7z", "", true) 393 } 394 395 func BenchmarkCopy(b *testing.B) { 396 benchmarkArchive(b, "copy.7z", "", true) 397 } 398 399 func BenchmarkDeflate(b *testing.B) { 400 benchmarkArchive(b, "deflate.7z", "", true) 401 } 402 403 func BenchmarkDelta(b *testing.B) { 404 benchmarkArchive(b, "delta.7z", "", true) 405 } 406 407 func BenchmarkLZMA(b *testing.B) { 408 benchmarkArchive(b, "lzma.7z", "", true) 409 } 410 411 func BenchmarkLZMA2(b *testing.B) { 412 benchmarkArchive(b, "lzma2.7z", "", true) 413 } 414 415 func BenchmarkBCJ2(b *testing.B) { 416 benchmarkArchive(b, "bcj2.7z", "", true) 417 } 418 419 func BenchmarkComplex(b *testing.B) { 420 benchmarkArchive(b, "lzma1900.7z", "", true) 421 } 422 423 func BenchmarkLZ4(b *testing.B) { 424 benchmarkArchive(b, "lz4.7z", "", true) 425 } 426 427 func BenchmarkBrotli(b *testing.B) { 428 benchmarkArchive(b, "brotli.7z", "", true) 429 } 430 431 func BenchmarkZstandard(b *testing.B) { 432 benchmarkArchive(b, "zstd.7z", "", true) 433 } 434 435 func BenchmarkNaiveReader(b *testing.B) { 436 benchmarkArchive(b, "lzma1900.7z", "", false) 437 } 438 439 func BenchmarkOptimisedReader(b *testing.B) { 440 benchmarkArchive(b, "lzma1900.7z", "", true) 441 } 442 443 func BenchmarkNaiveParallelReader(b *testing.B) { 444 benchmarkArchiveNaiveParallel(b, "lzma1900.7z", runtime.NumCPU()) 445 } 446 447 func BenchmarkNaiveSingleParallelReader(b *testing.B) { 448 benchmarkArchiveNaiveParallel(b, "lzma1900.7z", 1) 449 } 450 451 func BenchmarkParallelReader(b *testing.B) { 452 benchmarkArchiveParallel(b, "lzma1900.7z") 453 } 454 455 func BenchmarkBCJ(b *testing.B) { 456 benchmarkArchive(b, "bcj.7z", "", true) 457 } 458 459 func BenchmarkPPC(b *testing.B) { 460 benchmarkArchive(b, "ppc.7z", "", true) 461 } 462 463 func BenchmarkARM(b *testing.B) { 464 benchmarkArchive(b, "arm.7z", "", true) 465 } 466 467 func BenchmarkSPARC(b *testing.B) { 468 benchmarkArchive(b, "sparc.7z", "", true) 469 }