PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/rubygems-0.8.11/test/test_package.rb

https://bitbucket.org/redricko/pragprog-scripting
Ruby | 605 lines | 523 code | 49 blank | 33 comment | 8 complexity | 3cb28b749a82c5710b013e339b78d4a2 MD5 | raw file
  1. #---
  2. # Excerpted from "Everyday Scripting in Ruby"
  3. # We make no guarantees that this code is fit for any purpose.
  4. # Visit http://www.pragmaticprogrammer.com/titles/bmsft for more book information.
  5. #---
  6. require 'rubygems'
  7. Gem::manage_gems
  8. require 'rubygems/package'
  9. require 'test/unit'
  10. require 'stringio'
  11. class File
  12. # straight from setup.rb
  13. def File.dir?(path)
  14. # for corrupted windows stat()
  15. File.directory?((path[-1,1] == '/') ? path : path + '/')
  16. end
  17. def File.read_b(name)
  18. File.open(name, "rb"){|f| f.read}
  19. end
  20. end
  21. module TarTester
  22. private
  23. def assert_headers_equal(h1, h2)
  24. fields = %w[name 100 mode 8 uid 8 gid 8 size 12 mtime 12 checksum 8
  25. typeflag 1 linkname 100 magic 6 version 2 uname 32
  26. gname 32 devmajor 8 devminor 8 prefix 155]
  27. offset = 0
  28. until fields.empty?
  29. name = fields.shift
  30. length = fields.shift.to_i
  31. if name == "checksum"
  32. chksum_off = offset
  33. offset += length
  34. next
  35. end
  36. assert_equal(h1[offset, length], h2[offset, length],
  37. "Field #{name} of the tar header differs.")
  38. offset += length
  39. end
  40. assert_equal(h1[chksum_off, 8], h2[chksum_off, 8])
  41. end
  42. def tar_file_header(fname, dname, mode, length)
  43. h = header("0", fname, dname, length, mode)
  44. checksum = calc_checksum(h)
  45. header("0", fname, dname, length, mode, checksum)
  46. end
  47. def tar_dir_header(name, prefix, mode)
  48. h = header("5", name, prefix, 0, mode)
  49. checksum = calc_checksum(h)
  50. header("5", name, prefix, 0, mode, checksum)
  51. end
  52. def header(type, fname, dname, length, mode, checksum = nil)
  53. # struct tarfile_entry_posix {
  54. # char name[100]; # ASCII + (Z unless filled)
  55. # char mode[8]; # 0 padded, octal null
  56. # char uid[8]; # ditto
  57. # char gid[8]; # ditto
  58. # char size[12]; # 0 padded, octal, null
  59. # char mtime[12]; # 0 padded, octal, null
  60. # char checksum[8]; # 0 padded, octal, null and space
  61. # char typeflag[1]; # file: "0" dir: "5"
  62. # char linkname[100]; # ASCII + (Z unless filled)
  63. # char magic[6]; # "ustar\0"
  64. # char version[2]; # "00"
  65. # char uname[32]; # ASCIIZ
  66. # char gname[32]; # ASCIIZ
  67. # char devmajor[8]; # 0 padded, octal, null
  68. # char devminor[8]; # o padded, octal, null
  69. # char prefix[155]; # ASCII + (Z unless filled)
  70. # };
  71. checksum ||= " " * 8
  72. arr = [ASCIIZ(fname, 100), Z(to_oct(mode, 7)), Z(to_oct(0, 7)),
  73. Z(to_oct(0,7)), Z(to_oct(length, 11)), Z(to_oct(0,11)),
  74. checksum, type, "\0" * 100, "ustar\0", "00", ASCIIZ("wheel", 32),
  75. ASCIIZ("wheel", 32), Z(to_oct(0,7)), Z(to_oct(0,7)),
  76. ASCIIZ(dname, 155) ]
  77. arr = arr.join("").split(//).map{|x| x[0]}
  78. h = arr.pack("C100C8C8C8C12C12" + # name, mode, uid, gid, size, mtime
  79. "C8CC100C6C2" + # checksum, typeflag, linkname, magic, version
  80. "C32C32C8C8C155") # uname, gname, devmajor, devminor, prefix
  81. ret = h + "\0" * (512 - h.size)
  82. assert_equal(512, ret.size)
  83. ret
  84. end
  85. def calc_checksum(header)
  86. sum = header.unpack("C*").inject{|s,a| s + a}
  87. SP(Z(to_oct(sum, 6)))
  88. end
  89. def to_oct(n, pad_size)
  90. "%0#{pad_size}o" % n
  91. end
  92. def ASCIIZ(str, length)
  93. str + "\0" * (length - str.length)
  94. end
  95. def SP(s)
  96. s + " "
  97. end
  98. def Z(s)
  99. s + "\0"
  100. end
  101. def SP_Z(s)
  102. s + " \0"
  103. end
  104. end
  105. class TC_TarHeader < Test::Unit::TestCase
  106. include Gem::Package
  107. include TarTester
  108. def test_arguments_are_checked
  109. e = Gem::Package::ArgumentError
  110. assert_raises(e){TarHeader.new :name=>"", :size=>"", :mode=>"" }
  111. assert_raises(e){TarHeader.new :name=>"", :size=>"", :prefix=>"" }
  112. assert_raises(e){TarHeader.new :name=>"", :prefix=>"", :mode=>"" }
  113. assert_raises(e){TarHeader.new :prefix=>"", :size=>"", :mode=>"" }
  114. end
  115. def test_basic_headers
  116. assert_headers_equal(tar_file_header("bla", "", 012345, 10),
  117. TarHeader.new(:name => "bla", :mode => 012345,
  118. :size => 10, :prefix => "").to_s)
  119. assert_headers_equal(tar_dir_header("bla", "", 012345),
  120. TarHeader.new(:name => "bla", :mode => 012345,
  121. :size => 0, :prefix => "",
  122. :typeflag => "5" ).to_s)
  123. end
  124. def test_long_name_works
  125. assert_headers_equal(tar_file_header("a" * 100, "", 012345, 10),
  126. TarHeader.new(:name => "a" * 100, :mode => 012345,
  127. :size => 10, :prefix => "").to_s)
  128. assert_headers_equal(tar_file_header("a" * 100, "bb" * 60,
  129. 012345, 10),
  130. TarHeader.new(:name => "a" * 100, :mode => 012345,
  131. :size => 10, :prefix => "bb" * 60).to_s)
  132. end
  133. def test_new_from_stream
  134. header = tar_file_header("a" * 100, "", 012345, 10)
  135. h = nil
  136. header = StringIO.new header
  137. assert_nothing_raised{ h = TarHeader.new_from_stream header }
  138. assert_equal("a" * 100, h.name)
  139. assert_equal(012345, h.mode)
  140. assert_equal(10, h.size)
  141. assert_equal("", h.prefix)
  142. assert_equal("ustar", h.magic)
  143. end
  144. end
  145. class TC_TarWriter < Test::Unit::TestCase
  146. include Gem::Package
  147. include TarTester
  148. require 'stringio'
  149. class DummyIO
  150. attr_reader :data
  151. def initialize
  152. @data = ""
  153. end
  154. def write(dat)
  155. data << dat
  156. dat.size
  157. end
  158. def reset
  159. @data = ""
  160. end
  161. end
  162. def setup
  163. @data = "a" * 10
  164. @dummyos = DummyIO.new
  165. @os = TarWriter.new(@dummyos)
  166. end
  167. def teardown
  168. @os.close
  169. end
  170. def test_add_file_simple
  171. @dummyos.reset
  172. TarWriter.new(@dummyos) do |os|
  173. os.add_file_simple("lib/foo/bar", 0644, 10) {|f| f.write "a" * 10 }
  174. os.add_file_simple("lib/bar/baz", 0644, 100) {|f| f.write "fillme"}
  175. end
  176. assert_headers_equal(tar_file_header("lib/foo/bar", "", 0644, 10),
  177. @dummyos.data[0,512])
  178. assert_equal("a" * 10 + "\0" * 502, @dummyos.data[512,512])
  179. assert_headers_equal(tar_file_header("lib/bar/baz", "", 0644, 100),
  180. @dummyos.data[512*2,512])
  181. assert_equal("fillme" + "\0" * 506, @dummyos.data[512*3,512])
  182. assert_equal("\0" * 512, @dummyos.data[512*4, 512])
  183. assert_equal("\0" * 512, @dummyos.data[512*5, 512])
  184. end
  185. def test_write_operations_fail_after_closed
  186. @dummyos.reset
  187. @os.add_file_simple("sadd", 0644, 20) { |f| }
  188. @os.close
  189. assert_raises(ClosedIO) { @os.flush }
  190. assert_raises(ClosedIO) { @os.add_file("dfdsf", 0644){} }
  191. assert_raises(ClosedIO) { @os.mkdir "sdfdsf", 0644 }
  192. end
  193. def test_file_name_is_split_correctly
  194. # test insane file lengths, and
  195. # a{100}/b{155}, etc
  196. @dummyos.reset
  197. names = ["a" * 155 + '/' + "b" * 100, "a" * 151 + "/" + ("qwer/" * 19) + "bla" ]
  198. o_names = ["b" * 100, "qwer/" * 19 + "bla"]
  199. o_prefixes = ["a" * 155, "a" * 151]
  200. names.each {|name| @os.add_file_simple(name, 0644, 10) { } }
  201. o_names.each_with_index do |nam, i|
  202. assert_headers_equal(tar_file_header(nam, o_prefixes[i], 0644, 10),
  203. @dummyos.data[2*i*512,512])
  204. end
  205. assert_raises(TooLongFileName) do
  206. @os.add_file_simple(File.join("a" * 152, "b" * 10, "a" * 92), 0644,10) {}
  207. end
  208. assert_raises(TooLongFileName) do
  209. @os.add_file_simple(File.join("a" * 162, "b" * 10), 0644,10) {}
  210. end
  211. assert_raises(TooLongFileName) do
  212. @os.add_file_simple(File.join("a" * 10, "b" * 110), 0644,10) {}
  213. end
  214. end
  215. def test_add_file
  216. dummyos = StringIO.new
  217. class << dummyos
  218. def method_missing(meth, *a)
  219. self.string.send(meth, *a)
  220. end
  221. end
  222. os = TarWriter.new dummyos
  223. content1 = ('a'..'z').to_a.join("") # 26
  224. content2 = ('aa'..'zz').to_a.join("") # 1352
  225. TarWriter.new(dummyos) do |os|
  226. os.add_file("lib/foo/bar", 0644) {|f| f.write "a" * 10 }
  227. os.add_file("lib/bar/baz", 0644) {|f| f.write content1 }
  228. os.add_file("lib/bar/baz", 0644) {|f| f.write content2 }
  229. os.add_file("lib/bar/baz", 0644) {|f| }
  230. end
  231. assert_headers_equal(tar_file_header("lib/foo/bar", "", 0644, 10),
  232. dummyos[0,512])
  233. assert_equal("a" * 10 + "\0" * 502, dummyos[512,512])
  234. offset = 512 * 2
  235. [content1, content2, ""].each do |data|
  236. assert_headers_equal(tar_file_header("lib/bar/baz", "", 0644,
  237. data.size),
  238. dummyos[offset,512])
  239. offset += 512
  240. until !data || data == ""
  241. chunk = data[0,512]
  242. data[0,512] = ""
  243. assert_equal(chunk + "\0" * (512-chunk.size),
  244. dummyos[offset,512])
  245. offset += 512
  246. end
  247. end
  248. assert_equal("\0" * 1024, dummyos[offset,1024])
  249. end
  250. def test_add_file_tests_seekability
  251. assert_raises(Gem::Package::NonSeekableIO) do
  252. @os.add_file("libdfdsfd", 0644) {|f| }
  253. end
  254. end
  255. def test_write_header
  256. @dummyos.reset
  257. @os.add_file_simple("lib/foo/bar", 0644, 0) { |f| }
  258. @os.flush
  259. assert_headers_equal(tar_file_header("lib/foo/bar", "", 0644, 0),
  260. @dummyos.data[0,512])
  261. @dummyos.reset
  262. @os.mkdir("lib/foo", 0644)
  263. assert_headers_equal(tar_dir_header("lib/foo", "", 0644),
  264. @dummyos.data[0,512])
  265. @os.mkdir("lib/bar", 0644)
  266. assert_headers_equal(tar_dir_header("lib/bar", "", 0644),
  267. @dummyos.data[512*1,512])
  268. end
  269. def test_write_data
  270. @dummyos.reset
  271. @os.add_file_simple("lib/foo/bar", 0644, 10) { |f| f.write @data }
  272. @os.flush
  273. assert_equal(@data + ("\0" * (512-@data.size)),
  274. @dummyos.data[512,512])
  275. end
  276. def test_file_size_is_checked
  277. @dummyos.reset
  278. assert_raises(TarWriter::FileOverflow) do
  279. @os.add_file_simple("lib/foo/bar", 0644, 10) {|f| f.write "1" * 100}
  280. end
  281. assert_nothing_raised do
  282. @os.add_file_simple("lib/foo/bar", 0644, 10) {|f| }
  283. end
  284. end
  285. end
  286. class TC_TarReader < Test::Unit::TestCase
  287. include Gem::Package
  288. include TarTester
  289. require 'stringio'
  290. def setup
  291. end
  292. def teardown
  293. end
  294. def test_multiple_entries
  295. str = tar_file_header("lib/foo", "", 010644, 10) + "\0" * 512
  296. str += tar_file_header("bar", "baz", 0644, 0)
  297. str += tar_dir_header("foo", "bar", 012345)
  298. str += "\0" * 1024
  299. names = %w[lib/foo bar foo]
  300. prefixes = ["", "baz", "bar"]
  301. modes = [010644, 0644, 012345]
  302. sizes = [10, 0, 0]
  303. isdir = [false, false, true]
  304. isfile = [true, true, false]
  305. TarReader.new(StringIO.new(str)) do |is|
  306. i = 0
  307. is.each_entry do |entry|
  308. assert_kind_of(TarReader::Entry, entry)
  309. assert_equal(names[i], entry.name)
  310. assert_equal(prefixes[i], entry.prefix)
  311. assert_equal(sizes[i], entry.size)
  312. assert_equal(modes[i], entry.mode)
  313. assert_equal(isdir[i], entry.is_directory?)
  314. assert_equal(isfile[i], entry.is_file?)
  315. if prefixes[i] != ""
  316. assert_equal(File.join(prefixes[i], names[i]),
  317. entry.full_name)
  318. else
  319. assert_equal(names[i], entry.name)
  320. end
  321. i += 1
  322. end
  323. assert_equal(names.size, i)
  324. end
  325. end
  326. def test_rewind_entry_works
  327. content = ('a'..'z').to_a.join(" ")
  328. str = tar_file_header("lib/foo", "", 010644, content.size) + content +
  329. "\0" * (512 - content.size)
  330. str << "\0" * 1024
  331. TarReader.new(StringIO.new(str)) do |is|
  332. is.each_entry do |entry|
  333. 3.times do
  334. entry.rewind
  335. assert_equal(content, entry.read)
  336. assert_equal(content.size, entry.pos)
  337. end
  338. end
  339. end
  340. end
  341. def test_rewind_works
  342. content = ('a'..'z').to_a.join(" ")
  343. str = tar_file_header("lib/foo", "", 010644, content.size) + content +
  344. "\0" * (512 - content.size)
  345. str << "\0" * 1024
  346. TarReader.new(StringIO.new(str)) do |is|
  347. 3.times do
  348. is.rewind
  349. i = 0
  350. is.each_entry do |entry|
  351. assert_equal(content, entry.read)
  352. i += 1
  353. end
  354. assert_equal(1, i)
  355. end
  356. end
  357. end
  358. def test_read_works
  359. contents = ('a'..'z').inject(""){|s,x| s << x * 100}
  360. str = tar_file_header("lib/foo", "", 010644, contents.size) + contents
  361. str += "\0" * (512 - (str.size % 512))
  362. TarReader.new(StringIO.new(str)) do |is|
  363. is.each_entry do |entry|
  364. assert_kind_of(TarReader::Entry, entry)
  365. data = entry.read(3000) # bigger than contents.size
  366. assert_equal(contents, data)
  367. assert_equal(true, entry.eof?)
  368. end
  369. end
  370. TarReader.new(StringIO.new(str)) do |is|
  371. is.each_entry do |entry|
  372. assert_kind_of(TarReader::Entry, entry)
  373. data = entry.read(100)
  374. (entry.size - data.size).times {|i| data << entry.getc.chr }
  375. assert_equal(contents, data)
  376. assert_equal(nil, entry.read(10))
  377. assert_equal(true, entry.eof?)
  378. end
  379. end
  380. TarReader.new(StringIO.new(str)) do |is|
  381. is.each_entry do |entry|
  382. assert_kind_of(TarReader::Entry, entry)
  383. data = entry.read
  384. assert_equal(contents, data)
  385. assert_equal(nil, entry.read(10))
  386. assert_equal(nil, entry.read)
  387. assert_equal(nil, entry.getc)
  388. assert_equal(true, entry.eof?)
  389. end
  390. end
  391. end
  392. def test_eof_works
  393. str = tar_file_header("bar", "baz", 0644, 0)
  394. TarReader.new(StringIO.new(str)) do |is|
  395. is.each_entry do |entry|
  396. assert_kind_of(TarReader::Entry, entry)
  397. data = entry.read
  398. assert_equal(nil, data)
  399. assert_equal(nil, entry.read(10))
  400. assert_equal(nil, entry.read)
  401. assert_equal(nil, entry.getc)
  402. assert_equal(true, entry.eof?)
  403. end
  404. end
  405. str = tar_dir_header("foo", "bar", 012345)
  406. TarReader.new(StringIO.new(str)) do |is|
  407. is.each_entry do |entry|
  408. assert_kind_of(TarReader::Entry, entry)
  409. data = entry.read
  410. assert_equal(nil, data)
  411. assert_equal(nil, entry.read(10))
  412. assert_equal(nil, entry.read)
  413. assert_equal(nil, entry.getc)
  414. assert_equal(true, entry.eof?)
  415. end
  416. end
  417. str = tar_dir_header("foo", "bar", 012345)
  418. str += tar_file_header("bar", "baz", 0644, 0)
  419. str += tar_file_header("bar", "baz", 0644, 0)
  420. TarReader.new(StringIO.new(str)) do |is|
  421. is.each_entry do |entry|
  422. assert_kind_of(TarReader::Entry, entry)
  423. data = entry.read
  424. assert_equal(nil, data)
  425. assert_equal(nil, entry.read(10))
  426. assert_equal(nil, entry.read)
  427. assert_equal(nil, entry.getc)
  428. assert_equal(true, entry.eof?)
  429. end
  430. end
  431. end
  432. class TC_TarInput < Test::Unit::TestCase
  433. include Gem::Package
  434. include TarTester
  435. require 'rbconfig'
  436. require 'zlib'
  437. # Sometimes the setgid bit doesn't take. Don't know if this
  438. # is a problem on all systems, or just some. But for now, we
  439. # will ignore it in the tests.
  440. SETGID_BIT = 02000
  441. def setup
  442. FileUtils.mkdir_p "data__"
  443. inner_tar = tar_file_header("bla", "", 0612, 10)
  444. inner_tar += "0123456789" + "\0" * 502
  445. inner_tar += tar_file_header("foo", "", 0636, 5)
  446. inner_tar += "01234" + "\0" * 507
  447. inner_tar += tar_dir_header("__dir__", "", 0600)
  448. inner_tar += "\0" * 1024
  449. str = StringIO.new ""
  450. begin
  451. os = Zlib::GzipWriter.new str
  452. os.write inner_tar
  453. ensure
  454. os.finish
  455. end
  456. str.rewind
  457. File.open("data__/bla.tar", "wb") do |f|
  458. f.write tar_file_header("data.tar.gz", "", 0644, str.string.size)
  459. f.write str.string
  460. f.write "\0" * ((512 - (str.string.size % 512)) % 512 )
  461. @spec = Gem::Specification.new do |spec|
  462. spec.author = "Mauricio :)"
  463. end
  464. meta = @spec.to_yaml
  465. f.write tar_file_header("metadata", "", 0644, meta.size)
  466. f.write meta + "\0" * (1024 - meta.size)
  467. f.write "\0" * 1024
  468. end
  469. @file = "data__/bla.tar"
  470. @entry_names = %w{bla foo __dir__}
  471. @entry_sizes = [10, 5, 0]
  472. #FIXME: are these modes system dependent?
  473. @entry_modes = [0100612, 0100636, 040600]
  474. @entry_files = %w{data__/bla data__/foo}
  475. @entry_contents = %w[0123456789 01234]
  476. end
  477. def teardown
  478. # FileUtils.rm_rf "data__"
  479. end
  480. def test_each_works
  481. TarInput.open(@file) do |is|
  482. i = 0
  483. is.each_with_index do |entry, i|
  484. assert_kind_of(TarReader::Entry, entry)
  485. assert_equal(@entry_names[i], entry.name)
  486. assert_equal(@entry_sizes[i], entry.size)
  487. end
  488. assert_equal(2, i)
  489. assert_equal(@spec, is.metadata)
  490. end
  491. end
  492. def test_extract_entry_works
  493. TarInput.open(@file) do |is|
  494. assert_equal(@spec,is.metadata)
  495. i = 0
  496. is.each_with_index do |entry, i|
  497. is.extract_entry "data__", entry
  498. name = File.join("data__", entry.name)
  499. if entry.is_directory?
  500. assert File.dir?(name)
  501. else
  502. assert File.file?(name)
  503. assert_equal(@entry_sizes[i], File.stat(name).size)
  504. #FIXME: win32? !!
  505. end
  506. unless ::Config::CONFIG["arch"] =~ /msdos|win32/i
  507. assert_equal(@entry_modes[i],
  508. File.stat(name).mode & (~SETGID_BIT))
  509. end
  510. end
  511. assert_equal(2, i)
  512. end
  513. @entry_files.each_with_index do |x, i|
  514. assert(File.file?(x))
  515. assert_equal(@entry_contents[i], File.read_b(x))
  516. end
  517. end
  518. end
  519. class TC_TarOutput < Test::Unit::TestCase
  520. include Gem::Package
  521. include TarTester
  522. require 'zlib'
  523. def setup
  524. FileUtils.mkdir_p "data__", :verbose=>false
  525. @file = "data__/bla2.tar"
  526. end
  527. def teardown
  528. FileUtils.rm_rf "data__"
  529. end
  530. require 'zlib'
  531. def test_file_looks_good
  532. TarOutput.open(@file) do |os|
  533. os.metadata = "bla".to_yaml
  534. end
  535. f = File.open(@file, "rb")
  536. TarReader.new(f) do |is|
  537. i = 0
  538. is.each do |entry|
  539. case i
  540. when 0
  541. assert_equal("data.tar.gz", entry.name)
  542. when 1
  543. assert_equal("metadata.gz", entry.name)
  544. gzis = Zlib::GzipReader.new entry
  545. assert_equal("bla".to_yaml, gzis.read)
  546. gzis.close
  547. end
  548. i += 1
  549. end
  550. assert_equal(2, i)
  551. end
  552. ensure
  553. f.close
  554. end
  555. end
  556. end