1require 'rubygems/test_case'
2require 'rubygems/package'
3
4##
5# A test case for Gem::Package::Tar* classes
6
7class Gem::Package::TarTestCase < Gem::TestCase
8
9  def ASCIIZ(str, length)
10    str + "\0" * (length - str.length)
11  end
12
13  def SP(s)
14    s + " "
15  end
16
17  def SP_Z(s)
18    s + " \0"
19  end
20
21  def Z(s)
22    s + "\0"
23  end
24
25  def assert_headers_equal(expected, actual)
26    expected = expected.to_s unless String === expected
27    actual = actual.to_s unless String === actual
28
29    fields = %w[
30      name 100
31      mode 8
32      uid 8
33      gid 8
34      size 12
35      mtime 12
36      checksum 8
37      typeflag 1
38      linkname 100
39      magic 6
40      version 2
41      uname 32
42      gname 32
43      devmajor 8
44      devminor 8
45      prefix 155
46    ]
47
48    offset = 0
49
50    until fields.empty? do
51      name = fields.shift
52      length = fields.shift.to_i
53
54      if name == "checksum" then
55        chksum_off = offset
56        offset += length
57        next
58      end
59
60      assert_equal expected[offset, length], actual[offset, length],
61                   "Field #{name} of the tar header differs."
62
63      offset += length
64    end
65
66    assert_equal expected[chksum_off, 8], actual[chksum_off, 8]
67  end
68
69  def calc_checksum(header)
70    sum = header.unpack("C*").inject{|s,a| s + a}
71    SP(Z(to_oct(sum, 6)))
72  end
73
74  def header(type, fname, dname, length, mode, checksum = nil)
75    checksum ||= " " * 8
76
77    arr = [                  # struct tarfile_entry_posix
78      ASCIIZ(fname, 100),    # char name[100];     ASCII + (Z unless filled)
79      Z(to_oct(mode, 7)),    # char mode[8];       0 padded, octal null
80      Z(to_oct(0, 7)),       # char uid[8];        ditto
81      Z(to_oct(0, 7)),       # char gid[8];        ditto
82      Z(to_oct(length, 11)), # char size[12];      0 padded, octal, null
83      Z(to_oct(0, 11)),      # char mtime[12];     0 padded, octal, null
84      checksum,              # char checksum[8];   0 padded, octal, null, space
85      type,                  # char typeflag[1];   file: "0"  dir: "5"
86      "\0" * 100,            # char linkname[100]; ASCII + (Z unless filled)
87      "ustar\0",             # char magic[6];      "ustar\0"
88      "00",                  # char version[2];    "00"
89      ASCIIZ("wheel", 32),   # char uname[32];     ASCIIZ
90      ASCIIZ("wheel", 32),   # char gname[32];     ASCIIZ
91      Z(to_oct(0, 7)),       # char devmajor[8];   0 padded, octal, null
92      Z(to_oct(0, 7)),       # char devminor[8];   0 padded, octal, null
93      ASCIIZ(dname, 155)     # char prefix[155];   ASCII + (Z unless filled)
94    ]
95
96    format = "C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155"
97    h = if RUBY_VERSION >= "1.9" then
98          arr.join
99        else
100          arr = arr.join("").split(//).map{|x| x[0]}
101          arr.pack format
102        end
103    ret = h + "\0" * (512 - h.size)
104    assert_equal(512, ret.size)
105    ret
106  end
107
108  def tar_dir_header(name, prefix, mode)
109    h = header("5", name, prefix, 0, mode)
110    checksum = calc_checksum(h)
111    header("5", name, prefix, 0, mode, checksum)
112  end
113
114  def tar_file_header(fname, dname, mode, length)
115    h = header("0", fname, dname, length, mode)
116    checksum = calc_checksum(h)
117    header("0", fname, dname, length, mode, checksum)
118  end
119
120  def to_oct(n, pad_size)
121    "%0#{pad_size}o" % n
122  end
123
124  def util_entry(tar)
125    io = TempIO.new tar
126
127    header = Gem::Package::TarHeader.from io
128
129    Gem::Package::TarReader::Entry.new header, io
130  end
131
132  def util_dir_entry
133    util_entry tar_dir_header("foo", "bar", 0)
134  end
135
136end
137
138