@@ -11,7 +11,9 @@ contributions. There are a few DOs and DON'Ts that should be followed:
1111 - Use thoughtfully-named topic branches for contributions. Rebase your commits
1212 into logical chunks as necessary.
1313
14- - Use [ quality commit messages] [ qcm ] .
14+ - Use [ quality commit messages] [ qcm ] for each commit (minitar uses a rebase
15+ merge strategy). Ensure that each commit includes the required Developer
16+ Certificate of Origin [ sign-off] [ sign-off ] .
1517
1618 - Add your name or GitHub handle to ` CONTRIBUTORS.md ` and a record in the
1719 ` CHANGELOG.md ` as a separate commit from your main change. (Follow the style
@@ -49,7 +51,10 @@ without such declaration, the pull request **will be declined**.
4951Any contribution (bug, feature request, or pull request) that uses unreviewed
5052LLM output will be rejected.
5153
52- ## Test Dependencies
54+ For an example of how this should be done, see [ #151 ] [ pr-151 ] and its
55+ [ associated commits] [ pr-151-commits ] .
56+
57+ ## Test
5358
5459minitar uses Ryan Davis's [ Hoe] [ Hoe ] to manage the release process, and it adds
5560a number of rake tasks. You will mostly be interested in ` rake ` , which runs
@@ -62,6 +67,186 @@ the development dependencies.
6267
6368You can run tests with code coverage analysis by running ` rake coverage ` .
6469
70+ ### Test Helpers
71+
72+ Minitar includes a number of custom test assertions, constants, and test utility
73+ methods that are useful for writing tests. These are maintained through modules
74+ defined in ` test/support ` .
75+
76+ #### Fixture Utilities
77+
78+ Minitar uses fixture tarballs in various tests, referenced by their base name
79+ (` test/fixtures/tar_input.tar.gz ` becomes ` tar_input ` , etc.). There are two
80+ utility methods:
81+
82+ - ` Fixture(name) ` : This returns the ` Pathname ` object for the full path of the
83+ named fixture tarball or ` nil ` if the named fixture does not exist.
84+
85+ - ` open_fixture(name) ` : This retrieves the named fixture and opens it. If the
86+ fixture ends with ` .gz ` or ` .tgz ` , it will be opened with a
87+ ` Zlib::GZipReader ` . A block may be provided to ensure that the fixture is
88+ automatically closed.
89+
90+ #### Header Assertions and Utilities
91+
92+ Tar headers need to be built and compared in an exacting way, even for tests.
93+
94+ There are two assertions:
95+
96+ - ` assert_headers_equal(expected, actual) ` : This compares headers by field order
97+ verifying that each field in ` actual ` is supposed to match the corresponding
98+ field in ` expected ` .
99+
100+ ` expected ` must be a string representation of the expected header and this
101+ assertion calls ` #to_s ` on the ` actual ` value so that both ` PosixHeader ` and
102+ ` PaxHeader ` instances are converted to string representations for comparison.
103+
104+ - ` assert_modes_equal(expected, actual, filename) ` : This compares the expected
105+ octal mode string of ` expected ` against ` actual ` for a given ` filename ` . The
106+ modes must be integer values. This assertion is skipped on Windows.
107+
108+ There are several other helper methods available for working with headers:
109+
110+ - ` build_tar_file_header(name, prefix, mode, length) ` : This builds a header for
111+ a file ` prefix/name ` with ` mode ` and ` length ` bytes. ` name ` is limited to 100
112+ bytes and ` prefix ` is limited to 155 bytes.
113+
114+ - ` build_tar_dir_header(name, prefix, mode) ` : This builds a header for a
115+ directory ` prefix/name ` with ` mode ` . ` name ` is limited to 100 bytes and
116+ ` prefix ` is limited to 155 bytes.
117+
118+ - ` build_tar_symlink_header(name, prefix, mode, target) ` : This builds a header
119+ for a symbolic link of ` prefix/name ` to ` target ` where the symbolic link has
120+ ` mode ` . ` name ` is limited to 100 bytes and ` prefix ` is limited to 155 bytes.
121+
122+ - ` build_tar_pax_header(name, prefix, bytes) ` : This builds a header block for a
123+ PAX extension at ` name/prefix ` with ` content_size ` bytes.
124+
125+ - ` build_header(type, name, prefix, size, mode, link = "") ` : This builds an
126+ otherwise unspecified header type. If you find yourself using this, it is
127+ recommended to add a new ` build_*_header ` helper method.
128+
129+ #### Tarball Helpers
130+
131+ Minitar has several complex assertions and utilities to work with both in-memory
132+ and on-disk tarballs. These work using two concepts, file hashes (` file_hash ` )
133+ and workspaces (` workspace ` ).
134+
135+ ##### File Hashes (` file_hash ` )
136+
137+ Many of these consume or produce a ` file_hash ` , which is a hash of
138+ ` {filename => content} ` where the tarball will be produced with such that each
139+ entry in the ` file_hash ` becomes a file named ` filename ` with the data
140+ ` content ` .
141+
142+ As an example, ` Minitar::TestHelpers ` has a ` MIXED_FILENAME_SCENARIOS ` constant
143+ that is a ` file_hash ` :
144+
145+ ``` ruby
146+ MIXED_FILENAME_SCENARIOS = {
147+ " short.txt" => " short content" ,
148+ " medium_length_filename_under_100_chars.txt" => " medium content" ,
149+ " dir1/medium_filename.js" => " medium nested content" ,
150+ " #{ " x" * 120 } .txt" => " long content" ,
151+ " nested/dir/#{ " y" * 110 } .css" => " long nested content"
152+ }.freeze
153+ ```
154+
155+ This will produce a tarball that looks like:
156+
157+ ```
158+ short.txt
159+ medium_length_filename_under_100_chars.txt
160+ dir1/medium_filename.js
161+ x[118 more 'x' characters...]x
162+ nested/dir/y[108 more y' characters...]y.css
163+ ```
164+
165+ Each file will contain the text as the content.
166+
167+ If the ` content ` is ` nil ` , this will be ignored for in-memory tarballs, but will
168+ be created as empty directory entries for on-disk tarballs.
169+
170+ ##### Workspace (` workspace ` )
171+
172+ A workspace is a temporary directory used for on-disk tests. It is created with
173+ the ` workspace ` utility method (see below) and must be passed a block where all
174+ setup and tests will be run.
175+
176+ At most one ` workspace ` may be used per test method.
177+
178+ ##### Assertions
179+
180+ There are five assertions:
181+
182+ - ` assert_tar_structure_preserved(original_files, extracted_files) ` : This is
183+ used primarily with string tarballs. Given two ` file_hash ` es representing
184+ tarball contents (the original files passed to ` create_tar_string ` and the
185+ extracted files returned from ` extract_tar_string ` ), it ensures that all files
186+ from the original contents are present and that no additional files have been
187+ added in the process.
188+
189+ - ` assert_files_extracted_in_workspace ` : Can only be run in a ` workspace ` and
190+ the test tarball must have been both created and extracted. This ensures that
191+ all of the files and/or directories expected have been extracted and that the
192+ contents of files match. File modes are ignored for this assertion.
193+
194+ - ` refute_file_path_duplication_in_workspace ` : Can only be run in a ` workspace `
195+ and the test tarball must have been both created and extracted. This is used
196+ to prevent regression of [ #62 ] [ issue-62 ] with explicit file tests. This only
197+ needs to be called after unpacking with Minitar methods.
198+
199+ - ` assert_extracted_files_match_source_files_in_workspace ` : Can only be run in a
200+ ` workspace ` and the test tarball must have been both created and extracted.
201+ This ensures that there are no files missing or added in the ` target `
202+ directory that should are not also be in the ` source ` directory. This does no
203+ contents comparison.
204+
205+ - ` assert_file_modes_match_in_workspace ` : Can only be run in a ` workspace ` and
206+ the test tarball must have been both created and extracted. This ensures that
207+ all files have the same modes between source and target. This is skipped on
208+ Windows.
209+
210+ ##### In-Memory Tarball Utilities
211+
212+ - ` create_tar_string ` : Given a ` file_hash ` , this creates a string containing the
213+ output of ` Minitar::Output.open ` and ` Minitar.pack_as_file ` .
214+
215+ - ` extract_tar_string ` : Given the string output of ` create_tar_string ` (or any
216+ uncompressed tarball string), uses ` Minitar::Input.open ` to read the files
217+ into a hash of ` {filename => content} ` .
218+
219+ - ` roundtrip_tar_string ` : calls ` create_tar_string ` on a ` file_hash ` and
220+ immediately calls ` extract_tar_string ` , returning a processed ` file_hash ` .
221+
222+ ##### On-Disk Workspace Tarball Utilities
223+
224+ - ` workspace ` : Prepares a temporary directory for working with tarballs on disk
225+ inside the block that must be provided. If given a hash of files, calls
226+ ` prepare_files ` . The workspace directory will be removed after the block
227+ finishes executing.
228+
229+ A workspace has a ` source ` directory, a ` target ` directory` , and the ` tarball`
230+ which will be created from the prepared files.
231+
232+ All other utility methods _ must_ be run inside of a ` workspace ` block.
233+
234+ - ` prepare_workspace ` : creates a file structure in the workspace source
235+ directory given the ` {filename => content} ` hash. For on-disk file structures,
236+ ` {directory_name => nil} ` can be used to create empty directories. Directory
237+ names will be created automatically for nested filenames.
238+
239+ - ` gnu_tar_create_in_workspace ` , ` gnu_tar_extract_in_workspace ` , and
240+ ` gnu_tar_list_in_workspace ` work with the workspace tarball using GNU tar
241+ (either ` tar ` or ` gtar ` ). GNU tar tests will be skipped if GNU tar is not
242+ available.
243+
244+ - ` minitar_pack_in_workspace ` , ` minitar_unpack_in_workspace ` use ` Minitar.pack `
245+ and ` Minitar.unpack ` , respectively, to work with the workspace tarball.
246+
247+ - ` minitar_writer_create_in_workspace ` uses ` Minitar::Writer ` to create the
248+ workspace tarball.
249+
65250## Workflow
66251
67252Here's the most direct way to get your work merged into the project:
@@ -79,6 +264,10 @@ Here's the most direct way to get your work merged into the project:
79264
80265[ dco ] : licences/dco.txt
81266[ hoe ] : https://github.com/seattlerb/hoe
267+ [ issue-62 ] : https://github.com/halostatue/minitar/issues/62
82268[ minitest ] : https://github.com/seattlerb/minitest
269+ [ pr-151-commits ] : https://github.com/halostatue/minitar/pull/151/commits
270+ [ pr-151 ] : https://github.com/halostatue/minitar/pull/151
83271[ qcm ] : http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
272+ [ sign-off ] : LICENCE.md#developer-certificate-of-origin
84273[ standardrb ] : https://github.com/standardrb/standard
0 commit comments