@ -53,11 +53,15 @@ import java.io.FileOutputStream;
import java.io.InputStreamReader ;
import java.io.InputStreamReader ;
import java.io.IOException ;
import java.io.IOException ;
import java.io.OutputStream ;
import java.io.OutputStream ;
import java.lang.Object ;
import java.lang.String ;
import java.lang.String ;
import java.util.ArrayList ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.List ;
import java.util.List ;
import java.util.concurrent.Callable ;
import java.util.concurrent.Executors ;
import java.util.concurrent.ExecutorService ;
import java.util.concurrent.Future ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipInputStream ;
import java.util.zip.ZipInputStream ;
@ -91,6 +95,13 @@ public class ArchiveTest extends CLIRepositoryTestCase {
assertArrayEquals ( new String [ 0 ] , listZipEntries ( result ) ) ;
assertArrayEquals ( new String [ 0 ] , listZipEntries ( result ) ) ;
}
}
@Test
public void testEmptyTar ( ) throws Exception {
final byte [ ] result = CLIGitCommand . rawExecute ( //
"git archive --format=tar " + emptyTree , db ) ;
assertArrayEquals ( new String [ 0 ] , listTarEntries ( result ) ) ;
}
@Test
@Test
public void testArchiveWithFiles ( ) throws Exception {
public void testArchiveWithFiles ( ) throws Exception {
writeTrashFile ( "a" , "a file with content!" ) ;
writeTrashFile ( "a" , "a file with content!" ) ;
@ -132,6 +143,32 @@ public class ArchiveTest extends CLIRepositoryTestCase {
assertArrayEquals ( expect , actual ) ;
assertArrayEquals ( expect , actual ) ;
}
}
@Test
public void testTarWithSubdir ( ) throws Exception {
writeTrashFile ( "a" , "a file with content!" ) ;
writeTrashFile ( "b.c" , "before subdir in git sort order" ) ;
writeTrashFile ( "b0c" , "after subdir in git sort order" ) ;
writeTrashFile ( "c" , "" ) ;
git . add ( ) . addFilepattern ( "a" ) . call ( ) ;
git . add ( ) . addFilepattern ( "b.c" ) . call ( ) ;
git . add ( ) . addFilepattern ( "b0c" ) . call ( ) ;
git . add ( ) . addFilepattern ( "c" ) . call ( ) ;
git . commit ( ) . setMessage ( "populate toplevel" ) . call ( ) ;
writeTrashFile ( "b/b" , "file in subdirectory" ) ;
writeTrashFile ( "b/a" , "another file in subdirectory" ) ;
git . add ( ) . addFilepattern ( "b" ) . call ( ) ;
git . commit ( ) . setMessage ( "add subdir" ) . call ( ) ;
final byte [ ] result = CLIGitCommand . rawExecute ( //
"git archive --format=tar master" , db ) ;
String [ ] expect = { "a" , "b.c" , "b0c" , "b/a" , "b/b" , "c" } ;
String [ ] actual = listTarEntries ( result ) ;
Arrays . sort ( expect ) ;
Arrays . sort ( actual ) ;
assertArrayEquals ( expect , actual ) ;
}
@Test
@Test
public void testArchivePreservesMode ( ) throws Exception {
public void testArchivePreservesMode ( ) throws Exception {
writeTrashFile ( "plain" , "a file with content" ) ;
writeTrashFile ( "plain" , "a file with content" ) ;
@ -158,6 +195,32 @@ public class ArchiveTest extends CLIRepositoryTestCase {
assertContainsEntryWithMode ( "zip-with-modes.zip" , "l" , "symlink" ) ;
assertContainsEntryWithMode ( "zip-with-modes.zip" , "l" , "symlink" ) ;
}
}
@Test
public void testTarPreservesMode ( ) throws Exception {
writeTrashFile ( "plain" , "a file with content" ) ;
writeTrashFile ( "executable" , "an executable file" ) ;
writeTrashFile ( "symlink" , "plain" ) ;
git . add ( ) . addFilepattern ( "plain" ) . call ( ) ;
git . add ( ) . addFilepattern ( "executable" ) . call ( ) ;
git . add ( ) . addFilepattern ( "symlink" ) . call ( ) ;
DirCache cache = db . lockDirCache ( ) ;
cache . getEntry ( "executable" ) . setFileMode ( FileMode . EXECUTABLE_FILE ) ;
cache . getEntry ( "symlink" ) . setFileMode ( FileMode . SYMLINK ) ;
cache . write ( ) ;
cache . commit ( ) ;
cache . unlock ( ) ;
git . commit ( ) . setMessage ( "three files with different modes" ) . call ( ) ;
final byte [ ] archive = CLIGitCommand . rawExecute ( //
"git archive --format=tar master" , db ) ;
writeRaw ( "with-modes.tar" , archive ) ;
assertTarContainsEntry ( "with-modes.tar" , "-rw-r--r--" , "plain" ) ;
assertTarContainsEntry ( "with-modes.tar" , "-rwxr-xr-x" , "executable" ) ;
assertTarContainsEntry ( "with-modes.tar" , "l" , "symlink -> plain" ) ;
}
@Test
@Test
public void testArchivePreservesContent ( ) throws Exception {
public void testArchivePreservesContent ( ) throws Exception {
final String payload = "“The quick brown fox jumps over the lazy dog!”" ;
final String payload = "“The quick brown fox jumps over the lazy dog!”" ;
@ -171,23 +234,45 @@ public class ArchiveTest extends CLIRepositoryTestCase {
zipEntryContent ( result , "xyzzy" ) ) ;
zipEntryContent ( result , "xyzzy" ) ) ;
}
}
private void assertContainsEntryWithMode ( String zipFilename , String mode , String name ) //
@Test
throws Exception {
public void testTarPreservesContent ( ) throws Exception {
final String payload = "“The quick brown fox jumps over the lazy dog!”" ;
writeTrashFile ( "xyzzy" , payload ) ;
git . add ( ) . addFilepattern ( "xyzzy" ) . call ( ) ;
git . commit ( ) . setMessage ( "add file with content" ) . call ( ) ;
final byte [ ] result = CLIGitCommand . rawExecute ( //
"git archive --format=tar HEAD" , db ) ;
assertArrayEquals ( new String [ ] { payload } , //
tarEntryContent ( result , "xyzzy" ) ) ;
}
private Process spawnAssumingCommandPresent ( String . . . cmdline ) {
final File cwd = db . getWorkTree ( ) ;
final File cwd = db . getWorkTree ( ) ;
final ProcessBuilder procBuilder = new ProcessBuilder ( "zipinfo" , zipFilename ) //
final ProcessBuilder procBuilder = new ProcessBuilder ( cmdl ine) //
. directory ( cwd ) //
. directory ( cwd ) //
. redirectErrorStream ( true ) ;
. redirectErrorStream ( true ) ;
Process proc = null ;
Process proc = null ;
try {
try {
proc = procBuilder . start ( ) ;
proc = procBuilder . start ( ) ;
} catch ( IOException e ) {
} catch ( IOException e ) {
// On machines without a "zipinfo" command, let the test pass.
// On machines without `cmdline[0]` , let the test pass.
assumeNoException ( e ) ;
assumeNoException ( e ) ;
}
}
proc . getOutputStream ( ) . close ( ) ;
return proc ;
final BufferedReader reader = new BufferedReader ( //
}
private BufferedReader readFromProcess ( Process proc ) throws Exception {
return new BufferedReader ( //
new InputStreamReader ( proc . getInputStream ( ) , "UTF-8" ) ) ;
new InputStreamReader ( proc . getInputStream ( ) , "UTF-8" ) ) ;
}
private void grepForEntry ( String name , String mode , String . . . cmdline ) //
throws Exception {
final Process proc = spawnAssumingCommandPresent ( cmdline ) ;
proc . getOutputStream ( ) . close ( ) ;
final BufferedReader reader = readFromProcess ( proc ) ;
try {
try {
String line ;
String line ;
while ( ( line = reader . readLine ( ) ) ! = null )
while ( ( line = reader . readLine ( ) ) ! = null )
@ -201,6 +286,16 @@ public class ArchiveTest extends CLIRepositoryTestCase {
}
}
}
}
private void assertContainsEntryWithMode ( String zipFilename , String mode , String name ) //
throws Exception {
grepForEntry ( name , mode , "zipinfo" , zipFilename ) ;
}
private void assertTarContainsEntry ( String tarfile , String mode , String name ) //
throws Exception {
grepForEntry ( name , mode , "tar" , "tvf" , tarfile ) ;
}
private void writeRaw ( String filename , byte [ ] data ) //
private void writeRaw ( String filename , byte [ ] data ) //
throws IOException {
throws IOException {
final File path = new File ( db . getWorkTree ( ) , filename ) ;
final File path = new File ( db . getWorkTree ( ) , filename ) ;
@ -224,6 +319,43 @@ public class ArchiveTest extends CLIRepositoryTestCase {
return l . toArray ( new String [ l . size ( ) ] ) ;
return l . toArray ( new String [ l . size ( ) ] ) ;
}
}
private static Future < Object > writeAsync ( final OutputStream stream , final byte [ ] data ) {
final ExecutorService executor = Executors . newSingleThreadExecutor ( ) ;
return executor . submit ( new Callable < Object > ( ) { //
public Object call ( ) throws IOException {
try {
stream . write ( data ) ;
return null ;
} finally {
stream . close ( ) ;
}
}
} ) ;
}
private String [ ] listTarEntries ( byte [ ] tarData ) throws Exception {
final List < String > l = new ArrayList < String > ( ) ;
final Process proc = spawnAssumingCommandPresent ( "tar" , "tf" , "-" ) ;
final BufferedReader reader = readFromProcess ( proc ) ;
final OutputStream out = proc . getOutputStream ( ) ;
// Dump tarball to tar stdin in background
final Future < ? > writing = writeAsync ( out , tarData ) ;
try {
String line ;
while ( ( line = reader . readLine ( ) ) ! = null )
l . add ( line ) ;
return l . toArray ( new String [ l . size ( ) ] ) ;
} finally {
writing . get ( ) ;
reader . close ( ) ;
proc . destroy ( ) ;
}
}
private static String [ ] zipEntryContent ( byte [ ] zipData , String path ) //
private static String [ ] zipEntryContent ( byte [ ] zipData , String path ) //
throws IOException {
throws IOException {
final ZipInputStream in = new ZipInputStream ( //
final ZipInputStream in = new ZipInputStream ( //
@ -246,4 +378,25 @@ public class ArchiveTest extends CLIRepositoryTestCase {
// not found
// not found
return null ;
return null ;
}
}
private String [ ] tarEntryContent ( byte [ ] tarData , String path ) //
throws Exception {
final List < String > l = new ArrayList < String > ( ) ;
final Process proc = spawnAssumingCommandPresent ( "tar" , "Oxf" , "-" , path ) ;
final BufferedReader reader = readFromProcess ( proc ) ;
final OutputStream out = proc . getOutputStream ( ) ;
final Future < ? > writing = writeAsync ( out , tarData ) ;
try {
String line ;
while ( ( line = reader . readLine ( ) ) ! = null )
l . add ( line ) ;
return l . toArray ( new String [ l . size ( ) ] ) ;
} finally {
writing . get ( ) ;
reader . close ( ) ;
proc . destroy ( ) ;
}
}
}
}