Bug 589668: take screenshots on test failures on Windows, screenshot utility written by Ted Mielczarek <ted.mielczarek@gmail.com>, build goop by me and jhford, r=khuey
authorGavin Sharp <gavin@gavinsharp.com>
Wed, 08 Feb 2012 12:30:07 -0800
changeset 89538 84f9eb64e005f030783e99cd3a5c309238c8af84
parent 89537 30b51328336115ec294fba9a104a60c07e8daf23
child 89539 1d61262c243cc16ea84496126fd1a13ecb70aa41
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerskhuey
bugs589668
milestone14.0a1
Bug 589668: take screenshots on test failures on Windows, screenshot utility written by Ted Mielczarek <ted.mielczarek@gmail.com>, build goop by me and jhford, r=khuey
build/automation.py.in
testing/mochitest/Makefile.in
testing/tools/screenshot/Makefile.in
testing/tools/screenshot/win32-screenshot.cpp
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -695,18 +695,18 @@ user_pref("camino.use_system_proxy_setti
     # Need to figure out what tool and whether it write to a file or stdout
     if self.UNIXISH:
       utility = [os.path.join(utilityPath, "screentopng")]
       imgoutput = 'stdout'
     elif self.IS_MAC:
       utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
       imgoutput = 'file'
     elif self.IS_WIN32:
-      self.log.info("If you fixed bug 589668, you'd get a screenshot here")
-      return
+      utility = [os.path.join(utilityPath, "screenshot.exe")]
+      imgoutput = 'file'
 
     # Run the capture correctly for the type of capture
     try:
       if imgoutput == 'file':
         tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail_')
         os.close(tmpfd)
         dumper = self.Process(utility + [imgfilename])
       elif imgoutput == 'stdout':
@@ -722,17 +722,17 @@ user_pref("camino.use_system_proxy_setti
     if dumper.returncode != 0:
       self.log.info("%s exited with code %d", utility, dumper.returncode)
       return
 
     try:
       if imgoutput == 'stdout':
         image = dumper_out
       elif imgoutput == 'file':
-        with open(imgfilename) as imgfile:
+        with open(imgfilename, 'rb') as imgfile:
           image = imgfile.read()
     except IOError, err:
         self.log.info("Failed to read image from %s", imgoutput)
 
     import base64
     encoded = base64.b64encode(image)
     self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -178,16 +178,20 @@ endif
 ifeq ($(OS_ARCH),Linux)
 TEST_HARNESS_BINS += fix-linux-stack.pl
 endif
 
 ifeq (gtk2_1,$(MOZ_WIDGET_TOOLKIT)_$(MOZ_X11))
 TEST_HARNESS_BINS += screentopng
 endif
 
+ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
+TEST_HARNESS_BINS += screenshot$(BIN_SUFFIX)
+endif
+
 # Components / typelibs that don't get packaged with
 # the build, but that we need for the test harness.
 TEST_HARNESS_COMPONENTS := \
   test_necko.xpt \
   $(NULL)
 
 # We need the test plugin as some tests rely on it
 ifeq (Darwin,$(OS_TARGET))
--- a/testing/tools/screenshot/Makefile.in
+++ b/testing/tools/screenshot/Makefile.in
@@ -48,9 +48,18 @@ ifdef MOZ_X11
 PROGRAM = screentopng
 CPPSRCS = gdk-screenshot.cpp
 LOCAL_INCLUDES = $(MOZ_GTK2_CFLAGS)
 OS_LIBS = $(MOZ_GTK2_LIBS) $(XSS_LIBS)
 
 endif # X11
 endif # GTK2
 
+ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
+
+PROGRAM = screenshot$(BIN_SUFFIX)
+CPPSRCS = win32-screenshot.cpp
+MOZ_GLUE_PROGRAM_LDFLAGS =
+USE_STATIC_LIBS = 1
+
+endif # windows
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/testing/tools/screenshot/win32-screenshot.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009, The Mozilla Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Mozilla Foundation nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY The Mozilla Foundation ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL The Mozilla Foundation BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Contributors:
+ *   Ted Mielczarek <ted.mielczarek@gmail.com>
+ */
+/*
+ * screenshot.cpp : Save a screenshot of the Windows desktop in .png format.
+ *  If a filename is specified as the first argument on the commandline,
+ *  then the image will be saved to that filename. Otherwise, the image will
+ *  be saved as "screenshot.png" in the current working directory.
+ *
+ *  Requires GDI+. All linker dependencies are specified explicitly in this
+ *  file, so you can compile screenshot.exe by simply running:
+ *    cl screenshot.cpp
+ */
+
+#undef WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <gdiplus.h>
+
+#pragma comment(lib, "user32.lib")
+#pragma comment(lib, "gdi32.lib")
+#pragma comment(lib, "gdiplus.lib")
+
+using namespace Gdiplus;
+
+// From http://msdn.microsoft.com/en-us/library/ms533843%28VS.85%29.aspx
+int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
+{
+  UINT  num = 0;          // number of image encoders
+  UINT  size = 0;         // size of the image encoder array in bytes
+
+  ImageCodecInfo* pImageCodecInfo = NULL;
+
+  GetImageEncodersSize(&num, &size);
+  if(size == 0)
+    return -1;  // Failure
+
+  pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
+  if(pImageCodecInfo == NULL)
+    return -1;  // Failure
+
+  GetImageEncoders(num, size, pImageCodecInfo);
+
+  for(UINT j = 0; j < num; ++j)
+    {
+      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
+        {
+          *pClsid = pImageCodecInfo[j].Clsid;
+          free(pImageCodecInfo);
+          return j;  // Success
+        }    
+    }
+
+  free(pImageCodecInfo);
+  return -1;  // Failure
+}
+
+int wmain(int argc, wchar_t** argv)
+{
+  GdiplusStartupInput gdiplusStartupInput;
+  ULONG_PTR gdiplusToken;
+  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+  HWND desktop = GetDesktopWindow();
+  HDC desktopdc = GetDC(desktop);
+  HDC mydc = CreateCompatibleDC(desktopdc);
+  int width = GetSystemMetrics(SM_CXSCREEN);
+  int height = GetSystemMetrics(SM_CYSCREEN);
+  HBITMAP mybmp = CreateCompatibleBitmap(desktopdc, width, height);
+  HBITMAP oldbmp = (HBITMAP)SelectObject(mydc, mybmp);
+  BitBlt(mydc,0,0,width,height,desktopdc,0,0, SRCCOPY|CAPTUREBLT);
+  SelectObject(mydc, oldbmp);
+
+  const wchar_t* filename = (argc > 1) ? argv[1] : L"screenshot.png";
+  Bitmap* b = Bitmap::FromHBITMAP(mybmp, NULL);
+  CLSID  encoderClsid;
+  Status stat = GenericError;
+  if (b && GetEncoderClsid(L"image/png", &encoderClsid) != -1) {
+    stat = b->Save(filename, &encoderClsid, NULL);
+  }
+  if (b)
+    delete b;
+  
+  // cleanup
+  GdiplusShutdown(gdiplusToken);
+  ReleaseDC(desktop, desktopdc);
+  DeleteObject(mybmp);
+  DeleteDC(mydc);
+  return stat == Ok ? 0 : 1;
+}