text: /*
   * Unit test suite for AVI Functions
   *
   * Copyright 2008 Detlef Riekenberg
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   *
   */
  
  #define COBJMACROS
  #define CONST_VTABLE
  
  #include "wine/test.h"
  #include "initguid.h"
  #include "wingdi.h"
  #include "vfw.h"
  
  /* ########################### */
  
  DEFINE_AVIGUID(CLSID_WAVFile,   0x00020003, 0, 0);
  static const CHAR winetest0[] = "winetest0";
  static const CHAR winetest1[] = "winetest1";
  static const CHAR testfilename[]  = "wine_avifil32_test.avi";
  
  /* ########################### */
  
  static const DWORD deffh[] = /* file_header */
  {
      FOURCC_RIFF, 0x34c6 /* length */, formtypeAVI,
      FOURCC_LIST, 0x1ac /* length */,
      listtypeAVIHEADER, ckidAVIMAINHDR, sizeof(MainAVIHeader),
  };
  
  static const MainAVIHeader defmah =
  {
      0x00008256, /* dwMicroSecPerFrame   */
      0x000080e8, /* dwMaxBytesPerSec     */
      0x00000000, /* dwPaddingGranularity */
      0x00000910, /* dwFlags              */
      1,          /* dwTotalFrames        */
      0,          /* dwInitialFrames      */
      2,          /* dwStreams            */
      0x00100000, /* dwSuggestedBufferSize*/
      8,          /* dwWidth              */
      6,          /* dwHeight             */
      { 0, 0, 0, 0 } /* dwReserved[4] */
  };
  
  static const AVIStreamHeader defash0 =
  {
      streamtypeVIDEO, /* fccType              */
      0x30323449,      /* fccHandler           */
      0x00000000,      /* dwFlags              */
      0,               /* wPriority            */
      0,               /* wLanguage            */
      0,               /* dwInitialFrames      */
      0x000003e9,      /* dwScale              */
      0x00007530,      /* dwRate               */
      0,               /* dwStart              */
      1,               /* dwLength             */
      0x00100000,      /* dwSuggestedBufferSize*/
      0xffffffff,      /* dwQuality            */
      0,               /* dwSampleSize         */
      { 0, 0, 0, 0 }   /* short left right top bottom */
  };
  
  static const AVIStreamHeader defash1 =
  {
      /* AVIStreamHeader */
      streamtypeAUDIO, /* fccType              */
      1,               /* fccHandler           */
      0,               /* dwFlags              */
      0,               /* wPriority            */
      0,               /* wLanguage            */
      0,               /* dwInitialFrames      */
      1,               /* dwScale              */
      0x00002b11,      /* dwRate               */
      0,               /* dwStart              */
      0x00000665,      /* dwLength             */
      0x00003000,      /* dwSuggestedBufferSize*/
      0xffffffff,      /* dwQuality            */
      2,               /* dwSampleSize         */
      { 0, 0, 0, 0 }   /* short left right top bottom */
  };
  
  static const PCMWAVEFORMAT defpcmwf =
  {
      {
          1,      /* wFormatTag      */
          2,      /* nChannels       */
          11025,  /* nSamplesPerSec  */
          22050,  /* nAvgBytesPerSec */
          2,      /* nBlockAlign     */
      },
      8,      /* wBitsPerSample  */
  };
  
  typedef struct common_avi_headers {
      DWORD           fh[sizeof(deffh)];
      MainAVIHeader   mah;
      AVIStreamHeader ash0;
      AVIStreamHeader ash1;
      PCMWAVEFORMAT   pcmwf;
  } COMMON_AVI_HEADERS;
  
  /* Extra data needed to get the VFW API to load the file */
  /* DWORD deffh */
  /* MainAVIHeader mah */
  static const DWORD streamlist[] =
  {
      FOURCC_LIST, 0xd4 /* length */,
      listtypeSTREAMHEADER, ckidSTREAMHEADER, 0x38 /* length */,
  };
  /* AVIStreamHeader ash0 */
  static const DWORD videostreamformat[] =
  {
      ckidSTREAMFORMAT, 0x28 /* length */,
      0x00000028, 0x00000008, 0x00000006, 0x00180001,
      0x30323449, 0x00000090, 0x00000000, 0x00000000,
      0x00000000, 0x00000000,
  };
  static const DWORD padding1[] =
  {
      ckidAVIPADDING, 0xc /* length */,
      0x00000004, 0x00000000, 0x63643030
  };
  static const DWORD videopropheader[] =
  {
      0x70727076, 0x44 /* length */,
      0x00000000, 0x00000000,
      0x0000001e, 0x00000008, 0x00000006, 0x00100009,
      0x00000008, 0x00000006, 0x00000001, 0x00000006,
      0x00000008, 0x00000006, 0x00000008, 0x00000000,
      0x00000000, 0x00000000, 0x00000000,
      FOURCC_LIST, 0x70 /* length */,
      listtypeSTREAMHEADER, ckidSTREAMHEADER, 0x38 /* length */,
  };
  /* AVIStreamHeader ash1 */
  static const DWORD audiostreamformat_pre[] =
  {
      ckidSTREAMFORMAT, sizeof(PCMWAVEFORMAT) /* length */,
  };
  /* PCMWAVEFORMAT pcmwf */
  static DWORD data[] =
  {
      ckidAVIPADDING, 0xc /* length */,
      0x00000004, 0x00000000, 0x62773130,
      ckidAVIPADDING, 0xc /* length */,
      0x6c6d646f, 0x686c6d64, 0x000000f8,
      FOURCC_LIST, 0x18 /* length */,
      0x4f464e49,
      0x54465349, 0xc /* length */,
      0x6676614c, 0x332e3235, 0x00302e37,
      ckidAVIPADDING, 0x4 /* length */,
      0,
      FOURCC_LIST, 0xd1b /* length */, listtypeAVIMOVIE,
      0, 0
  };
  
  /* ########################### */
  
  static void test_AVISaveOptions(void)
  {
      AVICOMPRESSOPTIONS options[2];
      LPAVICOMPRESSOPTIONS poptions[2];
      PAVISTREAM streams[2] = {NULL, NULL};
      HRESULT hres;
      DWORD   res;
      LONG    lres;
  
      poptions[0] = &options[0];
      poptions[1] = &options[1];
      ZeroMemory(options, sizeof(options));
  
      SetLastError(0xdeadbeef);
      hres = CreateEditableStream(&streams[0], NULL);
      ok(hres == AVIERR_OK, "0: got 0x%x and %p (expected AVIERR_OK)\n", hres, streams[0]);
  
      SetLastError(0xdeadbeef);
      hres = CreateEditableStream(&streams[1], NULL);
      ok(hres == AVIERR_OK, "1: got 0x%x and %p (expected AVIERR_OK)\n", hres, streams[1]);
  
      SetLastError(0xdeadbeef);
      hres = EditStreamSetNameA(streams[0], winetest0);
      ok(hres == AVIERR_OK, "0: got 0x%x (expected AVIERR_OK)\n", hres);
  
      SetLastError(0xdeadbeef);
      hres = EditStreamSetNameA(streams[1], winetest1);
      ok(hres == AVIERR_OK, "1: got 0x%x (expected AVIERR_OK)\n", hres);
  
      if (winetest_interactive) {
          SetLastError(0xdeadbeef);
          res = AVISaveOptions(0, ICMF_CHOOSE_DATARATE |ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_ALLCOMPRESSORS,
                               2, streams, poptions);
          trace("got %u with 0x%x/%u\n", res, GetLastError(), GetLastError());
      }
  
      SetLastError(0xdeadbeef);
      lres = AVISaveOptionsFree(2, poptions);
      ok(lres == AVIERR_OK, "got 0x%x with 0x%x/%u\n", lres, GetLastError(), GetLastError());
  
      SetLastError(0xdeadbeef);
      res = AVIStreamRelease(streams[0]);
      ok(res == 0, "0: got refcount %u (expected 0)\n", res);
  
      SetLastError(0xdeadbeef);
      res = AVIStreamRelease(streams[1]);
      ok(res == 0, "1: got refcount %u (expected 0)\n", res);
  
  }
  
  /* ########################### */
  
  static void test_EditStreamSetInfo(void)
  {
      PAVISTREAM stream = NULL;
      HRESULT hres;
      AVISTREAMINFOA info, info2;
  
      hres = CreateEditableStream(&stream, NULL);
      ok(hres == AVIERR_OK, "got 0x%08X, expected AVIERR_OK\n", hres);
  
      /* Size parameter is somehow checked (notice the crash with size=-1 below) */
      hres = EditStreamSetInfoA(stream, NULL, 0);
      ok( hres == AVIERR_BADSIZE, "got 0x%08X, expected AVIERR_BADSIZE\n", hres);
  
      hres = EditStreamSetInfoA(stream, NULL, sizeof(AVISTREAMINFOA)-1 );
      ok( hres == AVIERR_BADSIZE, "got 0x%08X, expected AVIERR_BADSIZE\n", hres);
  
      if(0)
      {   
          /* Crashing - first parameter not checked */
          EditStreamSetInfoA(NULL, &info, sizeof(info) );
  
          /* Crashing - second parameter not checked */
          EditStreamSetInfoA(stream, NULL, sizeof(AVISTREAMINFOA) );
  
          EditStreamSetInfoA(stream, NULL, -1);
      }
  
      hres = AVIStreamInfoA(stream, &info, sizeof(info) );
      ok( hres == 0, "got 0x%08X, expected 0\n", hres);
  
               /* Does the function check what's it's updating ? */
  
  #define IS_INFO_UPDATED(m) do { \
      hres = EditStreamSetInfoA(stream, &info, sizeof(info) ); \
      ok( hres == 0, "got 0x%08X, expected 0\n", hres); \
      hres = AVIStreamInfoA(stream, &info2, sizeof(info2) ); \
      ok( hres == 0, "got 0x%08X, expected 0\n", hres); \
      ok( info2.m == info.m, "EditStreamSetInfo did not update "#m" parameter\n" ); \
      } while(0)
  
      info.dwStart++;
      IS_INFO_UPDATED(dwStart);
      info.dwStart = 0;
      IS_INFO_UPDATED(dwStart);
  
      info.wPriority++;
      IS_INFO_UPDATED(wPriority);
      info.wPriority = 0;
      IS_INFO_UPDATED(wPriority);
  
      info.wLanguage++;
      IS_INFO_UPDATED(wLanguage);
      info.wLanguage = 0;
      IS_INFO_UPDATED(wLanguage);
  
      info.dwScale++;
      IS_INFO_UPDATED(dwScale);
      info.dwScale = 0;
      IS_INFO_UPDATED(dwScale);
  
      info.dwRate++;
      IS_INFO_UPDATED(dwRate);
      info.dwRate = 0;
      IS_INFO_UPDATED(dwRate);
  
      info.dwQuality++;
      IS_INFO_UPDATED(dwQuality);
      info.dwQuality = 0;
      IS_INFO_UPDATED(dwQuality);
      info.dwQuality = -2;
      IS_INFO_UPDATED(dwQuality);
      info.dwQuality = ICQUALITY_HIGH+1;
      IS_INFO_UPDATED(dwQuality);
  
      info.rcFrame.left = 0;
      IS_INFO_UPDATED(rcFrame.left);
      info.rcFrame.top = 0;
      IS_INFO_UPDATED(rcFrame.top);
      info.rcFrame.right = 0;
      IS_INFO_UPDATED(rcFrame.right);
      info.rcFrame.bottom = 0;
      IS_INFO_UPDATED(rcFrame.bottom);
  
      info.rcFrame.left = -1;
      IS_INFO_UPDATED(rcFrame.left);
      info.rcFrame.top = -1;
      IS_INFO_UPDATED(rcFrame.top);
      info.rcFrame.right = -1;
      IS_INFO_UPDATED(rcFrame.right);
      info.rcFrame.bottom = -1;
      IS_INFO_UPDATED(rcFrame.bottom);
      AVIStreamRelease(stream);
  #undef IS_INFO_UPDATED
  }
  
  
  static void init_test_struct(COMMON_AVI_HEADERS *cah)
  {
      memcpy(cah->fh, deffh, sizeof(deffh));
      cah->mah = defmah;
      cah->ash0 = defash0;
      cah->ash1 = defash1;
      cah->pcmwf = defpcmwf;
  }
  
  static void create_avi_file(const COMMON_AVI_HEADERS *cah, char *filename)
  {
      HANDLE hFile;
      DWORD written;
  
      hFile = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  
      ok(hFile != INVALID_HANDLE_VALUE, "Couldn't create file\n");
  
      WriteFile(hFile, &cah->fh, sizeof(deffh), &written, NULL);
      WriteFile(hFile, &cah->mah, sizeof(MainAVIHeader), &written, NULL);
      WriteFile(hFile, streamlist, sizeof(streamlist), &written, NULL);
      WriteFile(hFile, &cah->ash0, 0x38, &written, NULL);
      WriteFile(hFile, videostreamformat, sizeof(videostreamformat), &written, NULL);
      WriteFile(hFile, padding1, sizeof(padding1), &written, NULL);
      WriteFile(hFile, videopropheader, sizeof(videopropheader), &written, NULL);
      WriteFile(hFile, &cah->ash1, 0x38, &written, NULL);
      WriteFile(hFile, audiostreamformat_pre, sizeof(audiostreamformat_pre), &written, NULL);
      WriteFile(hFile, &cah->pcmwf, sizeof(PCMWAVEFORMAT), &written, NULL);
      WriteFile(hFile, data, sizeof(data), &written, NULL);
  
      CloseHandle(hFile);
  }
  
  static void test_default_data(void)
  {
      COMMON_AVI_HEADERS cah;
      char filename[MAX_PATH];
      PAVIFILE pFile;
      int res;
      LONG lSize;
      PAVISTREAM pStream0;
      PAVISTREAM pStream1;
      AVISTREAMINFOA asi0, asi1;
      WAVEFORMATEX wfx;
  
      GetTempPathA(MAX_PATH, filename);
      strcpy(filename+strlen(filename), testfilename);
  
      init_test_struct(&cah);
      create_avi_file(&cah, filename);
  
      res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
      ok(res == 0, "Unable to open file: error=%u\n", res);
  
      pStream0 = (void *)0xdeadbeef;
      res = AVIFileGetStream(pFile, &pStream0, ~0U, 0);
      ok(res == AVIERR_NODATA, "expected AVIERR_NODATA, got %u\n", res);
      ok(pStream0 == NULL, "AVIFileGetStream should set stream to NULL\n");
  
      res = AVIFileGetStream(pFile, &pStream0, 0, 0);
      ok(res == 0, "Unable to open video stream: error=%u\n", res);
  
      res = AVIFileGetStream(pFile, &pStream1, 0, 1);
      ok(res == 0, "Unable to open audio stream: error=%u\n", res);
  
      res = AVIStreamInfoA(pStream0, &asi0, sizeof(asi0));
      ok(res == 0, "Unable to read stream info: error=%u\n", res);
  
      res = AVIStreamInfoA(pStream1, &asi1, sizeof(asi1));
      ok(res == 0, "Unable to read stream info: error=%u\n", res);
  
      res = AVIStreamReadFormat(pStream0, AVIStreamStart(pStream1), NULL, &lSize);
      ok(res == 0, "Unable to read format size: error=%u\n", res);
  
      res = AVIStreamReadFormat(pStream1, AVIStreamStart(pStream1), &wfx, &lSize);
      ok(res == 0, "Unable to read format: error=%u\n", res);
  
      ok(asi0.fccType == streamtypeVIDEO, "got 0x%x (expected streamtypeVIDEO)\n", asi0.fccType);
      ok(asi0.fccHandler == 0x30323449, "got 0x%x (expected 0x30323449)\n", asi0.fccHandler);
      ok(asi0.dwFlags == 0, "got %u (expected 0)\n", asi0.dwFlags);
      ok(asi0.wPriority == 0, "got %u (expected 0)\n", asi0.wPriority);
      ok(asi0.wLanguage == 0, "got %u (expected 0)\n", asi0.wLanguage);
      ok(asi0.dwScale == 1001, "got %u (expected 1001)\n", asi0.dwScale);
      ok(asi0.dwRate == 30000, "got %u (expected 30000)\n", asi0.dwRate);
      ok(asi0.dwStart == 0, "got %u (expected 0)\n", asi0.dwStart);
      ok(asi0.dwLength == 1, "got %u (expected 1)\n", asi0.dwLength);
      ok(asi0.dwInitialFrames == 0, "got %u (expected 0)\n", asi0.dwInitialFrames);
      ok(asi0.dwSuggestedBufferSize == 0, "got %u (expected 0)\n", asi0.dwSuggestedBufferSize);
      ok(asi0.dwQuality == 0xffffffff, "got 0x%x (expected 0xffffffff)\n", asi0.dwQuality);
      ok(asi0.dwSampleSize == 0, "got %u (expected 0)\n", asi0.dwSampleSize);
      ok(asi0.rcFrame.left == 0, "got %u (expected 0)\n", asi0.rcFrame.left);
      ok(asi0.rcFrame.top == 0, "got %u (expected 0)\n", asi0.rcFrame.top);
      ok(asi0.rcFrame.right == 8, "got %u (expected 8)\n", asi0.rcFrame.right);  /* these are based on the values in the mah and not */
      ok(asi0.rcFrame.bottom == 6, "got %u (expected 6)\n", asi0.rcFrame.bottom);/* on the ones in the ash which are 0 here */
      ok(asi0.dwEditCount == 0, "got %u (expected 0)\n", asi0.dwEditCount);
      ok(asi0.dwFormatChangeCount == 0, "got %u (expected 0)\n", asi0.dwFormatChangeCount);
  
      ok(asi1.fccType == streamtypeAUDIO, "got 0x%x (expected streamtypeVIDEO)\n", asi1.fccType);
      ok(asi1.fccHandler == 0x1, "got 0x%x (expected 0x1)\n", asi1.fccHandler);
      ok(asi1.dwFlags == 0, "got %u (expected 0)\n", asi1.dwFlags);
      ok(asi1.wPriority == 0, "got %u (expected 0)\n", asi1.wPriority);
      ok(asi1.wLanguage == 0, "got %u (expected 0)\n", asi1.wLanguage);
      ok(asi1.dwScale == 1, "got %u (expected 1)\n", asi1.dwScale);
      ok(asi1.dwRate == 11025, "got %u (expected 11025)\n", asi1.dwRate);
      ok(asi1.dwStart == 0, "got %u (expected 0)\n", asi1.dwStart);
      ok(asi1.dwLength == 1637, "got %u (expected 1637)\n", asi1.dwLength);
      ok(asi1.dwInitialFrames == 0, "got %u (expected 0)\n", asi1.dwInitialFrames);
      ok(asi1.dwSuggestedBufferSize == 0, "got %u (expected 0)\n", asi1.dwSuggestedBufferSize);
      ok(asi1.dwQuality == 0xffffffff, "got 0x%x (expected 0xffffffff)\n", asi1.dwQuality);
      ok(asi1.dwSampleSize == 2, "got %u (expected 2)\n", asi1.dwSampleSize);
      ok(asi1.rcFrame.left == 0, "got %u (expected 0)\n", asi1.rcFrame.left);
      ok(asi1.rcFrame.top == 0, "got %u (expected 0)\n", asi1.rcFrame.top);
      ok(asi1.rcFrame.right == 0, "got %u (expected 0)\n", asi1.rcFrame.right);
      ok(asi1.rcFrame.bottom == 0, "got %u (expected 0)\n", asi1.rcFrame.bottom);
      ok(asi1.dwEditCount == 0, "got %u (expected 0)\n", asi1.dwEditCount);
      ok(asi1.dwFormatChangeCount == 0, "got %u (expected 0)\n", asi1.dwFormatChangeCount);
  
      ok(wfx.wFormatTag == 1, "got %u (expected 1)\n",wfx.wFormatTag);
      ok(wfx.nChannels == 2, "got %u (expected 2)\n",wfx.nChannels);
      ok(wfx.wFormatTag == 1, "got %u (expected 1)\n",wfx.wFormatTag);
      ok(wfx.nSamplesPerSec == 11025, "got %u (expected 11025)\n",wfx.nSamplesPerSec);
      ok(wfx.nAvgBytesPerSec == 22050, "got %u (expected 22050)\n",wfx.nAvgBytesPerSec);
      ok(wfx.nBlockAlign == 2, "got %u (expected 2)\n",wfx.nBlockAlign);
  
      AVIStreamRelease(pStream0);
      AVIStreamRelease(pStream1);
      AVIFileRelease(pFile);
      ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
  }
  
  static void test_amh_corruption(void)
  {
      COMMON_AVI_HEADERS cah;
      char filename[MAX_PATH];
      PAVIFILE pFile;
      int res;
  
      GetTempPathA(MAX_PATH, filename);
      strcpy(filename+strlen(filename), testfilename);
  
      /* Make sure only AVI files with the proper headers will be loaded */
      init_test_struct(&cah);
      cah.fh[3] = mmioFOURCC('A', 'V', 'i', ' ');
  
      create_avi_file(&cah, filename);
      res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
      ok(res != 0, "Able to open file: error=%u\n", res);
  
      ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
  }
  
  static void test_ash1_corruption(void)
  {
      COMMON_AVI_HEADERS cah;
      char filename[MAX_PATH];
      PAVIFILE pFile;
      int res;
      PAVISTREAM pStream1;
      AVISTREAMINFOA asi1;
  
      GetTempPathA(MAX_PATH, filename);
      strcpy(filename+strlen(filename), testfilename);
  
      /* Corrupt the sample size in the audio stream header */
      init_test_struct(&cah);
      cah.ash1.dwSampleSize = 0xdeadbeef;
  
      create_avi_file(&cah, filename);
  
      res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
      ok(res == 0, "Unable to open file: error=%u\n", res);
  
      res = AVIFileGetStream(pFile, &pStream1, 0, 1);
      ok(res == 0, "Unable to open audio stream: error=%u\n", res);
  
      res = AVIStreamInfoA(pStream1, &asi1, sizeof(asi1));
      ok(res == 0, "Unable to read stream info: error=%u\n", res);
  
      /* The result will still be 2, because the value is dynamically replaced with the nBlockAlign
         value from the stream format header. The next test will prove this */
      ok(asi1.dwSampleSize == 2, "got %u (expected 2)\n", asi1.dwSampleSize);
  
      AVIStreamRelease(pStream1);
      AVIFileRelease(pFile);
      ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
  }
  
  static void test_ash1_corruption2(void)
  {
      COMMON_AVI_HEADERS cah;
      char filename[MAX_PATH];
      PAVIFILE pFile;
      int res;
      PAVISTREAM pStream1;
      AVISTREAMINFOA asi1;
  
      GetTempPathA(MAX_PATH, filename);
      strcpy(filename+strlen(filename), testfilename);
  
      /* Corrupt the block alignment in the audio format header */
      init_test_struct(&cah);
      cah.pcmwf.wf.nBlockAlign = 0xdead;
  
      create_avi_file(&cah, filename);
  
      res = AVIFileOpenA(&pFile, filename, OF_SHARE_DENY_WRITE, 0L);
      ok(res == 0, "Unable to open file: error=%u\n", res);
  
      res = AVIFileGetStream(pFile, &pStream1, 0, 1);
      ok(res == 0, "Unable to open audio stream: error=%u\n", res);
  
      ok(AVIStreamInfoA(pStream1, &asi1, sizeof(asi1)) == 0, "Unable to read stream info\n");
  
      /* The result will also be the corrupt value, as explained above. */
      ok(asi1.dwSampleSize == 0xdead, "got 0x%x (expected 0xdead)\n", asi1.dwSampleSize);
  
      AVIStreamRelease(pStream1);
      AVIFileRelease(pFile);
      ok(DeleteFileA(filename) !=0, "Deleting file %s failed\n", filename);
  }
  
  /* Outer IUnknown for COM aggregation tests */
  struct unk_impl {
      IUnknown IUnknown_iface;
      LONG ref;
      IUnknown *inner_unk;
  };
  
  static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface)
  {
      return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface);
  }
  
  static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
  {
      struct unk_impl *This = impl_from_IUnknown(iface);
      LONG ref = This->ref;
      HRESULT hr;
  
      if (IsEqualGUID(riid, &IID_IUnknown))
      {
          *ppv = iface;
          IUnknown_AddRef(iface);
          return S_OK;
      }
  
      hr = IUnknown_QueryInterface(This->inner_unk, riid, ppv);
      if (hr == S_OK)
      {
          trace("Working around COM aggregation ref counting bug\n");
          ok(ref == This->ref, "Outer ref count expected %d got %d\n", ref, This->ref);
          IUnknown_AddRef((IUnknown*)*ppv);
          ref = IUnknown_Release(This->inner_unk);
          ok(ref == 1, "Inner ref count expected 1 got %d\n", ref);
      }
  
      return hr;
  }
  
  static ULONG WINAPI unk_AddRef(IUnknown *iface)
  {
      struct unk_impl *This = impl_from_IUnknown(iface);
  
      return InterlockedIncrement(&This->ref);
  }
  
  static ULONG WINAPI unk_Release(IUnknown *iface)
  {
      struct unk_impl *This = impl_from_IUnknown(iface);
  
      return InterlockedDecrement(&This->ref);
  }
  
  static const IUnknownVtbl unk_vtbl =
  {
      unk_QueryInterface,
      unk_AddRef,
      unk_Release
  };
  
  static void test_COM(void)
  {
      struct unk_impl unk_obj = {{&unk_vtbl}, 19, NULL};
      IAVIFile *avif = NULL;
      IPersistFile *pf;
      IUnknown *unk;
      LONG refcount;
      HRESULT hr;
  
      /* COM aggregation */
      hr = CoCreateInstance(&CLSID_AVIFile, &unk_obj.IUnknown_iface, CLSCTX_INPROC_SERVER,
              &IID_IUnknown, (void**)&unk_obj.inner_unk);
      ok(hr == S_OK, "COM aggregation failed: %08x, expected S_OK\n", hr);
      hr = IUnknown_QueryInterface(&unk_obj.IUnknown_iface, &IID_IAVIFile, (void**)&avif);
      ok(hr == S_OK, "QueryInterface for IID_IAVIFile failed: %08x\n", hr);
      refcount = IAVIFile_AddRef(avif);
      ok(refcount == unk_obj.ref, "AVIFile just pretends to support COM aggregation\n");
      refcount = IAVIFile_Release(avif);
      ok(refcount == unk_obj.ref, "AVIFile just pretends to support COM aggregation\n");
      hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
      ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
      refcount = IPersistFile_Release(pf);
      ok(refcount == unk_obj.ref, "AVIFile just pretends to support COM aggregation\n");
      refcount = IAVIFile_Release(avif);
      ok(refcount == 19, "Outer ref count should be back at 19 but is %d\n", refcount);
      refcount = IUnknown_Release(unk_obj.inner_unk);
      ok(refcount == 0, "Inner ref count should be 0 but is %u\n", refcount);
  
      /* Invalid RIID */
      hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIStream,
              (void**)&avif);
      ok(hr == E_NOINTERFACE, "AVIFile create failed: %08x, expected E_NOINTERFACE\n", hr);
  
      /* Same refcount */
      hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIFile, (void**)&avif);
      ok(hr == S_OK, "AVIFile create failed: %08x, expected S_OK\n", hr);
      refcount = IAVIFile_AddRef(avif);
      ok(refcount == 2, "refcount == %u, expected 2\n", refcount);
      hr = IAVIFile_QueryInterface(avif, &IID_IUnknown, (void**)&unk);
      ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %08x\n", hr);
      refcount = IUnknown_AddRef(unk);
      ok(refcount == 4, "refcount == %u, expected 4\n", refcount);
      hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
      ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
      refcount = IPersistFile_AddRef(pf);
      ok(refcount == 6, "refcount == %u, expected 6\n", refcount);
  
      while (IAVIFile_Release(avif));
  }
  
  static void test_COM_wavfile(void)
  {
      struct unk_impl unk_obj = {{&unk_vtbl}, 19, NULL};
      IAVIFile *avif = NULL;
      IPersistFile *pf;
      IAVIStream *avis;
      IUnknown *unk;
      ULONG refcount;
      HRESULT hr;
  
      /* COM aggregation */
      hr = CoCreateInstance(&CLSID_WAVFile, &unk_obj.IUnknown_iface, CLSCTX_INPROC_SERVER,
              &IID_IUnknown, (void**)&unk_obj.inner_unk);
      ok(hr == S_OK, "COM aggregation failed: %08x, expected S_OK\n", hr);
      hr = IUnknown_QueryInterface(&unk_obj.IUnknown_iface, &IID_IAVIFile, (void**)&avif);
      ok(hr == S_OK, "QueryInterface for IID_IAVIFile failed: %08x\n", hr);
      refcount = IAVIFile_AddRef(avif);
      ok(refcount == unk_obj.ref, "WAVFile just pretends to support COM aggregation\n");
      refcount = IAVIFile_Release(avif);
      ok(refcount == unk_obj.ref, "WAVFile just pretends to support COM aggregation\n");
      hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
      ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
      refcount = IPersistFile_Release(pf);
      ok(refcount == unk_obj.ref, "WAVFile just pretends to support COM aggregation\n");
      refcount = IAVIFile_Release(avif);
      ok(refcount == 19, "Outer ref count should be back at 19 but is %d\n", refcount);
      refcount = IUnknown_Release(unk_obj.inner_unk);
      ok(refcount == 0, "Inner ref count should be 0 but is %u\n", refcount);
  
      /* Invalid RIID */
      hr = CoCreateInstance(&CLSID_WAVFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIStreaming,
              (void**)&avif);
      ok(hr == E_NOINTERFACE, "WAVFile create failed: %08x, expected E_NOINTERFACE\n", hr);
  
      /* Same refcount for all WAVFile interfaces */
      hr = CoCreateInstance(&CLSID_WAVFile, NULL, CLSCTX_INPROC_SERVER, &IID_IAVIFile, (void**)&avif);
      ok(hr == S_OK, "WAVFile create failed: %08x, expected S_OK\n", hr);
      refcount = IAVIFile_AddRef(avif);
      ok(refcount == 2, "refcount == %u, expected 2\n", refcount);
  
      hr = IAVIFile_QueryInterface(avif, &IID_IPersistFile, (void**)&pf);
      ok(hr == S_OK, "QueryInterface for IID_IPersistFile failed: %08x\n", hr);
      refcount = IPersistFile_AddRef(pf);
      ok(refcount == 4, "refcount == %u, expected 4\n", refcount);
      refcount = IPersistFile_Release(pf);
  
      hr = IAVIFile_QueryInterface(avif, &IID_IAVIStream, (void**)&avis);
      ok(hr == S_OK, "QueryInterface for IID_IAVIStream failed: %08x\n", hr);
      refcount = IAVIStream_AddRef(avis);
      ok(refcount == 5, "refcount == %u, expected 5\n", refcount);
      refcount = IAVIStream_Release(avis);
  
      hr = IAVIFile_QueryInterface(avif, &IID_IUnknown, (void**)&unk);
      ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %08x\n", hr);
      refcount = IUnknown_AddRef(unk);
      ok(refcount == 6, "refcount == %u, expected 6\n", refcount);
      refcount = IUnknown_Release(unk);
  
      while (IAVIFile_Release(avif));
  }
  
  static void test_COM_editstream(void)
  {
      IAVIEditStream *edit;
      IAVIStream *stream;
      IUnknown *unk;
      ULONG refcount;
      HRESULT hr;
  
      /* Same refcount for all AVIEditStream interfaces */
      hr = CreateEditableStream(&stream, NULL);
      ok(hr == S_OK, "AVIEditStream create failed: %08x, expected S_OK\n", hr);
      refcount = IAVIStream_AddRef(stream);
      ok(refcount == 2, "refcount == %u, expected 2\n", refcount);
  
      hr = IAVIStream_QueryInterface(stream, &IID_IAVIEditStream, (void**)&edit);
      ok(hr == S_OK, "QueryInterface for IID_IAVIEditStream failed: %08x\n", hr);
      refcount = IAVIEditStream_AddRef(edit);
      ok(refcount == 4, "refcount == %u, expected 4\n", refcount);
      refcount = IAVIEditStream_Release(edit);
  
      hr = IAVIEditStream_QueryInterface(edit, &IID_IUnknown, (void**)&unk);
      ok(hr == S_OK, "QueryInterface for IID_IUnknown failed: %08x\n", hr);
      refcount = IUnknown_AddRef(unk);
      ok(refcount == 5, "refcount == %u, expected 5\n", refcount);
      IUnknown_Release(unk);
  
      while (IAVIEditStream_Release(edit));
  }
  
  START_TEST(api)
  {
  
      AVIFileInit();
      test_EditStreamSetInfo();
      test_AVISaveOptions();
      test_default_data();
      test_amh_corruption();
      test_ash1_corruption();
      test_ash1_corruption2();
      test_COM();
      test_COM_wavfile();
      test_COM_editstream();
      AVIFileExit();
  
  }
  