]> git.saurik.com Git - apt.git/commitdiff
fix test/integration/test-apt-helper
authorMichael Vogt <mvo@debian.org>
Fri, 14 Mar 2014 08:02:44 +0000 (09:02 +0100)
committerMichael Vogt <mvo@debian.org>
Fri, 14 Mar 2014 08:02:44 +0000 (09:02 +0100)
1  2 
apt-pkg/contrib/fileutl.cc
apt-pkg/contrib/fileutl.h
cmdline/apt-helper.cc
test/integration/framework
test/integration/test-apt-helper

index 79bcf112c162563b47dcb50945238cab5d34e3f2,26945c183f00ef446dfdd2ececc10c1243b4c188..1eabf37f4e5906f74ad4673a5f6bb859a0dae01d
@@@ -1,6 -1,5 +1,5 @@@
  // -*- mode: cpp; mode: fold -*-
  // Description                                                                /*{{{*/
- // $Id: fileutl.cc,v 1.42 2002/09/14 05:29:22 jgg Exp $
  /* ######################################################################
     
     File Utilities
  #include <apt-pkg/sptr.h>
  #include <apt-pkg/aptconfiguration.h>
  #include <apt-pkg/configuration.h>
+ #include <apt-pkg/macros.h>
+ #include <ctype.h>
+ #include <stdarg.h>
+ #include <stddef.h>
+ #include <sys/select.h>
+ #include <time.h>
+ #include <string>
+ #include <vector>
  #include <cstdlib>
  #include <cstring>
  #include <cstdio>
  #include <iostream>
  #include <unistd.h>
  #include <fcntl.h>
  #include <sys/stat.h>
- #include <sys/types.h>
  #include <sys/time.h>
  #include <sys/wait.h>
  #include <dirent.h>
  #ifdef HAVE_BZ2
        #include <bzlib.h>
  #endif
+ #ifdef HAVE_LZMA
+       #include <stdint.h>
+       #include <lzma.h>
+ #endif
  
  #ifdef WORDS_BIGENDIAN
  #include <inttypes.h>
  
  using namespace std;
  
- class FileFdPrivate {
-       public:
- #ifdef HAVE_ZLIB
-       gzFile gz;
- #else
-       void* gz;
- #endif
- #ifdef HAVE_BZ2
-       BZFILE* bz2;
- #else
-       void* bz2;
- #endif
-       int compressed_fd;
-       pid_t compressor_pid;
-       bool pipe;
-       APT::Configuration::Compressor compressor;
-       unsigned int openmode;
-       unsigned long long seekpos;
-       FileFdPrivate() : gz(NULL), bz2(NULL),
-                         compressed_fd(-1), compressor_pid(-1), pipe(false),
-                         openmode(0), seekpos(0) {};
-       bool CloseDown(std::string const &FileName)
-       {
-          bool Res = true;
- #ifdef HAVE_ZLIB
-          if (gz != NULL) {
-             int const e = gzclose(gz);
-             gz = NULL;
-             // gzdclose() on empty files always fails with "buffer error" here, ignore that
-             if (e != 0 && e != Z_BUF_ERROR)
-                Res &= _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
-          }
- #endif
- #ifdef HAVE_BZ2
-          if (bz2 != NULL) {
-             BZ2_bzclose(bz2);
-             bz2 = NULL;
-          }
- #endif
-          if (compressor_pid > 0)
-             ExecWait(compressor_pid, "FileFdCompressor", true);
-          compressor_pid = -1;
-          return Res;
-       }
-       ~FileFdPrivate() { CloseDown(""); }
- };
  // RunScripts - Run a set of scripts from a configuration subtree     /*{{{*/
  // ---------------------------------------------------------------------
  /* */
@@@ -874,6 -835,122 +835,122 @@@ bool ExecWait(pid_t Pid,const char *Nam
  }
                                                                        /*}}}*/
  
+ class FileFdPrivate {                                                 /*{{{*/
+       public:
+ #ifdef HAVE_ZLIB
+       gzFile gz;
+ #endif
+ #ifdef HAVE_BZ2
+       BZFILE* bz2;
+ #endif
+ #ifdef HAVE_LZMA
+       struct LZMAFILE {
+          FILE* file;
+          uint8_t buffer[4096];
+          lzma_stream stream;
+          lzma_ret err;
+          bool eof;
+          bool compressing;
+          LZMAFILE() : file(NULL), eof(false), compressing(false) {}
+          ~LZMAFILE() {
+             if (compressing == true)
+             {
+               for (;;) {
+                       stream.avail_out = sizeof(buffer)/sizeof(buffer[0]);
+                       stream.next_out = buffer;
+                       err = lzma_code(&stream, LZMA_FINISH);
+                       if (err != LZMA_OK && err != LZMA_STREAM_END)
+                       {
+                               _error->Error("~LZMAFILE: Compress finalisation failed");
+                               break;
+                       }
+                       size_t const n =  sizeof(buffer)/sizeof(buffer[0]) - stream.avail_out;
+                       if (n && fwrite(buffer, 1, n, file) != n)
+                       {
+                               _error->Errno("~LZMAFILE",_("Write error"));
+                               break;
+                       }
+                       if (err == LZMA_STREAM_END)
+                               break;
+               }
+             }
+             lzma_end(&stream);
+             fclose(file);
+          }
+       };
+       LZMAFILE* lzma;
+ #endif
+       int compressed_fd;
+       pid_t compressor_pid;
+       bool pipe;
+       APT::Configuration::Compressor compressor;
+       unsigned int openmode;
+       unsigned long long seekpos;
+       FileFdPrivate() :
+ #ifdef HAVE_ZLIB
+                         gz(NULL),
+ #endif
+ #ifdef HAVE_BZ2
+                         bz2(NULL),
+ #endif
+ #ifdef HAVE_LZMA
+                         lzma(NULL),
+ #endif
+                         compressed_fd(-1), compressor_pid(-1), pipe(false),
+                         openmode(0), seekpos(0) {};
+       bool InternalClose(std::string const &FileName)
+       {
+          if (false)
+             /* dummy so that the rest can be 'else if's */;
+ #ifdef HAVE_ZLIB
+          else if (gz != NULL) {
+             int const e = gzclose(gz);
+             gz = NULL;
+             // gzdclose() on empty files always fails with "buffer error" here, ignore that
+             if (e != 0 && e != Z_BUF_ERROR)
+                return _error->Errno("close",_("Problem closing the gzip file %s"), FileName.c_str());
+          }
+ #endif
+ #ifdef HAVE_BZ2
+          else if (bz2 != NULL) {
+             BZ2_bzclose(bz2);
+             bz2 = NULL;
+          }
+ #endif
+ #ifdef HAVE_LZMA
+          else if (lzma != NULL) {
+             delete lzma;
+             lzma = NULL;
+          }
+ #endif
+          return true;
+       }
+       bool CloseDown(std::string const &FileName)
+       {
+          bool const Res = InternalClose(FileName);
+          if (compressor_pid > 0)
+             ExecWait(compressor_pid, "FileFdCompressor", true);
+          compressor_pid = -1;
+          return Res;
+       }
+       bool InternalStream() const {
+          return false
+ #ifdef HAVE_BZ2
+             || bz2 != NULL
+ #endif
+ #ifdef HAVE_LZMA
+             || lzma != NULL
+ #endif
+             ;
+       }
+       ~FileFdPrivate() { CloseDown(""); }
+ };
+                                                                       /*}}}*/
  // FileFd::Open - Open a file                                         /*{{{*/
  // ---------------------------------------------------------------------
  /* The most commonly used open mode combinations are given with Mode */
@@@ -891,7 -968,7 +968,7 @@@ bool FileFd::Open(string FileName,unsig
     {
        for (; compressor != compressors.end(); ++compressor)
        {
-        std::string file = std::string(FileName).append(compressor->Extension);
+        std::string file = FileName + compressor->Extension;
         if (FileExists(file) == false)
            continue;
         FileName = file;
@@@ -1061,30 -1138,12 +1138,12 @@@ bool FileFd::OpenDescriptor(int Fd, uns
  {
     Close();
     Flags = (AutoClose) ? FileFd::AutoClose : 0;
-    if (AutoClose == false && (
- #ifdef HAVE_ZLIB
-       compressor.Name == "gzip" ||
- #endif
- #ifdef HAVE_BZ2
-       compressor.Name == "bzip2" ||
- #endif
-       false))
-    {
-       // Need to duplicate fd here or gzclose for cleanup will close the fd as well
-       iFd = dup(Fd);
-    }
-    else
-       iFd = Fd;
+    iFd = Fd;
     this->FileName = "";
-    if (Fd == -1 || OpenInternDescriptor(Mode, compressor) == false)
+    if (OpenInternDescriptor(Mode, compressor) == false)
     {
        if (iFd != -1 && (
- #ifdef HAVE_ZLIB
-       compressor.Name == "gzip" ||
- #endif
- #ifdef HAVE_BZ2
-       compressor.Name == "bzip2" ||
- #endif
+       (Flags & Compressed) == Compressed ||
        AutoClose == true))
        {
         close (iFd);
  }
  bool FileFd::OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor)
  {
+    if (iFd == -1)
+       return false;
     if (compressor.Name == "." || compressor.Binary.empty() == true)
        return true;
  
+ #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+    // the API to open files is similar, so setup to avoid code duplicates later
+    // and while at it ensure that we close before opening (if its a reopen)
+    void* (*compress_open)(int, const char *) = NULL;
+    if (false)
+       /* dummy so that the rest can be 'else if's */;
+ #define APT_COMPRESS_INIT(NAME,OPEN) \
+    else if (compressor.Name == NAME) \
+    { \
+       compress_open = (void*(*)(int, const char *)) OPEN; \
+       if (d != NULL) d->InternalClose(FileName); \
+    }
+ #ifdef HAVE_ZLIB
+    APT_COMPRESS_INIT("gzip", gzdopen)
+ #endif
+ #ifdef HAVE_BZ2
+    APT_COMPRESS_INIT("bzip2", BZ2_bzdopen)
+ #endif
+ #ifdef HAVE_LZMA
+    APT_COMPRESS_INIT("xz", fdopen)
+    APT_COMPRESS_INIT("lzma", fdopen)
+ #endif
+ #undef APT_COMPRESS_INIT
+ #endif
     if (d == NULL)
     {
        d = new FileFdPrivate();
        d->openmode = Mode;
        d->compressor = compressor;
+ #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+       if (AutoClose == false && compress_open != NULL)
+       {
+        // Need to duplicate fd here or gz/bz2 close for cleanup will close the fd as well
+        int const internFd = dup(iFd);
+        if (internFd == -1)
+           return FileFdErrno("OpenInternDescriptor", _("Could not open file descriptor %d"), iFd);
+        iFd = internFd;
+       }
+ #endif
     }
  
- #ifdef HAVE_ZLIB
-    if (compressor.Name == "gzip")
+ #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+    if (compress_open != NULL)
     {
-       if (d->gz != NULL)
-       {
-        gzclose(d->gz);
-        d->gz = NULL;
-       }
+       void* compress_struct = NULL;
        if ((Mode & ReadWrite) == ReadWrite)
-        d->gz = gzdopen(iFd, "r+");
+        compress_struct = compress_open(iFd, "r+");
        else if ((Mode & WriteOnly) == WriteOnly)
-        d->gz = gzdopen(iFd, "w");
+        compress_struct = compress_open(iFd, "w");
        else
-        d->gz = gzdopen(iFd, "r");
-       if (d->gz == NULL)
+        compress_struct = compress_open(iFd, "r");
+       if (compress_struct == NULL)
         return false;
-       Flags |= Compressed;
-       return true;
-    }
+       if (false)
+        /* dummy so that the rest can be 'else if's */;
+ #ifdef HAVE_ZLIB
+       else if (compressor.Name == "gzip")
+        d->gz = (gzFile) compress_struct;
  #endif
  #ifdef HAVE_BZ2
-    if (compressor.Name == "bzip2")
-    {
-       if (d->bz2 != NULL)
+       else if (compressor.Name == "bzip2")
+        d->bz2 = (BZFILE*) compress_struct;
+ #endif
+ #ifdef HAVE_LZMA
+       else if (compressor.Name == "xz" || compressor.Name == "lzma")
        {
-        BZ2_bzclose(d->bz2);
-        d->bz2 = NULL;
+        uint32_t const xzlevel = 6;
+        uint64_t const memlimit = UINT64_MAX;
+        if (d->lzma == NULL)
+           d->lzma = new FileFdPrivate::LZMAFILE;
+        d->lzma->file = (FILE*) compress_struct;
+        d->lzma->stream = LZMA_STREAM_INIT;
+        if ((Mode & ReadWrite) == ReadWrite)
+           return FileFdError("ReadWrite mode is not supported for file %s", FileName.c_str());
+        if ((Mode & WriteOnly) == WriteOnly)
+        {
+           if (compressor.Name == "xz")
+           {
+              if (lzma_easy_encoder(&d->lzma->stream, xzlevel, LZMA_CHECK_CRC32) != LZMA_OK)
+                 return false;
+           }
+           else
+           {
+              lzma_options_lzma options;
+              lzma_lzma_preset(&options, xzlevel);
+              if (lzma_alone_encoder(&d->lzma->stream, &options) != LZMA_OK)
+                 return false;
+           }
+           d->lzma->compressing = true;
+        }
+        else
+        {
+           if (compressor.Name == "xz")
+           {
+              if (lzma_auto_decoder(&d->lzma->stream, memlimit, 0) != LZMA_OK)
+                 return false;
+           }
+           else
+           {
+              if (lzma_alone_decoder(&d->lzma->stream, memlimit) != LZMA_OK)
+                 return false;
+           }
+           d->lzma->compressing = false;
+        }
        }
-       if ((Mode & ReadWrite) == ReadWrite)
-        d->bz2 = BZ2_bzdopen(iFd, "r+");
-       else if ((Mode & WriteOnly) == WriteOnly)
-        d->bz2 = BZ2_bzdopen(iFd, "w");
-       else
-        d->bz2 = BZ2_bzdopen(iFd, "r");
-       if (d->bz2 == NULL)
-        return false;
+ #endif
        Flags |= Compressed;
        return true;
     }
        }
        else
        {
-        if (FileName.empty() == true)
+        if (d->compressed_fd != -1)
            dup2(d->compressed_fd,STDIN_FILENO);
         dup2(Pipe[1],STDOUT_FILENO);
        }
@@@ -1267,24 -1395,55 +1395,55 @@@ bool FileFd::Read(void *To,unsigned lon
     *((char *)To) = '\0';
     do
     {
+       if (false)
+        /* dummy so that the rest can be 'else if's */;
  #ifdef HAVE_ZLIB
-       if (d != NULL && d->gz != NULL)
+       else if (d != NULL && d->gz != NULL)
         Res = gzread(d->gz,To,Size);
-       else
  #endif
  #ifdef HAVE_BZ2
-       if (d != NULL && d->bz2 != NULL)
+       else if (d != NULL && d->bz2 != NULL)
         Res = BZ2_bzread(d->bz2,To,Size);
-       else
  #endif
+ #ifdef HAVE_LZMA
+       else if (d != NULL && d->lzma != NULL)
+       {
+        if (d->lzma->eof == true)
+           break;
+        d->lzma->stream.next_out = (uint8_t *) To;
+        d->lzma->stream.avail_out = Size;
+        if (d->lzma->stream.avail_in == 0)
+        {
+           d->lzma->stream.next_in = d->lzma->buffer;
+           d->lzma->stream.avail_in = fread(d->lzma->buffer, 1, sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]), d->lzma->file);
+        }
+        d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
+        if (d->lzma->err == LZMA_STREAM_END)
+        {
+           d->lzma->eof = true;
+           Res = Size - d->lzma->stream.avail_out;
+        }
+        else if (d->lzma->err != LZMA_OK)
+        {
+           Res = -1;
+           errno = 0;
+        }
+        else
+           Res = Size - d->lzma->stream.avail_out;
+       }
+ #endif
+       else
           Res = read(iFd,To,Size);
  
        if (Res < 0)
        {
         if (errno == EINTR)
            continue;
+        if (false)
+           /* dummy so that the rest can be 'else if's */;
  #ifdef HAVE_ZLIB
-        if (d != NULL && d->gz != NULL)
+        else if (d != NULL && d->gz != NULL)
         {
            int err;
            char const * const errmsg = gzerror(d->gz, &err);
         }
  #endif
  #ifdef HAVE_BZ2
-        if (d != NULL && d->bz2 != NULL)
+        else if (d != NULL && d->bz2 != NULL)
         {
            int err;
            char const * const errmsg = BZ2_bzerror(d->bz2, &err);
            if (err != BZ_IO_ERROR)
               return FileFdError("BZ2_bzread: %s (%d: %s)", _("Read error"), err, errmsg);
         }
+ #endif
+ #ifdef HAVE_LZMA
+        else if (d != NULL && d->lzma != NULL)
+           return FileFdError("lzma_read: %s (%d)", _("Read error"), d->lzma->err);
  #endif
         return FileFdErrno("read",_("Read error"));
        }
@@@ -1364,23 -1527,45 +1527,45 @@@ bool FileFd::Write(const void *From,uns
     errno = 0;
     do
     {
+       if (false)
+        /* dummy so that the rest can be 'else if's */;
  #ifdef HAVE_ZLIB
-       if (d != NULL && d->gz != NULL)
-          Res = gzwrite(d->gz,From,Size);
-       else
+       else if (d != NULL && d->gz != NULL)
+        Res = gzwrite(d->gz,From,Size);
  #endif
  #ifdef HAVE_BZ2
-       if (d != NULL && d->bz2 != NULL)
-          Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
-       else
+       else if (d != NULL && d->bz2 != NULL)
+        Res = BZ2_bzwrite(d->bz2,(void*)From,Size);
+ #endif
+ #ifdef HAVE_LZMA
+       else if (d != NULL && d->lzma != NULL)
+       {
+        d->lzma->stream.next_in = (uint8_t *)From;
+        d->lzma->stream.avail_in = Size;
+        d->lzma->stream.next_out = d->lzma->buffer;
+        d->lzma->stream.avail_out = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]);
+        d->lzma->err = lzma_code(&d->lzma->stream, LZMA_RUN);
+        if (d->lzma->err != LZMA_OK)
+           return false;
+        size_t const n = sizeof(d->lzma->buffer)/sizeof(d->lzma->buffer[0]) - d->lzma->stream.avail_out;
+        size_t const m = (n == 0) ? 0 : fwrite(d->lzma->buffer, 1, n, d->lzma->file);
+        if (m != n)
+           Res = -1;
+        else
+           Res = Size - d->lzma->stream.avail_in;
+       }
  #endif
-          Res = write(iFd,From,Size);
+       else
+        Res = write(iFd,From,Size);
        if (Res < 0 && errno == EINTR)
         continue;
        if (Res < 0)
        {
+        if (false)
+           /* dummy so that the rest can be 'else if's */;
  #ifdef HAVE_ZLIB
-        if (d != NULL && d->gz != NULL)
+        else if (d != NULL && d->gz != NULL)
         {
            int err;
            char const * const errmsg = gzerror(d->gz, &err);
         }
  #endif
  #ifdef HAVE_BZ2
-        if (d != NULL && d->bz2 != NULL)
+        else if (d != NULL && d->bz2 != NULL)
         {
            int err;
            char const * const errmsg = BZ2_bzerror(d->bz2, &err);
            if (err != BZ_IO_ERROR)
               return FileFdError("BZ2_bzwrite: %s (%d: %s)", _("Write error"), err, errmsg);
         }
+ #endif
+ #ifdef HAVE_LZMA
+        else if (d != NULL && d->lzma != NULL)
+           return FileFdErrno("lzma_fwrite", _("Write error"));
  #endif
         return FileFdErrno("write",_("Write error"));
        }
        
-       From = (char *)From + Res;
+       From = (char const *)From + Res;
        Size -= Res;
        if (d != NULL)
         d->seekpos += Res;
@@@ -1424,7 -1613,7 +1613,7 @@@ bool FileFd::Write(int Fd, const void *
        if (Res < 0)
         return _error->Errno("write",_("Write error"));
  
-       From = (char *)From + Res;
+       From = (char const *)From + Res;
        Size -= Res;
     }
     while (Res > 0 && Size > 0);
  /* */
  bool FileFd::Seek(unsigned long long To)
  {
-    if (d != NULL && (d->pipe == true
- #ifdef HAVE_BZ2
-                       || d->bz2 != NULL
- #endif
-       ))
+    if (d != NULL && (d->pipe == true || d->InternalStream() == true))
     {
        // Our poor man seeking in pipes is costly, so try to avoid it
        unsigned long long seekpos = Tell();
  
        if ((d->openmode & ReadOnly) != ReadOnly)
         return FileFdError("Reopen is only implemented for read-only files!");
- #ifdef HAVE_BZ2
-      if (d->bz2 != NULL) 
-      {
-       BZ2_bzclose(d->bz2);
-       d->bz2 = NULL;
-      }
- #endif
+       d->InternalClose(FileName);
        if (iFd != -1)
         close(iFd);
        iFd = -1;
  /* */
  bool FileFd::Skip(unsigned long long Over)
  {
-    if (d != NULL && (d->pipe == true
- #ifdef HAVE_BZ2
-                       || d->bz2 != NULL
- #endif
-       ))
+    if (d != NULL && (d->pipe == true || d->InternalStream() == true))
     {
        d->seekpos += Over;
        char buffer[1024];
@@@ -1548,8 -1723,12 +1723,12 @@@ bool FileFd::Truncate(unsigned long lon
     // truncating /dev/null is always successful - as we get an error otherwise
     if (To == 0 && FileName == "/dev/null")
        return true;
- #if defined HAVE_ZLIB || defined HAVE_BZ2
-    if (d != NULL && (d->gz != NULL || d->bz2 != NULL))
+ #if defined HAVE_ZLIB || defined HAVE_BZ2 || defined HAVE_LZMA
+    if (d != NULL && (d->InternalStream() == true
+ #ifdef HAVE_ZLIB
+           || d->gz != NULL
+ #endif
+           ))
        return FileFdError("Truncating compressed files is not implemented (%s)", FileName.c_str());
  #endif
     if (ftruncate(iFd,To) != 0)
@@@ -1567,11 -1746,7 +1746,7 @@@ unsigned long long FileFd::Tell(
     // seeking around, but not all users of FileFd use always Seek() and co
     // so d->seekpos isn't always true and we can just use it as a hint if
     // we have nothing else, but not always as an authority…
-    if (d != NULL && (d->pipe == true
- #ifdef HAVE_BZ2
-                       || d->bz2 != NULL
- #endif
-       ))
+    if (d != NULL && (d->pipe == true || d->InternalStream() == true))
        return d->seekpos;
  
     off_t Res;
@@@ -1646,11 -1821,7 +1821,7 @@@ unsigned long long FileFd::Size(
  
     // for compressor pipes st_size is undefined and at 'best' zero,
     // so we 'read' the content and 'seek' back - see there
-    if (d != NULL && (d->pipe == true
- #ifdef HAVE_BZ2
-                       || (d->bz2 && size > 0)
- #endif
-       ))
+    if (d != NULL && (d->pipe == true || (d->InternalStream() == true && size > 0)))
     {
        unsigned long long const oldSeek = Tell();
        char ignore[1000];
@@@ -1793,7 -1964,13 +1964,13 @@@ bool FileFd::FileFdError(const char *De
  }
                                                                        /*}}}*/
  
- gzFile FileFd::gzFd() { return (gzFile) d->gz; }
+ APT_DEPRECATED gzFile FileFd::gzFd() {
+ #ifdef HAVE_ZLIB
+    return d->gz;
+ #else
+    return NULL;
+ #endif
+ }
  
  
  // Glob - wrapper around "glob()"                                      /*{{{*/
@@@ -1841,14 -2018,3 +2018,14 @@@ std::string GetTempDir(
  
     return string(tmpdir);
  }
 +
 +bool Rename(std::string From, std::string To)
 +{
 +   if (rename(From.c_str(),To.c_str()) != 0)
 +   {
 +      _error->Error(_("rename failed, %s (%s -> %s)."),strerror(errno),
 +                    From.c_str(),To.c_str());
 +      return false;
 +   }   
 +   return true;
 +}
index f0569b6fdfb66cf7587f102d6bae5852655d318e,35f3ab0f4846cbccb4784d1e4846b0f00d21007a..278a25742c0d255bb0f0ef1d56902df2aa62cb71
@@@ -27,6 -27,7 +27,7 @@@
  #include <string>
  #include <vector>
  #include <set>
+ #include <time.h>
  
  #include <zlib.h>
  
@@@ -94,7 -95,7 +95,7 @@@ class FileF
        And as the auto-conversation converts a 'unsigned long *' to a 'bool'
        instead of 'unsigned long long *' we need to provide this explicitely -
        otherwise applications magically start to fail… */
-    __deprecated bool Read(void *To,unsigned long long Size,unsigned long *Actual)
+    bool Read(void *To,unsigned long long Size,unsigned long *Actual) APT_DEPRECATED
     {
        unsigned long long R;
        bool const T = Read(To, Size, &R);
     // Simple manipulators
     inline int Fd() {return iFd;};
     inline void Fd(int fd) { OpenDescriptor(fd, ReadWrite);};
-    __deprecated gzFile gzFd();
+    gzFile gzFd() APT_DEPRECATED APT_PURE;
  
     inline bool IsOpen() {return iFd >= 0;};
     inline bool Failed() {return (Flags & Fail) == Fail;};
     bool OpenInternDescriptor(unsigned int const Mode, APT::Configuration::Compressor const &compressor);
  
     // private helpers to set Fail flag and call _error->Error
-    bool FileFdErrno(const char* Function, const char* Description,...) __like_printf(3) __cold;
-    bool FileFdError(const char* Description,...) __like_printf(2) __cold;
+    bool FileFdErrno(const char* Function, const char* Description,...) APT_PRINTF(3) APT_COLD;
+    bool FileFdError(const char* Description,...) APT_PRINTF(2) APT_COLD;
  };
  
  bool RunScripts(const char *Cnf);
@@@ -161,10 -162,9 +162,10 @@@ bool CopyFile(FileFd &From,FileFd &To)
  int GetLock(std::string File,bool Errors = true);
  bool FileExists(std::string File);
  bool RealFileExists(std::string File);
- bool DirectoryExists(std::string const &Path) __attrib_const;
+ bool DirectoryExists(std::string const &Path) APT_CONST;
  bool CreateDirectory(std::string const &Parent, std::string const &Path);
  time_t GetModificationTime(std::string const &Path);
 +bool Rename(std::string From, std::string To);
  
  std::string GetTempDir();
  
diff --combined cmdline/apt-helper.cc
index e3652d1eeae7f97557f314a5246ff32f4f142e2f,37279ec280711ad8bba3196db5c8a8efe6bc5fb6..2c1107d9053e7845acaee798ac00541c87b2312b
@@@ -7,6 -7,7 +7,7 @@@
  // Include Files                                                      /*{{{*/
  #include <config.h>
  
+ #include <apt-pkg/configuration.h>
  #include <apt-pkg/cmndline.h>
  #include <apt-pkg/error.h>
  #include <apt-pkg/init.h>
  
  #include <apt-private/acqprogress.h>
  #include <apt-private/private-output.h>
+ #include <apt-private/private-download.h>
  #include <apt-private/private-cmndline.h>
  
- #include <errno.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <fcntl.h>
+ #include <iostream>
+ #include <string>
+ #include <vector>
  
  #include <apti18n.h>
                                                                        /*}}}*/
- using namespace std;
  
- bool DoDownloadFile(CommandLine &CmdL)
static bool DoDownloadFile(CommandLine &CmdL)
  {
     if (CmdL.FileSize() <= 2)
        return _error->Error(_("Must specify at least one pair url/filename"));
     Fetcher.Setup(&Stat);
     std::string download_uri = CmdL.FileList[1];
     std::string targetfile = CmdL.FileList[2];
-    HashString hash;
 -   new pkgAcqFile(&Fetcher, download_uri, "", 0, "desc", "short-desc", 
++   std::string hash;
 +   if (CmdL.FileSize() > 3)
-       hash = HashString(CmdL.FileList[3]);
-    new pkgAcqFile(&Fetcher, download_uri, "", 0, "desc", "short-desc", 
++      hash = CmdL.FileList[3];
++   new pkgAcqFile(&Fetcher, download_uri, hash, 0, "desc", "short-desc", 
                    "dest-dir-ignored", targetfile);
     Fetcher.Run();
-    if (!FileExists(targetfile))
-    {
-       _error->Error(_("Download Failed"));
-       return false;
-    }
-    if(hash.empty() == false)
-    {
-       if(hash.VerifyFile(targetfile) == false)
-       {
-          _error->Error(_("HashSum Failed"));
-          Rename(targetfile, targetfile+".failed");
-          return false;
-       }
-    }
+    bool Failed = false;
 -   if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == false ||
++   if (AcquireRun(Fetcher, 0, &Failed, NULL) == false || Failed == true ||
+        FileExists(targetfile) == false)
+       return _error->Error(_("Download Failed"));
     return true;
  }
  
bool ShowHelp(CommandLine &CmdL)
static bool ShowHelp(CommandLine &)
  {
-    ioprintf(cout,_("%s %s for %s compiled on %s %s\n"),PACKAGE,PACKAGE_VERSION,
+    ioprintf(std::cout,_("%s %s for %s compiled on %s %s\n"),PACKAGE,PACKAGE_VERSION,
            COMMON_ARCH,__DATE__,__TIME__);
  
     if (_config->FindB("version") == true)
       return true;
  
-    cout << 
+    std::cout <<
      _("Usage: apt-helper [options] command\n"
        "       apt-helper [options] download-file uri target-path\n"
        "\n"
index d9bacef836489e07cf48c974fd6d69147662891c,00c8f3abcb0a11fb376fa323791286f7df64cb8f..8e401cb5f506b0e7229f02d7b77447733a97f528
@@@ -90,18 -90,22 +90,22 @@@ msgdone() 
                echo "${CDONE}DONE${CNORMAL}";
        fi
  }
 -
 +getaptconfig() {
 +      if [ -f ./aptconfig.conf ]; then
 +            echo "./aptconfig.conf"
 +      elif [ -f ../aptconfig.conf ]; then
 +            echo "../aptconfig.conf"
 +        fi
 +}
  runapt() {
        msgdebug "Executing: ${CCMD}$*${CDEBUG} "
        local CMD="$1"
        shift
-       MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/$CMD "$@"
+       case $CMD in
+       sh|aptitude|*/*) ;;
+       *) CMD="${BUILDDIRECTORY}/$CMD";;
+       esac
 -      if [ -f ./aptconfig.conf ]; then
 -              MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${LIBRARYPATH} $CMD "$@"
 -      elif [ -f ../aptconfig.conf ]; then
 -              MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG=../aptconfig.conf LD_LIBRARY_PATH=${LIBRARYPATH} $CMD "$@"
 -      else
 -              MALLOC_PERTURB_=21 MALLOC_CHECK_=2 LD_LIBRARY_PATH=${LIBRARYPATH} $CMD "$@"
 -      fi
++      MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${BUILDDIRECTORY} $CMD "$@"
  }
  aptconfig() { runapt apt-config "$@"; }
  aptcache() { runapt apt-cache "$@"; }
@@@ -111,24 -115,19 +115,19 @@@ aptftparchive() { runapt apt-ftparchiv
  aptkey() { runapt apt-key "$@"; }
  aptmark() { runapt apt-mark "$@"; }
  apt() { runapt apt "$@"; }
- apthelper() { 
-   APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${APTHELPERBINDIR} ${APTHELPERBINDIR}/apt-helper "$@";
- }
- aptwebserver() {
-   LD_LIBRARY_PATH=${APTWEBSERVERBINDIR} ${APTWEBSERVERBINDIR}/aptwebserver "$@";
- }
+ apthelper() { runapt "${APTHELPERBINDIR}/apt-helper" "$@"; }
+ aptwebserver() { runapt "${APTWEBSERVERBINDIR}/aptwebserver" "$@"; }
+ aptitude() { runapt aptitude "$@"; }
  dpkg() {
        command dpkg --root=${TMPWORKINGDIRECTORY}/rootdir --force-not-root --force-bad-path --log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log "$@"
  }
aptitude() {
-       APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${BUILDDIRECTORY}  command aptitude "$@"
dpkgcheckbuilddeps() {
+       command dpkg-checkbuilddeps --admindir=${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg "$@"
  }
  gdb() {
        echo "gdb: run »$*«"
-       APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${BUILDDIRECTORY} command gdb ${BUILDDIRECTORY}/$1 --args "$@"
- }
- http() {
-       LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/methods/http
+       APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${LIBRARYPATH} command gdb ${BUILDDIRECTORY}/$1 --args "$@"
  }
  gpg() {
        # see apt-key for the whole trickery. Setup is done in setupenvironment
@@@ -173,6 -172,7 +172,7 @@@ setupenvironment() 
  
          # allow overriding the default BUILDDIR location
        BUILDDIRECTORY=${APT_INTEGRATION_TESTS_BUILD_DIR:-"${TESTDIRECTORY}/../../build/bin"}
+       LIBRARYPATH=${APT_INTEGRATION_TESTS_LIBRARY_PATH:-"${BUILDDIRECTORY}"}
          METHODSDIR=${APT_INTEGRATION_TESTS_METHODS_DIR:-"${BUILDDIRECTORY}/methods"}
          APTHELPERBINDIR=${APT_INTEGRATION_TESTS_LIBEXEC_DIR:-"${BUILDDIRECTORY}"}
          APTWEBSERVERBINDIR=${APT_INTEGRATION_TESTS_WEBSERVER_BIN_DIR:-"${BUILDDIRECTORY}"}
        mkdir rootdir aptarchive keys
        cd rootdir
        mkdir -p etc/apt/apt.conf.d etc/apt/sources.list.d etc/apt/trusted.gpg.d etc/apt/preferences.d
-       mkdir -p var/cache var/lib var/log tmp
+       mkdir -p var/cache var/lib/apt var/log tmp
        mkdir -p var/lib/dpkg/info var/lib/dpkg/updates var/lib/dpkg/triggers
        touch var/lib/dpkg/available
        mkdir -p usr/lib/apt
        ln -s ${METHODSDIR} usr/lib/apt/methods
+       ln -s ${BUILDDIRECTORY}/../../debian/apt.conf.autoremove etc/apt/apt.conf.d/01autoremove
        cd ..
        local PACKAGESFILE=$(echo "$(basename $0)" | sed -e 's/^test-/Packages-/' -e 's/^skip-/Packages-/')
        if [ -f "${TESTDIRECTORY}/${PACKAGESFILE}" ]; then
        echo 'quiet::NoUpdate "true";' >> aptconfig.conf
        echo "Acquire::https::CaInfo \"${TESTDIR}/apt.pem\";" > rootdir/etc/apt/apt.conf.d/99https
          echo "Apt::Cmd::Disable-Script-Warning \"1\";" > rootdir/etc/apt/apt.conf.d/apt-binary
-       export LC_ALL=C.UTF-8
-       export PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin"
        configcompression '.' 'gz' #'bz2' 'lzma' 'xz'
  
        # gpg needs a trustdb to function, but it can't be invalid (not even empty)
        # newer gpg versions are fine without it, but play it safe for now
        gpg --quiet --check-trustdb --secret-keyring $SECRETKEYRING --keyring $SECRETKEYRING >/dev/null 2>&1
  
+       # cleanup the environment a bit
+       export PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin"
+       export LC_ALL=C.UTF-8
+       unset LANGUAGE APT_CONFIG
+       unset GREP_OPTIONS DEB_BUILD_PROFILES
        msgdone "info"
  }
  
@@@ -254,6 -259,10 +259,10 @@@ getarchitectures() 
        echo "$(aptconfig dump | grep APT::Architecture | cut -d'"' -f 2 | sed '/^$/ d' | sort | uniq | tr '\n' ' ')"
  }
  
+ getarchitecturesfromcommalist() {
+       echo "$1" | sed -e 's#,#\n#g' | sed -e "s/^native\$/$(getarchitecture 'native')/"
+ }
  configarchitecture() {
        {
                echo "APT::Architecture \"$(getarchitecture $1)\";"
@@@ -427,7 -436,7 +436,7 @@@ Package: $NAME" >> ${BUILDDIR}/debian/c
  #             fi
        done
  
-       for arch in $(echo "$ARCH" | sed -e 's#,#\n#g' | sed -e "s#^native\$#$(getarchitecture 'native')#"); do
+       for arch in $(getarchitecturesfromcommalist "$ARCH"); do
                rm -rf ${BUILDDIR}/debian/tmp
                mkdir -p ${BUILDDIR}/debian/tmp/DEBIAN ${BUILDDIR}/debian/tmp/usr/share/doc/${NAME} ${BUILDDIR}/debian/tmp/usr/bin
                cp ${BUILDDIR}/debian/copyright ${BUILDDIR}/debian/changelog ${BUILDDIR}/FEATURES ${BUILDDIR}/debian/tmp/usr/share/doc/${NAME}
@@@ -582,7 -591,7 +591,7 @@@ insertpackage() 
   something went horribly wrong! They are autogenerated
   und used only by testcases and surf no other propose…"}"
        local ARCHS=""
-       for arch in $(echo "$ARCH" | sed -e 's#,#\n#g' | sed -e "s#^native\$#$(getarchitecture 'native')#"); do
+       for arch in $(getarchitecturesfromcommalist "$ARCH"); do
                if [ "$arch" = 'all' -o "$arch" = 'none' ]; then
                        ARCHS="$(getarchitectures)"
                else
@@@ -644,7 -653,7 +653,7 @@@ insertinstalledpackage() 
  
        local FILE='rootdir/var/lib/dpkg/status'
        local INFO='rootdir/var/lib/dpkg/info'
-       for arch in $(echo "$ARCH" | sed -e 's#,#\n#g' | sed -e "s#^native\$#$(getarchitecture 'native')#"); do
+       for arch in $(getarchitecturesfromcommalist "$ARCH"); do
                echo "Package: $NAME
  Status: $STATUS
  Priority: $PRIORITY
@@@ -850,18 -859,16 +859,16 @@@ signreleasefiles() 
  
  webserverconfig() {
        msgtest "Set webserver config option '${1}' to" "$2"
-       downloadfile "http://localhost:8080/_config/set/${1}/${2}" '/dev/null' >/dev/null
-       local DOWNLOG='download-testfile.log'
-       rm -f "$DOWNLOG"
-       local STATUS="${TMPWORKINGDIRECTORY}/rootdir/tmp/webserverconfig.status"
-       downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG"
-       if [ "$(cat "$STATUS")" = '200' ]; then
+       local DOWNLOG='rootdir/tmp/download-testfile.log'
+       local STATUS='rootdir/tmp/webserverconfig.status'
+       rm -f "$STATUS" "$DOWNLOG"
+       if downloadfile "http://localhost:8080/_config/set/${1}/${2}" "$STATUS" > "$DOWNLOG"; then
                msgpass
        else
-               cat >&2 "$DOWNLOG"
-               msgfail "Statuscode was $(cat "$STATUS")"
+               cat "$DOWNLOG" "$STATUS"
+               msgfail
        fi
-       rm "$STATUS"
+       testwebserverlaststatuscode '200'
  }
  
  rewritesourceslist() {
@@@ -919,23 -926,29 +926,29 @@@ connect = 808
  changetocdrom() {
        mkdir -p rootdir/media/cdrom/.disk
        local CD="$(readlink -f rootdir/media/cdrom)"
-       echo "acquire::cdrom::mount \"${CD}\";" > rootdir/etc/apt/apt.conf.d/00cdrom
-       echo 'acquire::cdrom::autodetect 0;' >> rootdir/etc/apt/apt.conf.d/00cdrom
+       echo "acquire::cdrom::mount \"${CD}\";
+ acquire::cdrom::${CD}/::mount \"mv ${CD}-unmounted ${CD}\";
+ acquire::cdrom::${CD}/::umount \"mv ${CD} ${CD}-unmounted\";
+ acquire::cdrom::autodetect 0;" > rootdir/etc/apt/apt.conf.d/00cdrom
        echo -n "$1" > ${CD}/.disk/info
        if [ ! -d aptarchive/dists ]; then
                msgdie 'Flat file archive cdroms can not be created currently'
                return 1
        fi
-       mv aptarchive/dists $CD
+       mv aptarchive/dists "$CD"
        ln -s "$(readlink -f ./incoming)" $CD/pool
        find rootdir/etc/apt/sources.list.d/ -name 'apt-test-*.list' -delete
+       # start with an unmounted disk
+       mv "${CD}" "${CD}-unmounted"
+       # we don't want the disk to be modifiable
+       addtrap 'prefix' "chmod -f -R +w $PWD/rootdir/media/cdrom/dists/ $PWD/rootdir/media/cdrom-unmounted/dists/ || true;"
+       chmod -R -w rootdir/media/cdrom-unmounted/dists
  }
  
  downloadfile() {
-         PROTO="$(echo "$1" | cut -d':' -f 1)"
-         apthelper -o Acquire::https::CaInfo=${TESTDIR}/apt.pem \
-                   -o Debug::Acquire::${PROTO}=1 \
-                   download-file "$1" "$2" 2>&1
+       local PROTO="$(echo "$1" | cut -d':' -f 1 )"
+       apthelper -o Debug::Acquire::${PROTO}=1 \
+               download-file "$1" "$2" 2>&1 || true
        # only if the file exists the download was successful
        if [ -e "$2" ]; then
                return 0
  checkdiff() {
        local DIFFTEXT="$(command diff -u "$@" | sed -e '/^---/ d' -e '/^+++/ d' -e '/^@@/ d')"
        if [ -n "$DIFFTEXT" ]; then
-               echo
-               echo "$DIFFTEXT"
+               echo >&2
+               echo >&2 "$DIFFTEXT"
                return 1
        else
                return 0
@@@ -997,11 -1010,17 +1010,17 @@@ testequalor2() 
        shift 2
        msgtest "Test for equality OR of" "$*"
        $* >$COMPAREAGAINST 2>&1 || true
-       (checkdiff $COMPAREFILE1 $COMPAREAGAINST 1> /dev/null ||
-               checkdiff $COMPAREFILE2 $COMPAREAGAINST 1> /dev/null) && msgpass ||
-               ( echo "\n${CINFO}Diff against OR 1${CNORMAL}" "$(checkdiff $COMPAREFILE1 $COMPAREAGAINST)" \
-                      "\n${CINFO}Diff against OR 2${CNORMAL}" "$(checkdiff $COMPAREFILE2 $COMPAREAGAINST)" &&
-                 msgfail )
+       if checkdiff $COMPAREFILE1 $COMPAREAGAINST >/dev/null 2>&1 || \
+               checkdiff $COMPAREFILE2 $COMPAREAGAINST >/dev/null 2>&1
+       then
+               msgpass
+       else
+               echo -n "\n${CINFO}Diff against OR 1${CNORMAL}"
+               checkdiff $COMPAREFILE1 $COMPAREAGAINST || true
+               echo -n "${CINFO}Diff against OR 2${CNORMAL}"
+               checkdiff $COMPAREFILE2 $COMPAREAGAINST || true
+               msgfail
+       fi
  }
  
  testshowvirtual() {
@@@ -1027,24 -1046,24 +1046,24 @@@ testnopackage() 
        msgtest "Test for non-existent packages" "apt-cache show $*"
        local SHOWPKG="$(aptcache show "$@" 2>&1 | grep '^Package: ')"
        if [ -n "$SHOWPKG" ]; then
-               echo
-               echo "$SHOWPKG"
+               echo >&2
+               echo >&2 "$SHOWPKG"
                msgfail
-               return 1
+       else
+               msgpass
        fi
-       msgpass
  }
  
  testdpkginstalled() {
        msgtest "Test for correctly installed package(s) with" "dpkg -l $*"
        local PKGS="$(dpkg -l "$@" 2>/dev/null | grep '^i' | wc -l)"
        if [ "$PKGS" != $# ]; then
-               echo $PKGS
-               dpkg -l "$@" | grep '^[a-z]'
+               echo >&2 $PKGS
+               dpkg -l "$@" | grep '^[a-z]' >&2
                msgfail
-               return 1
+       else
+               msgpass
        fi
-       msgpass
  }
  
  testdpkgnotinstalled() {
        local PKGS="$(dpkg -l "$@" 2> /dev/null | grep '^i' | wc -l)"
        if [ "$PKGS" != 0 ]; then
                echo
-               dpkg -l "$@" | grep '^[a-z]'
+               dpkg -l "$@" | grep '^[a-z]' >&2
                msgfail
-               return 1
+       else
+               msgpass
        fi
-       msgpass
  }
  
  testmarkedauto() {
@@@ -1081,8 -1100,8 +1100,8 @@@ testsuccess() 
        if $@ >${OUTPUT} 2>&1; then
                msgpass
        else
-               echo
-               cat $OUTPUT
+               echo >&2
+               cat >&2 $OUTPUT
                msgfail
        fi
  }
@@@ -1095,14 -1114,35 +1114,35 @@@ testfailure() 
        fi
        local OUTPUT="${TMPWORKINGDIRECTORY}/rootdir/tmp/testfailure.output"
        if $@ >${OUTPUT} 2>&1; then
-               echo
-               cat $OUTPUT
+               echo >&2
+               cat >&2 $OUTPUT
                msgfail
        else
                msgpass
        fi
  }
  
+ testwebserverlaststatuscode() {
+       local DOWNLOG='rootdir/tmp/webserverstatus-testfile.log'
+       local STATUS='rootdir/tmp/webserverstatus-statusfile.log'
+       rm -f "$DOWNLOG" "$STATUS"
+       msgtest 'Test last status code from the webserver was' "$1"
+       downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG"
+       if [ "$(cat "$STATUS")" = "$1" ]; then
+               msgpass
+       else
+               echo >&2
+               if [ -n "$2" ]; then
+                       shift
+                       echo >&2 '#### Additionally provided output files contain:'
+                       cat >&2 "$@"
+               fi
+               echo >&2 '#### Download log of the status code:'
+               cat >&2 "$DOWNLOG"
+               msgfail "Status was $(cat "$STATUS")"
+       fi
+ }
  pause() {
        echo "STOPPED execution. Press enter to continue"
        local IGNORE
index 37ed95181dba8b8f211ece503f8ed1793da085b7,0000000000000000000000000000000000000000..6505b59565e03e184507358e3382cde0353cf495
mode 100755,000000..100755
--- /dev/null
@@@ -1,37 -1,0 +1,39 @@@
- testfileequal download.stderr 'E: HashSum Failed'
- testfileequal foo5.failed 'foo'
 +#!/bin/sh
 +set -e
 +
 +TESTDIR=$(readlink -f $(dirname $0))
 +. $TESTDIR/framework
 +
 +setupenvironment
 +configarchitecture "i386"
 +
 +changetohttpswebserver
 +
 +echo "foo" > aptarchive/foo
 +
 +msgtest 'apt-file download-file md5sum'
 +apthelper -qq download-file http://localhost:8080/foo foo2 MD5Sum:d3b07384d113edec49eaa6238ad5ff00 && msgpass || msgfail
 +testfileequal foo2 'foo'
 +
 +msgtest 'apt-file download-file sha1'
 +apthelper -qq download-file http://localhost:8080/foo foo1 SHA1:f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 && msgpass || msgfail
 +testfileequal foo1 'foo'
 +
 +msgtest 'apt-file download-file sha256'
 +apthelper -qq download-file http://localhost:8080/foo foo3 SHA256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c && msgpass || msgfail
 +testfileequal foo3 'foo'
 +
 +msgtest 'apt-file download-file no-hash'
 +apthelper -qq download-file http://localhost:8080/foo foo4 && msgpass || msgfail
 +testfileequal foo4 'foo'
 +
 +msgtest 'apt-file download-file wrong hash'
 +if ! apthelper -qq download-file http://localhost:8080/foo foo5 MD5Sum:aabbcc 2>&1 2> download.stderr; then
 +   msgpass
 +else
 +   msgfail
 +fi
++testfileequal download.stderr 'E: Failed to fetch http://localhost:8080/foo  Hash Sum mismatch
++
++E: Download Failed'
++testfileequal foo5.FAILED 'foo'