summaryrefslogtreecommitdiff
path: root/test/rubygems/package/tar_test_case.rb
blob: e3d812bf3fd23d630a986798bfc106738e190f48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# frozen_string_literal: true

require_relative "../helper"
require "rubygems/package"

##
# A test case for Gem::Package::Tar* classes

class Gem::Package::TarTestCase < Gem::TestCase
  def ASCIIZ(str, length)
    str + "\0" * (length - str.length)
  end

  def SP(s)
    s + " "
  end

  def SP_Z(s)
    s + " \0"
  end

  def Z(s)
    s + "\0"
  end

  def assert_headers_equal(expected, actual)
    expected = expected.to_s unless String === expected
    actual = actual.to_s unless String === actual

    fields = %w[
      name 100
      mode 8
      uid 8
      gid 8
      size 12
      mtime 12
      checksum 8
      typeflag 1
      linkname 100
      magic 6
      version 2
      uname 32
      gname 32
      devmajor 8
      devminor 8
      prefix 155
    ]

    offset = 0

    until fields.empty? do
      name = fields.shift
      length = fields.shift.to_i

      if name == "checksum"
        chksum_off = offset
        offset += length
        next
      end

      assert_equal expected[offset, length], actual[offset, length],
                   "Field #{name} of the tar header differs."

      offset += length
    end

    assert_equal expected[chksum_off, 8], actual[chksum_off, 8]
  end

  def calc_checksum(header)
    sum = header.sum(0)
    SP(Z(to_oct(sum, 6)))
  end

  def header(type, fname, dname, length, mode, mtime, checksum = nil, linkname = "")
    checksum ||= " " * 8

    arr = [                  # struct tarfile_entry_posix
      ASCIIZ(fname, 100),    # char name[100];     ASCII + (Z unless filled)
      Z(to_oct(mode, 7)),    # char mode[8];       0 padded, octal null
      Z(to_oct(0, 7)),       # char uid[8];        ditto
      Z(to_oct(0, 7)),       # char gid[8];        ditto
      Z(to_oct(length, 11)), # char size[12];      0 padded, octal, null
      Z(to_oct(mtime, 11)),  # char mtime[12];     0 padded, octal, null
      checksum,              # char checksum[8];   0 padded, octal, null, space
      type,                  # char typeflag[1];   file: "0"  dir: "5"
      ASCIIZ(linkname, 100), # char linkname[100]; ASCII + (Z unless filled)
      "ustar\0",             # char magic[6];      "ustar\0"
      "00",                  # char version[2];    "00"
      ASCIIZ("wheel", 32),   # char uname[32];     ASCIIZ
      ASCIIZ("wheel", 32),   # char gname[32];     ASCIIZ
      Z(to_oct(0, 7)),       # char devmajor[8];   0 padded, octal, null
      Z(to_oct(0, 7)),       # char devminor[8];   0 padded, octal, null
      ASCIIZ(dname, 155),    # char prefix[155];   ASCII + (Z unless filled)
    ]

    h = arr.join
    ret = ASCIIZ(h, 512)
    assert_equal(512, ret.size)
    ret
  end

  def header_with_checksum(type, fname, dname, length, mode, mtime, linkname = "")
    h = header(type, fname, dname, length, mode, mtime, nil, linkname)
    checksum = calc_checksum(h)
    header(type, fname, dname, length, mode, mtime, checksum, linkname)
  end

  def tar_dir_header(name, prefix, mode, mtime)
    header_with_checksum("5", name, prefix, 0, mode, mtime)
  end

  def tar_file_header(fname, dname, mode, length, mtime)
    header_with_checksum("0", fname, dname, length, mode, mtime)
  end

  def tar_symlink_header(fname, dname, mode, mtime, linkname)
    header_with_checksum("2", fname, dname, 0, mode, mtime, linkname)
  end

  def tar_file_contents(content)
    pad = (512 - (content.size % 512)) % 512
    content + "\0" * pad
  end

  def to_oct(n, pad_size)
    format("%0#{pad_size}o", n)
  end

  def util_entry(tar)
    io = tar.respond_to?(:read) ? tar : TempIO.new(tar)

    header = Gem::Package::TarHeader.from io

    Gem::Package::TarReader::Entry.open header, io
  end

  def close_util_entry(entry)
    entry.instance_variable_get(:@io).close!
  end

  def util_dir_entry
    util_entry tar_dir_header("foo", "bar", 0, Time.now)
  end

  def util_symlink_entry
    util_entry tar_symlink_header("foo", "bar", 0, Time.now, "link")
  end

  def util_tar(&block)
    tar_io = StringIO.new
    Gem::Package::TarWriter.new(tar_io, &block)
    tar_io.rewind
    tar_io
  end

  def util_tar_gz(&block)
    tar_io = util_tar(&block)
    StringIO.new util_gzip(tar_io.string)
  end

  def util_gem_data_tar(spec = nil, &block)
    data_tgz = util_tar_gz(&block)
    util_tar do |tar|
      if spec
        tar.add_file "metadata.gz", 0o444 do |io|
          io.write util_gzip(spec.to_yaml)
        end
      end
      tar.add_file "data.tar.gz", 0o644 do |io|
        io.write data_tgz.string
      end
    end
  end
end