summaryrefslogtreecommitdiff
path: root/lib/rubygems/package/tar_test_case.rb
blob: 5253e32f362e5694b3c8b51413ea1f6ab67959be (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
require 'rubygems/test_case'
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" then
        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.unpack("C*").inject{|s,a| s + a}
    SP(Z(to_oct(sum, 6)))
  end

  def header(type, fname, dname, length, mode, mtime, checksum = nil)
    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"
      "\0" * 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)
    ]

    format = "C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155"
    h = if RUBY_VERSION >= "1.9" then
          arr.join
        else
          arr = arr.join("").split(//).map{|x| x[0]}
          arr.pack format
        end
    ret = h + "\0" * (512 - h.size)
    assert_equal(512, ret.size)
    ret
  end

  def tar_dir_header(name, prefix, mode, mtime)
    h = header("5", name, prefix, 0, mode, mtime)
    checksum = calc_checksum(h)
    header("5", name, prefix, 0, mode, mtime, checksum)
  end

  def tar_file_header(fname, dname, mode, length, mtime)
    h = header("0", fname, dname, length, mode, mtime)
    checksum = calc_checksum(h)
    header("0", fname, dname, length, mode, mtime, checksum)
  end

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

  def util_entry(tar)
    io = TempIO.new tar

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

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

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

end