Changeset 37928 in webkit
- Timestamp:
- Oct 28, 2008 10:26:58 AM (15 years ago)
- Location:
- trunk/WebKitTools
- Files:
-
- 2 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebKitTools/ChangeLog
r37904 r37928 1 2008-10-28 Pierre-Olivier Latour <pol@apple.com> 2 3 Reviewed by Dan Bernstein 4 5 Primary changes in DumpRenderTree: 6 - Ensure font smoothing is disabled (this is also called LCD anti-aliasing and 7 is different from regular font CG anti-aliasing) as font-smoothing settings 8 depends on the display and can also be changed by the user 9 - Use a new cleared buffer for each test instead of the reusing same one to 10 avoid potential result corruption across tests 11 - Can now receive the expected pixel hash as a suffix to the test path or 12 url as "path'hash" 13 - Make sure hash is computed in a endian-independent way 14 - Improve the code that sets/restores the screen color profile 15 - Make the code more cross-platformy with std::string goodness 16 - Added an "on-screen" mode where the snapshot will take into account surfaces 17 on the window (like OpenGL content): this uses the new CG APIs on 10.5 or 18 reading from the display framebuffer on 10.4. This mode is not active by 19 default for performance reason, but must be explicitly activated from the test 20 file using the new "testOnscreen()" JS API. 21 22 Primary changes in ImageDiff: 23 - Provide a new comparison algorithm that is more tolerant to "acceptable" 24 failures (i.e. very small differences in font rendering, which --threshold is 25 not really good at handling) 26 - Generate normalized intensity-only diff images 27 28 Primary changes in run-webkit-tests: 29 - Take advantage of hashes for pixel tests which makes them much faster by 30 minimizing image comparisons 31 - Removed repaint options as these should be set from within test files using 32 JS API 33 - Replaced "threshold" option in by "tolerance" expressed in percents 34 - Added more logging when in "verbose" mode 35 36 https://bugs.webkit.org/show_bug.cgi?id=21322 37 * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj: 38 * DumpRenderTree/ForwardingHeaders/wtf/PassRefPtr.h: Copied from JavaScriptGlue/ForwardingHeaders/wtf/PassRefPtr.h. 39 * DumpRenderTree/ForwardingHeaders/wtf/RefPtr.h: Copied from JavaScriptGlue/ForwardingHeaders/wtf/RefPtr.h. 40 * DumpRenderTree/LayoutTestController.cpp: 41 (LayoutTestController::LayoutTestController): 42 (testOnscreenCallback): 43 (LayoutTestController::staticFunctions): 44 * DumpRenderTree/LayoutTestController.h: 45 (LayoutTestController::testOnscreen): 46 (LayoutTestController::setTestOnscreen): 47 (LayoutTestController::testPathOrURL): 48 (LayoutTestController::expectedPixelHash): 49 * DumpRenderTree/PixelDumpSupport.h: 50 * DumpRenderTree/cg/ImageDiffCG.cpp: 51 (strtof): 52 (releaseMallocBuffer): 53 (createDifferenceImage): 54 (imageHasAlpha): 55 (main): 56 * DumpRenderTree/cg/PixelDumpSupportCG.cpp: 57 (computeMD5HashStringForBitmapContext): 58 (dumpWebViewAsPixelsAndCompareWithExpected): 59 * DumpRenderTree/cg/PixelDumpSupportCG.h: 60 (BitmapContext::createByAdoptingBitmapAndContext): 61 (BitmapContext::~BitmapContext): 62 (BitmapContext::cgContext): 63 (BitmapContext::BitmapContext): 64 * DumpRenderTree/mac/DumpRenderTree.mm: 65 (shouldIgnoreWebCoreNodeLeaks): 66 (setDefaultsToConsistentValuesForTesting): 67 (crashHandler): 68 (initializeGlobalsFromCommandLineOptions): 69 (prepareConsistentTestingEnvironment): 70 (dumpRenderTree): 71 (sizeWebViewForCurrentTest): 72 (dump): 73 (runTest): 74 * DumpRenderTree/mac/PixelDumpSupportMac.mm: 75 (restoreMainDisplayColorProfile): 76 (setupMainDisplayColorProfile): 77 (createBitmapContextFromWebView): 78 * DumpRenderTree/win/DumpRenderTree.cpp: 79 (dump): 80 (runTest): 81 * DumpRenderTree/win/PixelDumpSupportWin.cpp: 82 (getBitmapContextFromWebView): 83 * Scripts/run-webkit-tests: 84 1 85 2008-10-27 Kevin Ollivier <kevino@theolliviers.com> 2 86 -
trunk/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj
r35193 r37928 34 34 1AC6C84A0D07638600CD3161 /* PluginObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7800D07589B00CD3161 /* PluginObject.cpp */; }; 35 35 1AC6C84B0D07638600CD3161 /* TestObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7810D07589B00CD3161 /* TestObject.cpp */; }; 36 23BCB8900EA57623003C6289 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23BCB88F0EA57623003C6289 /* OpenGL.framework */; }; 36 37 9340994C08540CAE007F3BC8 /* DumpRenderTreePrefix.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */; }; 37 38 9340995108540CAE007F3BC8 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9335435F03D75502008635CE /* WebKit.framework */; }; … … 132 133 1AC6C7800D07589B00CD3161 /* PluginObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginObject.cpp; sourceTree = "<group>"; }; 133 134 1AC6C7810D07589B00CD3161 /* TestObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestObject.cpp; sourceTree = "<group>"; }; 135 23BCB88F0EA57623003C6289 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; }; 134 136 32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpRenderTreePrefix.h; sourceTree = "<group>"; }; 135 137 375F09710DAC3CB600C8B4E5 /* WebKitWeightWatcher100.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher100.ttf; path = fonts/WebKitWeightWatcher100.ttf; sourceTree = "<group>"; }; … … 235 237 A817090408B164D300CCB9FB /* JavaScriptCore.framework in Frameworks */, 236 238 9340995108540CAE007F3BC8 /* WebKit.framework in Frameworks */, 239 23BCB8900EA57623003C6289 /* OpenGL.framework in Frameworks */, 237 240 ); 238 241 runOnlyForDeploymentPostprocessing = 0; … … 376 379 B5A752A108AF5D1F00138E45 /* QuartzCore.framework */, 377 380 9335435F03D75502008635CE /* WebKit.framework */, 381 23BCB88F0EA57623003C6289 /* OpenGL.framework */, 378 382 ); 379 383 name = Frameworks; -
trunk/WebKitTools/DumpRenderTree/LayoutTestController.cpp
r37633 r37928 35 35 #include <wtf/MathExtras.h> 36 36 37 LayoutTestController::LayoutTestController( bool testRepaintDefault, bool testRepaintSweepHorizontallyDefault)37 LayoutTestController::LayoutTestController(const std::string& testPathOrURL, const std::string& expectedPixelHash) 38 38 : m_dumpAsText(false) 39 39 , m_dumpAsPDF(false) … … 55 55 , m_closeRemainingWindowsWhenComplete(true) 56 56 , m_stopProvisionalFrameLoads(false) 57 , m_testRepaint(testRepaintDefault) 58 , m_testRepaintSweepHorizontally(testRepaintSweepHorizontallyDefault) 57 , m_testOnscreen(false) 58 , m_testRepaint(false) 59 , m_testRepaintSweepHorizontally(false) 59 60 , m_waitToDump(false) 60 61 , m_windowIsKey(true) 61 62 , m_globalFlag(false) 63 , m_testPathOrURL(testPathOrURL) 64 , m_expectedPixelHash(expectedPixelHash) 62 65 { 63 66 } … … 203 206 LayoutTestController* controller = reinterpret_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); 204 207 controller->setCloseRemainingWindowsWhenComplete(JSValueToBoolean(context, arguments[0])); 208 return JSValueMakeUndefined(context); 209 } 210 211 static JSValueRef testOnscreenCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 212 { 213 LayoutTestController* controller = reinterpret_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject)); 214 controller->setTestOnscreen(true); 205 215 return JSValueMakeUndefined(context); 206 216 } … … 746 756 { "setUserStyleSheetLocation", setUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 747 757 { "setWindowIsKey", setWindowIsKeyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 758 { "testOnscreen", testOnscreenCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 748 759 { "testRepaint", testRepaintCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 749 760 { "waitUntilDone", waitUntilDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, -
trunk/WebKitTools/DumpRenderTree/LayoutTestController.h
r37633 r37928 32 32 #include <JavaScriptCore/JSObjectRef.h> 33 33 #include <wtf/RefCounted.h> 34 #include <string> 34 35 35 36 class LayoutTestController : public RefCounted<LayoutTestController> { 36 37 public: 37 LayoutTestController( bool testRepaintDefault, bool testRepaintSweepHorizontallyDefault);38 LayoutTestController(const std::string& testPathOrURL, const std::string& expectedPixelHash); 38 39 ~LayoutTestController(); 39 40 … … 131 132 void setStopProvisionalFrameLoads(bool stopProvisionalFrameLoads) { m_stopProvisionalFrameLoads = stopProvisionalFrameLoads; } 132 133 134 bool testOnscreen() const { return m_testOnscreen; } 135 void setTestOnscreen(bool testOnscreen) { m_testOnscreen = testOnscreen; } 136 133 137 bool testRepaint() const { return m_testRepaint; } 134 138 void setTestRepaint(bool testRepaint) { m_testRepaint = testRepaint; } … … 145 149 bool globalFlag() const { return m_globalFlag; } 146 150 void setGlobalFlag(bool globalFlag) { m_globalFlag = globalFlag; } 147 151 152 const std::string& testPathOrURL() const { return m_testPathOrURL; } 153 const std::string& expectedPixelHash() const { return m_expectedPixelHash; } 154 148 155 private: 149 156 bool m_dumpAsText; … … 166 173 bool m_closeRemainingWindowsWhenComplete; 167 174 bool m_stopProvisionalFrameLoads; 175 bool m_testOnscreen; 168 176 bool m_testRepaint; 169 177 bool m_testRepaintSweepHorizontally; … … 173 181 bool m_globalFlag; 174 182 183 std::string m_testPathOrURL; 184 std::string m_expectedPixelHash; // empty string if no hash 185 175 186 static JSClassRef getJSClass(); 176 187 static JSStaticValue* staticValues(); -
trunk/WebKitTools/DumpRenderTree/PixelDumpSupport.h
r29663 r37928 30 30 #define PixelDumpSupport_h 31 31 32 void dumpWebViewAsPixelsAndCompareWithExpected(const char* currentTest, bool forceAllTestsToDumpPixels); 32 #include <string> 33 34 void dumpWebViewAsPixelsAndCompareWithExpected(const std::string& expectedHash); 35 36 #if PLATFORM(MAC) 33 37 34 38 // Can be used as a signal handler 35 void restore ColorSpace(int ignored);39 void restoreMainDisplayColorProfile(int ignored); 36 40 37 // May change your color space, requiring a call to restoreColorSpace 38 void initializeColorSpaceAndScreeBufferForPixelTests(); 41 // May change your color space, requiring a call to restoreMainDisplayColorProfile 42 void setupMainDisplayColorProfile(); 43 44 #endif 39 45 40 46 #endif // PixelDumpSupport_h -
trunk/WebKitTools/DumpRenderTree/cg/ImageDiffCG.cpp
r37270 r37928 37 37 #include <fcntl.h> 38 38 #include <io.h> 39 #include <wtf/MathExtras.h> 39 40 #endif 40 41 … … 55 56 56 57 #if PLATFORM(WIN) 58 static inline float strtof(const char *nptr, char **endptr) 59 { 60 return strtod(nptr, endptr); 61 } 57 62 static const CFStringRef kUTTypePNG = CFSTR("public.png"); 58 63 #endif … … 73 78 } 74 79 75 // Generates an RGBA8 bitmap in the reference image colorspace containing the differences between the 2 images 76 static RetainPtr<CGContextRef> createDifferenceBitmap(CGImageRef testImage, CGImageRef referenceImage) 77 { 78 // we must have both images to take diff 79 if (!testImage || !referenceImage) { 80 fprintf(stderr, "test or reference image is missing!"); 81 return 0; 82 } 83 84 // we must have both images be the same dimensions 85 if ((CGImageGetWidth(testImage) != CGImageGetWidth(referenceImage)) || (CGImageGetHeight(testImage) != CGImageGetHeight(referenceImage))) { 86 fprintf(stderr, "test and reference image dimensions don't match!"); 87 return 0; 88 } 89 90 // we must have both images in the same colorspace 91 if (!CFEqual(CGImageGetColorSpace(testImage), CGImageGetColorSpace(referenceImage))) { 92 fprintf(stderr, "test and reference image colorspaces don't match!"); 93 return 0; 94 } 95 96 static CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); 97 size_t rowBytes = (4 * CGImageGetWidth(referenceImage) + 63) & ~63; // Use a multiple of 64 bytes to improve CG performance 98 CFDataSetLength(data, CGImageGetHeight(referenceImage) * rowBytes); 99 RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(CFDataGetMutableBytePtr(data), CGImageGetWidth(referenceImage), CGImageGetHeight(referenceImage), 8, rowBytes, CGImageGetColorSpace(referenceImage), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big)); 100 101 CGContextSetBlendMode(context.get(), kCGBlendModeNormal); 102 CGContextDrawImage(context.get(), CGRectMake(0, 0, static_cast<CGFloat>(CGImageGetWidth(testImage)), static_cast<CGFloat>(CGImageGetHeight(testImage))), testImage); 103 CGContextSetBlendMode(context.get(), kCGBlendModeDifference); 104 CGContextDrawImage(context.get(), CGRectMake(0, 0, static_cast<CGFloat>(CGImageGetWidth(referenceImage)), static_cast<CGFloat>(CGImageGetHeight(referenceImage))), referenceImage); 105 106 return context; 107 } 108 109 // Counts the number of non-black pixels, and returns the percentage of non-black pixels to total pixels in the image. 110 static float computePercentageDifferent(CGContextRef diffBitmap, unsigned threshold) 111 { 112 // if diffBitmap is nil, then there was an error, and it didn't match. 113 if (!diffBitmap) 114 return 100.0f; 115 116 size_t pixelsHigh = CGBitmapContextGetHeight(diffBitmap); 117 size_t pixelsWide = CGBitmapContextGetWidth(diffBitmap); 118 size_t bytesPerRow = CGBitmapContextGetBytesPerRow(diffBitmap); 119 unsigned char* pixelRowData = static_cast<unsigned char*>(CGBitmapContextGetData(diffBitmap)); 120 unsigned differences = 0; 121 122 for (unsigned row = 0; row < pixelsHigh; row++) { 123 for (unsigned col = 0; col < (pixelsWide * 4); col += 4) { 124 unsigned char* red = pixelRowData + col; 125 unsigned char* green = red + 1; 126 unsigned char* blue = red + 2; 127 unsigned distance = *red + *green + *blue; 128 if (distance > threshold) { 129 differences++; 130 // shift the pixels towards white to make them more visible 131 *red = static_cast<unsigned char>(min(UCHAR_MAX, *red + 100)); 132 *green = static_cast<unsigned char>(min(UCHAR_MAX, *green + 100)); 133 *blue = static_cast<unsigned char>(min(UCHAR_MAX, *blue + 100)); 80 static void releaseMallocBuffer(void* info, const void* data, size_t size) 81 { 82 free((void*)data); 83 } 84 85 static RetainPtr<CGImageRef> createDifferenceImage(CGImageRef baseImage, CGImageRef testImage, float& difference) 86 { 87 static RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); 88 RetainPtr<CGImageRef> diffImage; 89 90 size_t width = CGImageGetWidth(baseImage); 91 size_t height = CGImageGetHeight(baseImage); 92 size_t rowBytes = width * 4; 93 94 // Draw base image in bitmap context 95 void* baseBuffer = calloc(height, rowBytes); 96 CGContextRef baseContext = CGBitmapContextCreate(baseBuffer, width, height, 8, rowBytes, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); 97 CGContextDrawImage(baseContext, CGRectMake(0, 0, width, height), baseImage); 98 CGContextRelease(baseContext); 99 100 // Draw test image in bitmap context 101 void* buffer = calloc(height, rowBytes); 102 CGContextRef context = CGBitmapContextCreate(buffer, width, height, 8, rowBytes, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); 103 CGContextDrawImage(context, CGRectMake(0, 0, width, height), testImage); 104 CGContextRelease(context); 105 106 // Compare the content of the 2 bitmaps 107 void* diffBuffer = malloc(width * height); 108 float count = 0.0f; 109 float sum = 0.0f; 110 float maxDistance = 0.0f; 111 unsigned char* basePixel = (unsigned char*)baseBuffer; 112 unsigned char* pixel = (unsigned char*)buffer; 113 unsigned char* diff = (unsigned char*)diffBuffer; 114 for (size_t y = 0; y < height; ++y) { 115 for (size_t x = 0; x < width; ++x) { 116 float red = (pixel[0] - basePixel[0]) / max<float>(255 - basePixel[0], basePixel[0]); 117 float green = (pixel[1] - basePixel[1]) / max<float>(255 - basePixel[1], basePixel[1]); 118 float blue = (pixel[2] - basePixel[2]) / max<float>(255 - basePixel[2], basePixel[2]); 119 float alpha = (pixel[3] - basePixel[3]) / max<float>(255 - basePixel[3], basePixel[3]); 120 float distance = sqrtf(red * red + green * green + blue * blue + alpha * alpha) / 2.0f; 121 122 *diff++ = (unsigned char)(distance * 255.0f); 123 124 if (distance >= 1.0f / 255.0f) { 125 count += 1.0f; 126 sum += distance; 127 if (distance > maxDistance) 128 maxDistance = distance; 134 129 } 135 } 136 pixelRowData += bytesPerRow; 137 } 138 139 float totalPixels = static_cast<float>(pixelsHigh * pixelsWide); 140 return (differences * 100.f) / totalPixels; 141 } 142 143 static void compareImages(CGImageRef testImage, CGImageRef referenceImage, unsigned threshold) 144 { 145 // prepare the difference blend to check for pixel variations 146 RetainPtr<CGContextRef> diffBitmap = createDifferenceBitmap(testImage, referenceImage); 147 148 float percentage = computePercentageDifferent(diffBitmap.get(), threshold); 149 150 percentage = (float)((int)(percentage * 100.0f)) / 100.0f; // round to 2 decimal places 151 152 // send message to let them know if an image was wrong 153 if (percentage > 0.0f) { 154 // since the diff might actually show something, send it to stdout 155 if (diffBitmap) { 156 RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(diffBitmap.get())); 157 RetainPtr<CFMutableDataRef> imageData(AdoptCF, CFDataCreateMutable(0, 0)); 158 RetainPtr<CGImageDestinationRef> imageDest(AdoptCF, CGImageDestinationCreateWithData(imageData.get(), kUTTypePNG, 1, 0)); 159 CGImageDestinationAddImage(imageDest.get(), image.get(), 0); 160 CGImageDestinationFinalize(imageDest.get()); 161 printf("Content-Length: %lu\n", CFDataGetLength(imageData.get())); 162 fwrite(CFDataGetBytePtr(imageData.get()), 1, CFDataGetLength(imageData.get()), stdout); 130 131 basePixel += 4; 132 pixel += 4; 133 } 134 } 135 136 // Compute the difference as a percentage combining both the number of different pixels and their difference amount i.e. the average distance over the entire image 137 if (count > 0.0f) 138 difference = 100.0f * sum / (height * width); 139 else 140 difference = 0.0f; 141 142 // Generate a normalized diff image if there is any difference 143 if (difference > 0.0f) { 144 if (maxDistance < 1.0f) { 145 diff = (unsigned char*)diffBuffer; 146 for(size_t p = 0; p < height * width; ++p) 147 diff[p] = diff[p] / maxDistance; 163 148 } 164 149 165 fprintf(stdout, "diff: %01.2f%% failed\n", percentage); 166 } else 167 fprintf(stdout, "diff: %01.2f%% passed\n", percentage); 150 CGDataProviderRef provider = CGDataProviderCreateWithData(0, diffBuffer, width * height, releaseMallocBuffer); 151 CGColorSpaceRef diffColorspace = CGColorSpaceCreateDeviceGray(); 152 diffImage.adoptCF(CGImageCreate(width, height, 8, 8, width, diffColorspace, 0, provider, 0, false, kCGRenderingIntentDefault)); 153 CGColorSpaceRelease(diffColorspace); 154 CGDataProviderRelease(provider); 155 } 156 else 157 free(diffBuffer); 158 159 // Destroy drawing buffers 160 if (buffer) 161 free(buffer); 162 if (baseBuffer) 163 free(baseBuffer); 164 165 return diffImage; 166 } 167 168 static inline bool imageHasAlpha(CGImageRef image) 169 { 170 CGImageAlphaInfo info = CGImageGetAlphaInfo(image); 171 172 return (info >= kCGImageAlphaPremultipliedLast) && (info <= kCGImageAlphaFirst); 168 173 } 169 174 … … 175 180 #endif 176 181 177 unsigned threshold = 0;182 float tolerance = 0.0f; 178 183 179 184 for (int i = 1; i < argc; ++i) { 180 if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--t hreshold")) {185 if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--tolerance")) { 181 186 if (i >= argc - 1) 182 187 exit(1); 183 t hreshold = strtol(argv[i + 1], 0, 0);188 tolerance = strtof(argv[i + 1], 0); 184 189 ++i; 185 190 continue; … … 210 215 211 216 if (actualImage && baselineImage) { 212 compareImages(actualImage.get(), baselineImage.get(), threshold); 217 RetainPtr<CGImageRef> diffImage; 218 float difference = 100.0f; 219 220 if ((CGImageGetWidth(actualImage.get()) == CGImageGetWidth(baselineImage.get())) && (CGImageGetHeight(actualImage.get()) == CGImageGetHeight(baselineImage.get())) && (imageHasAlpha(actualImage.get()) == imageHasAlpha(baselineImage.get()))) { 221 diffImage = createDifferenceImage(actualImage.get(), baselineImage.get(), difference); // difference is passed by reference 222 if (difference <= tolerance) 223 difference = 0.0f; 224 else { 225 difference = roundf(difference * 100.0f) / 100.0f; 226 difference = max(difference, 0.01f); // round to 2 decimal places 227 } 228 } else 229 fputs("error, test and reference image have different properties.\n", stderr); 230 231 if (difference > 0.0f) { 232 if (diffImage) { 233 RetainPtr<CFMutableDataRef> imageData(AdoptCF, CFDataCreateMutable(0, 0)); 234 RetainPtr<CGImageDestinationRef> imageDest(AdoptCF, CGImageDestinationCreateWithData(imageData.get(), kUTTypePNG, 1, 0)); 235 CGImageDestinationAddImage(imageDest.get(), diffImage.get(), 0); 236 CGImageDestinationFinalize(imageDest.get()); 237 printf("Content-Length: %lu\n", CFDataGetLength(imageData.get())); 238 fwrite(CFDataGetBytePtr(imageData.get()), 1, CFDataGetLength(imageData.get()), stdout); 239 } 240 241 fprintf(stdout, "diff: %01.2f%% failed\n", difference); 242 } else 243 fprintf(stdout, "diff: %01.2f%% passed\n", difference); 244 213 245 actualImage = 0; 214 246 baselineImage = 0; -
trunk/WebKitTools/DumpRenderTree/cg/PixelDumpSupportCG.cpp
r37594 r37928 35 35 #include "LayoutTestController.h" 36 36 #include <ImageIO/CGImageDestination.h> 37 #include <algorithm> 38 #include <ctype.h> 37 39 #include <wtf/Assertions.h> 40 #include <wtf/RefPtr.h> 38 41 #include <wtf/RetainPtr.h> 39 42 #include <wtf/StringExtras.h> … … 64 67 } 65 68 66 static void getMD5HashStringForBitmap(CGContextRef bitmap, char string[33])69 static void computeMD5HashStringForBitmapContext(CGContextRef bitmapContext, char hashString[33]) 67 70 { 71 ASSERT(CGBitmapContextGetBitsPerPixel(bitmapContext) == 32); // ImageDiff assumes 32 bit RGBA, we must as well. 72 size_t pixelsHigh = CGBitmapContextGetHeight(bitmapContext); 73 size_t pixelsWide = CGBitmapContextGetWidth(bitmapContext); 74 size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmapContext); 75 76 // We need to swap the bytes to ensure consistent hashes independently of endianness 68 77 MD5_CTX md5Context; 78 MD5_Init(&md5Context); 79 unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmapContext)); 80 #if PLATFORM(MAC) 81 if ((CGBitmapContextGetBitmapInfo(bitmapContext) & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Big) { 82 for (unsigned row = 0; row < pixelsHigh; row++) { 83 uint32_t buffer[pixelsWide]; 84 for (unsigned column = 0; column < pixelsWide; column++) 85 buffer[column] = OSReadLittleInt32(bitmapData, 4 * column); 86 MD5_Update(&md5Context, buffer, 4 * pixelsWide); 87 bitmapData += bytesPerRow; 88 } 89 } else 90 #endif 91 { 92 for (unsigned row = 0; row < pixelsHigh; row++) { 93 MD5_Update(&md5Context, bitmapData, 4 * pixelsWide); 94 bitmapData += bytesPerRow; 95 } 96 } 69 97 unsigned char hash[16]; 70 71 size_t bitsPerPixel = CGBitmapContextGetBitsPerPixel(bitmap);72 ASSERT(bitsPerPixel == 32); // ImageDiff assumes 32 bit RGBA, we must as well.73 size_t bytesPerPixel = bitsPerPixel / 8;74 size_t pixelsHigh = CGBitmapContextGetHeight(bitmap);75 size_t pixelsWide = CGBitmapContextGetWidth(bitmap);76 size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmap);77 ASSERT(bytesPerRow >= (pixelsWide * bytesPerPixel));78 79 MD5_Init(&md5Context);80 unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmap));81 for (unsigned row = 0; row < pixelsHigh; row++) {82 MD5_Update(&md5Context, bitmapData, static_cast<unsigned>(pixelsWide * bytesPerPixel));83 bitmapData += bytesPerRow;84 }85 98 MD5_Final(hash, &md5Context); 86 99 87 string[0] = '\0';100 hashString[0] = '\0'; 88 101 for (int i = 0; i < 16; i++) 89 snprintf( string, 33, "%s%02x", string, hash[i]);102 snprintf(hashString, 33, "%s%02x", hashString, hash[i]); 90 103 } 91 104 92 void d rawSelectionRect(CGContextRef context, const CGRect& rect)105 void dumpWebViewAsPixelsAndCompareWithExpected(const std::string& expectedHash) 93 106 { 94 CGContextSaveGState(context); 95 CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); 96 CGContextStrokeRect(context, rect); 97 CGContextRestoreGState(context); 98 } 99 100 void dumpWebViewAsPixelsAndCompareWithExpected(const char* /*currentTest*/, bool /*forceAllTestsToDumpPixels*/) 101 { 102 RetainPtr<CGContextRef> context = getBitmapContextFromWebView(); 103 107 RefPtr<BitmapContext> context; 108 104 109 #if PLATFORM(MAC) 105 if (gLayoutTestController->testRepaint()) 106 repaintWebView(context.get(), gLayoutTestController->testRepaintSweepHorizontally()); 107 else 108 paintWebView(context.get()); 109 110 if (gLayoutTestController->dumpSelectionRect()) 111 drawSelectionRect(context.get(), getSelectionRect()); 110 context = createBitmapContextFromWebView(gLayoutTestController->testOnscreen(), gLayoutTestController->testRepaint(), gLayoutTestController->testRepaintSweepHorizontally(), gLayoutTestController->dumpSelectionRect()); 112 111 #endif 113 114 // Compute the actual hash to compare to the expected image's hash. 112 ASSERT(context); 113 114 // Compute the hash of the bitmap context pixels 115 115 char actualHash[33]; 116 getMD5HashStringForBitmap(context.get(), actualHash);116 computeMD5HashStringForBitmapContext(context->cgContext(), actualHash); 117 117 printf("\nActualHash: %s\n", actualHash); 118 119 // FIXME: We should compare the actualHash to the expected hash here and 120 // only set dumpImage to true if they don't match, but DRT doesn't have 121 // enough information currently to find the expected checksum file. 118 119 // Check the computed hash against the expected one and dump image on mismatch 122 120 bool dumpImage = true; 123 121 if (expectedHash.length() > 0) { 122 ASSERT(expectedHash.length() == 32); 123 124 printf("\nExpectedHash: %s\n", expectedHash.c_str()); 125 126 if (expectedHash == actualHash) // FIXME: do case insensitive compare 127 dumpImage = false; 128 } 129 124 130 if (dumpImage) { 125 RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context .get()));131 RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context->cgContext())); 126 132 printPNG(image.get()); 127 133 } -
trunk/WebKitTools/DumpRenderTree/cg/PixelDumpSupportCG.h
r29663 r37928 32 32 #define PixelDumpSupportCG_h 33 33 34 #include <wtf/PassRefPtr.h> 35 #include <wtf/RefCounted.h> 34 36 #include <wtf/RetainPtr.h> 35 37 36 #ifndef CGFLOAT_DEFINED 37 #ifdef __LP64__ 38 typedef double CGFloat; 39 #else 40 typedef float CGFloat; 41 #endif 42 #define CGFLOAT_DEFINED 1 38 typedef struct CGContext* CGContextRef; 39 40 #if PLATFORM(MAC) 41 typedef void* PlatformBitmapBuffer; 42 #elif PLATFORM(WIN) 43 typedef HBITMAP PlatformBitmapBuffer; 43 44 #endif 44 45 45 typedef struct CGContext* CGContextRef; 46 struct CGRect; 46 class BitmapContext : public RefCounted<BitmapContext> { 47 public: 48 static PassRefPtr<BitmapContext> createByAdoptingBitmapAndContext(PlatformBitmapBuffer buffer, CGContextRef context) 49 { 50 return adoptRef(new BitmapContext(buffer, context)); 51 } 47 52 48 RetainPtr<CGContextRef> getBitmapContextFromWebView(); 49 CGRect getSelectionRect(); 53 ~BitmapContext() 54 { 55 if (m_buffer) 56 #if PLATFORM(MAC) 57 free(m_buffer); 58 #elif PLATFORM(WIN) 59 DeleteObject(m_buffer); 60 #endif 61 } 50 62 51 void paintWebView(CGContextRef); 52 void repaintWebView(CGContextRef context, bool horizontal); 53 void drawSelectionRect(CGContextRef, const CGRect&); 63 CGContextRef cgContext() const { return m_context.get(); } 64 65 private: 66 67 BitmapContext(PlatformBitmapBuffer buffer, CGContextRef context) 68 : m_buffer(buffer) 69 , m_context(AdoptCF, context) 70 { 71 } 72 73 PlatformBitmapBuffer m_buffer; 74 RetainPtr<CGContextRef> m_context; 75 76 }; 77 78 PassRefPtr<BitmapContext> createBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect); 54 79 55 80 #endif // PixelDumpSupportCG_h -
trunk/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm
r37633 r37928 76 76 #import <wtf/OwnPtr.h> 77 77 78 using namespace std; 79 78 80 @interface DumpRenderTreeEvent : NSEvent 79 81 @end 80 82 81 static void runTest(const char *pathOrURL);83 static void runTest(const string& testPathOrURL); 82 84 83 85 // Deciding when it's OK to dump out the state is a bit tricky. All these must be true: … … 112 114 113 115 static int dumpPixels; 114 static int dumpAllPixels;115 116 static int threaded; 116 static int testRepaintDefault;117 static int repaintSweepHorizontallyDefault;118 117 static int dumpTree = YES; 119 118 static int forceComplexText; 120 119 static BOOL printSeparators; 121 static NSString *currentTest = nil;122 120 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation; 123 121 … … 182 180 } 183 181 184 static BOOL shouldIgnoreWebCoreNodeLeaks(CFStringRefURLString)185 { 186 static CFStringRefconst ignoreSet[] = {182 static bool shouldIgnoreWebCoreNodeLeaks(const string& URLString) 183 { 184 static char* const ignoreSet[] = { 187 185 // Keeping this infrastructure around in case we ever need it again. 188 186 }; 189 static const int ignoreSetCount = sizeof(ignoreSet) / sizeof( CFStringRef);187 static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(char*); 190 188 191 189 for (int i = 0; i < ignoreSetCount; i++) { 192 CFStringRef ignoreString = ignoreSet[i];193 CFRange range = CFRangeMake(0, CFStringGetLength(URLString));194 CFOptionFlags flags = kCFCompareAnchored | kCFCompareBackwards | kCFCompareCaseInsensitive;195 if ( CFStringFindWithOptions(URLString, ignoreString, range, flags, NULL))196 return YES;197 } 198 return NO;190 // FIXME: ignore case 191 string curIgnore(ignoreSet[i]); 192 // Match at the end of the URLString 193 if (!URLString.compare(URLString.length() - curIgnore.length(), curIgnore.length(), curIgnore)) 194 return true; 195 } 196 return false; 199 197 } 200 198 … … 308 306 { 309 307 // Give some clear to undocumented defaults values 310 static const int MediumFontSmoothing = 2;308 static const int NoFontSmoothing = 0; 311 309 static const int BlueTintedAppearance = 1; 312 310 … … 314 312 [defaults setObject:@"DoubleMax" forKey:@"AppleScrollBarVariant"]; 315 313 [defaults setInteger:4 forKey:@"AppleAntiAliasingThreshold"]; // smallest font size to CG should perform antialiasing on 316 [defaults setInteger: MediumFontSmoothing forKey:@"AppleFontSmoothing"];314 [defaults setInteger:NoFontSmoothing forKey:@"AppleFontSmoothing"]; 317 315 [defaults setInteger:BlueTintedAppearance forKey:@"AppleAquaColorVariant"]; 318 316 [defaults setObject:@"0.709800 0.835300 1.000000" forKey:@"AppleHighlightColor"]; … … 351 349 write(STDERR_FILENO, signalName, strlen(signalName)); 352 350 write(STDERR_FILENO, "\n", 1); 353 restore ColorSpace(0);351 restoreMainDisplayColorProfile(0); 354 352 exit(128 + sig); 355 353 } … … 400 398 { 401 399 struct option options[] = { 402 {"dump-all-pixels", no_argument, &dumpAllPixels, YES},403 {"horizontal-sweep", no_argument, &repaintSweepHorizontallyDefault, YES},404 400 {"notree", no_argument, &dumpTree, NO}, 405 401 {"pixel-tests", no_argument, &dumpPixels, YES}, 406 {"repaint", no_argument, &testRepaintDefault, YES},407 402 {"tree", no_argument, &dumpTree, YES}, 408 403 {"threaded", no_argument, &threaded, YES}, … … 461 456 462 457 if (dumpPixels) 463 initializeColorSpaceAndScreeBufferForPixelTests();458 setupMainDisplayColorProfile(); 464 459 allocateGlobalControllers(); 465 460 … … 527 522 528 523 if (dumpPixels) 529 restore ColorSpace(0);524 restoreMainDisplayColorProfile(0); 530 525 } 531 526 … … 822 817 { 823 818 // W3C SVG tests expect to be 480x360 824 bool isSVGW3CTest = ( [currentTest rangeOfString:@"svg/W3C-SVG-1.1"].length);819 bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos); 825 820 if (isSVGW3CTest) 826 821 [[mainFrame webView] setFrameSize:NSMakeSize(480, 360)]; … … 916 911 } 917 912 918 if (dump AllPixels || (dumpPixels && !dumpAsText))919 dumpWebViewAsPixelsAndCompareWithExpected( [currentTest UTF8String], dumpAllPixels);913 if (dumpPixels && !dumpAsText) 914 dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash()); 920 915 921 916 puts("#EOF"); // terminate the (possibly empty) pixels block … … 930 925 { 931 926 return strstr(pathOrURL, "loading/"); 932 }933 934 static CFURLRef createCFURLFromPathOrURL(CFStringRef pathOrURLString)935 {936 CFURLRef URL;937 if (CFStringHasPrefix(pathOrURLString, CFSTR("http://")) || CFStringHasPrefix(pathOrURLString, CFSTR("https://")))938 URL = CFURLCreateWithString(NULL, pathOrURLString, NULL);939 else940 URL = CFURLCreateWithFileSystemPath(NULL, pathOrURLString, kCFURLPOSIXPathStyle, FALSE);941 return URL;942 927 } 943 928 … … 970 955 } 971 956 972 static void runTest(const char *pathOrURL) 973 { 974 CFStringRef pathOrURLString = CFStringCreateWithCString(NULL, pathOrURL, kCFStringEncodingUTF8); 957 static void runTest(const string& testPathOrURL) 958 { 959 ASSERT(!testPathOrURL.empty()); 960 961 // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows. 962 string pathOrURL(testPathOrURL); 963 string expectedPixelHash; 964 965 size_t separatorPos = pathOrURL.find("'"); 966 if (separatorPos != string::npos) { 967 pathOrURL = string(testPathOrURL, 0, separatorPos); 968 expectedPixelHash = string(testPathOrURL, separatorPos + 1); 969 } 970 971 NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()]; 975 972 if (!pathOrURLString) { 976 fprintf(stderr, "Failed to parse filename as UTF-8: %s\n", pathOrURL);973 fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str()); 977 974 return; 978 975 } 979 976 980 CFURLRef URL = createCFURLFromPathOrURL(pathOrURLString); 981 if (!URL) { 982 CFRelease(pathOrURLString); 983 fprintf(stderr, "Can't turn %s into a CFURL\n", pathOrURL); 977 NSURL *url; 978 if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"]) 979 url = [NSURL URLWithString:pathOrURLString]; 980 else 981 url = [NSURL fileURLWithPath:pathOrURLString]; 982 if (!url) { 983 fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str()); 984 984 return; 985 985 } 986 986 987 const string testURL([[url absoluteString] UTF8String]); 988 987 989 resetWebViewToConsistentStateBeforeTesting(); 988 990 989 gLayoutTestController = new LayoutTestController(test RepaintDefault, repaintSweepHorizontallyDefault);991 gLayoutTestController = new LayoutTestController(testURL, expectedPixelHash); 990 992 topLoadingFrame = nil; 991 993 done = NO; … … 993 995 if (disallowedURLs) 994 996 CFSetRemoveAllValues(disallowedURLs); 995 if (shouldLogFrameLoadDelegates(pathOrURL ))997 if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) 996 998 gLayoutTestController->setDumpFrameLoadCallbacks(true); 997 999 … … 1001 1003 lastClickPosition = NSZeroPoint; 1002 1004 1003 if (currentTest != nil)1004 CFRelease(currentTest);1005 currentTest = (NSString *)pathOrURLString;1006 1005 [prevTestBFItem release]; 1007 1006 prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain]; … … 1010 1009 WorkQueue::shared()->setFrozen(false); 1011 1010 1012 BOOL _shouldIgnoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(CFURLGetString(URL));1013 if ( _shouldIgnoreWebCoreNodeLeaks)1011 bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL); 1012 if (ignoreWebCoreNodeLeaks) 1014 1013 [WebCoreStatistics startIgnoringWebCoreNodeLeaks]; 1015 1014 1016 1015 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1017 [mainFrame loadRequest:[NSURLRequest requestWithURL:(NSURL *)URL]]; 1018 CFRelease(URL); 1016 [mainFrame loadRequest:[NSURLRequest requestWithURL:url]]; 1019 1017 [pool release]; 1020 1018 while (!done) { … … 1059 1057 gLayoutTestController = 0; 1060 1058 1061 if ( _shouldIgnoreWebCoreNodeLeaks)1059 if (ignoreWebCoreNodeLeaks) 1062 1060 [WebCoreStatistics stopIgnoringWebCoreNodeLeaks]; 1063 1061 } -
trunk/WebKitTools/DumpRenderTree/mac/PixelDumpSupportMac.mm
r32995 r37928 35 35 #include "LayoutTestController.h" 36 36 #include <CoreGraphics/CGBitmapContext.h> 37 #ifndef BUILDING_ON_LEOPARD 38 #include <OpenGL/OpenGL.h> 39 #include <OpenGL/CGLMacro.h> 40 #endif 37 41 #include <wtf/Assertions.h> 38 #include <wtf/Re tainPtr.h>42 #include <wtf/RefPtr.h> 39 43 40 44 #import <WebKit/WebDocumentPrivate.h> 41 45 #import <WebKit/WebKit.h> 42 46 43 static unsigned char* screenCaptureBuffer; 44 45 static bool changedColorProfile = false; 46 static CMProfileLocation currentColorProfileLocation; 47 static CGColorSpaceRef sharedColorSpace; 48 49 void restoreColorSpace(int ignored) 47 // To ensure pixel tests consistency, we need to always render in the same colorspace. 48 // Unfortunately, because of AppKit / WebKit constraints, we can't render directly in the colorspace of our choice. 49 // This implies we have to temporarily change the profile of the main display to the colorspace we want to render into. 50 // We also need to make sure the CGBitmapContext we return is in that same colorspace. 51 52 #define PROFILE_PATH "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc" // FIXME: Should we be using "/System/Library/ColorSync/Profiles/sRGB Profile.icc"? 53 54 static CMProfileLocation sInitialProfileLocation; // The locType field is initialized to 0 which is the same as cmNoProfileBase 55 56 void restoreMainDisplayColorProfile(int ignored) 50 57 { 51 58 // This is used as a signal handler, and thus the calls into ColorSync are unsafe 52 59 // But we might as well try to restore the user's color profile, we're going down anyway... 53 if ( changedColorProfile) {54 CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };55 int error = CMSetDeviceProfile(cmDisplayDeviceClass, cmDefaultDeviceID, &scope, cmDefaultProfileID, ¤tColorProfileLocation);60 if (sInitialProfileLocation.locType != cmNoProfileBase) { 61 const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost }; 62 int error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &sInitialProfileLocation); 56 63 if (error) 57 fprintf(stderr, "Failed to re tore previous color profile! You may need to open System Preferences : Displays : Color and manually restore your color settings. (Error: %i)", error);58 changedColorProfile = false;64 fprintf(stderr, "Failed to restore initial color profile for main display! Open System Preferences > Displays > Color and manually re-select the profile. (Error: %i)", error); 65 sInitialProfileLocation.locType = cmNoProfileBase; 59 66 } 60 67 } 61 68 62 static void failedGettingCurrentProfile(int error)69 void setupMainDisplayColorProfile() 63 70 { 64 fprintf(stderr, "Failed to get current color profile. I will not be able to restore your current profile, thus I'm not changing it. Many pixel tests may fail as a result. (Error: %i)\n", error); 71 const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost }; 72 int error; 73 74 CMProfileRef profile = 0; 75 error = CMGetProfileByAVID((CMDisplayIDType)kCGDirectMainDisplay, &profile); 76 if (!error) { 77 UInt32 size = sizeof(CMProfileLocation); 78 error = NCMGetProfileLocation(profile, &sInitialProfileLocation, &size); 79 CMCloseProfile(profile); 80 } 81 if (error) { 82 fprintf(stderr, "Failed to retrieve current color profile for main display, thus it won't be changed. Many pixel tests may fail as a result. (Error: %i)", error); 83 sInitialProfileLocation.locType = cmNoProfileBase; 84 return; 85 } 86 87 CMProfileLocation location; 88 location.locType = cmFileBasedProfile; 89 NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:PROFILE_PATH]]; 90 FSRef fsRef; 91 if (url && CFURLGetFSRef((CFURLRef)url, &fsRef)) { 92 error = FSGetCatalogInfo(&fsRef, 0, 0, 0, &location.u.fileLoc.spec, 0); 93 if (error == noErr) 94 error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &location); 95 } 96 else 97 error = -1; 98 99 if (error) { 100 fprintf(stderr, "Failed to set color profile for main display! Many pixel tests may fail as a result. (Error: %i)", error); 101 sInitialProfileLocation.locType = cmNoProfileBase; 102 return; 103 } 104 105 // Other signals are handled in installSignalHandlers() which also calls restoreMainDisplayColorProfile() 106 signal(SIGINT, restoreMainDisplayColorProfile); 107 signal(SIGHUP, restoreMainDisplayColorProfile); 108 signal(SIGTERM, restoreMainDisplayColorProfile); 109 110 fprintf(stderr, "\n\nWARNING: Temporarily changing the main display color profile to \"%s\": the colors on your screen will change for the duration of the testing.\n", PROFILE_PATH); 111 fprintf(stderr, "This allows the WebKit pixel-based regression tests to have consistent color values across all machines.\n"); 65 112 } 66 113 67 static void setDefaultColorProfileToRGB()114 PassRefPtr<BitmapContext> createBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect) 68 115 { 69 CMProfileRef genericProfile = (CMProfileRef)[[NSColorSpace genericRGBColorSpace] colorSyncProfile]; 70 CMProfileLocation genericProfileLocation; 71 UInt32 locationSize = sizeof(genericProfileLocation); 72 int error = NCMGetProfileLocation(genericProfile, &genericProfileLocation, &locationSize); 73 if (error) { 74 failedGettingCurrentProfile(error); 75 return; 76 } 77 78 CMProfileLocation previousProfileLocation; 79 error = CMGetDeviceProfile(cmDisplayDeviceClass, cmDefaultDeviceID, cmDefaultProfileID, &previousProfileLocation); 80 if (error) { 81 failedGettingCurrentProfile(error); 82 return; 83 } 84 85 CMProfileRef previousProfile; 86 error = CMOpenProfile(&previousProfile, &previousProfileLocation); 87 if (error) { 88 failedGettingCurrentProfile(error); 89 return; 90 } 91 92 CFStringRef previousProfileName; 93 CFStringRef genericProfileName; 94 char previousProfileNameString[1024]; 95 char genericProfileNameString[1024]; 96 CMCopyProfileDescriptionString(previousProfile, &previousProfileName); 97 CMCopyProfileDescriptionString(genericProfile, &genericProfileName); 98 CFStringGetCString(previousProfileName, previousProfileNameString, sizeof(previousProfileNameString), kCFStringEncodingUTF8); 99 CFStringGetCString(genericProfileName, genericProfileNameString, sizeof(previousProfileNameString), kCFStringEncodingUTF8); 100 CFRelease(previousProfileName); 101 CFRelease(genericProfileName); 102 103 CMCloseProfile(previousProfile); 104 105 fprintf(stderr, "\n\nWARNING: Temporarily changing your system color profile from \"%s\" to \"%s\".\n", previousProfileNameString, genericProfileNameString); 106 fprintf(stderr, "This allows the WebKit pixel-based regression tests to have consistent color values across all machines.\n"); 107 fprintf(stderr, "The colors on your screen will change for the duration of the testing.\n\n"); 108 109 CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost }; 110 if ((error = CMSetDeviceProfile(cmDisplayDeviceClass, cmDefaultDeviceID, &scope, cmDefaultProfileID, &genericProfileLocation))) { 111 fprintf(stderr, "Failed to set color profile to \"%s\"! Many pixel tests will fail as a result. (Error: %i)", 112 genericProfileNameString, error); 116 WebView* view = [mainFrame webView]; 117 NSSize webViewSize = [view frame].size; 118 size_t pixelsWide = static_cast<size_t>(webViewSize.width); 119 size_t pixelsHigh = static_cast<size_t>(webViewSize.height); 120 size_t rowBytes = (4 * pixelsWide + 63) & ~63; // Use a multiple of 64 bytes to improve CG performance 121 122 void *buffer = calloc(pixelsHigh, rowBytes); 123 if (!buffer) 124 return 0; 125 126 static CGColorSpaceRef colorSpace = 0; 127 if (!colorSpace) { 128 CMProfileLocation location; 129 location.locType = cmFileBasedProfile; 130 NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:PROFILE_PATH]]; 131 FSRef fsRef; 132 if (url && CFURLGetFSRef((CFURLRef)url, &fsRef)) { 133 if (FSGetCatalogInfo(&fsRef, 0, 0, 0, &location.u.fileLoc.spec, 0) == noErr) { 134 CMProfileRef profile; 135 if (CMOpenProfile(&profile, &location) == noErr) { 136 colorSpace = CGColorSpaceCreateWithPlatformColorSpace(profile); 137 CMCloseProfile(profile); 138 } 139 } 140 } 141 } 142 143 CGContextRef context = CGBitmapContextCreate(buffer, pixelsWide, pixelsHigh, 8, rowBytes, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); // Use ARGB8 on PPC or BGRA8 on X86 to improve CG performance 144 if (!context) { 145 free(buffer); 146 return 0; 147 } 148 149 // The BitmapContext keeps the CGContextRef and the pixel buffer alive 150 RefPtr<BitmapContext> bitmapContext = BitmapContext::createByAdoptingBitmapAndContext(buffer, context); 151 152 NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]; 153 ASSERT(nsContext); 154 155 if (incrementalRepaint) { 156 if (sweepHorizontally) { 157 for (NSRect column = NSMakeRect(0, 0, 1, webViewSize.height); column.origin.x < webViewSize.width; column.origin.x++) 158 [view displayRectIgnoringOpacity:column inContext:nsContext]; 159 } else { 160 for (NSRect line = NSMakeRect(0, 0, webViewSize.width, 1); line.origin.y < webViewSize.height; line.origin.y++) 161 [view displayRectIgnoringOpacity:line inContext:nsContext]; 162 } 113 163 } else { 114 currentColorProfileLocation = previousProfileLocation; 115 changedColorProfile = true; 116 signal(SIGINT, restoreColorSpace); 117 signal(SIGHUP, restoreColorSpace); 118 signal(SIGTERM, restoreColorSpace); 119 } 164 if (onscreen) { 165 #ifdef BUILDING_ON_LEOPARD 166 // Ask the window server to provide us a composited version of the *real* window content including surfaces (i.e. OpenGL content) 167 // Note that the returned image might differ very slightly from the window backing because of dithering artifacts in the window server compositor 168 169 CGImageRef image = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, [[view window] windowNumber], kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque); 170 CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); 171 CGImageRelease(image); 172 #else 173 // On 10.4 and earlier, we have to move the window temporarily "onscreen" and read directly from the display framebuffer using OpenGL 174 // In this code path, we need to ensure the window is above any other window or captured result will be corrupted 175 176 NSWindow *window = [view window]; 177 int oldLevel = [window level]; 178 NSRect oldFrame = [window frame]; 179 180 NSRect newFrame = [[[NSScreen screens] objectAtIndex:0] frame]; 181 newFrame = NSMakeRect(newFrame.origin.x + (newFrame.size.width - oldFrame.size.width) / 2, newFrame.origin.y + (newFrame.size.height - oldFrame.size.height) / 2, oldFrame.size.width, oldFrame.size.height); 182 [window setLevel:NSScreenSaverWindowLevel]; 183 [window setFrame:newFrame display:NO animate:NO]; 184 185 CGRect rect = CGRectMake(newFrame.origin.x, newFrame.origin.y, webViewSize.width, webViewSize.height); 186 CGDirectDisplayID displayID; 187 CGDisplayCount count; 188 if (CGGetDisplaysWithRect(rect, 1, &displayID, &count) == kCGErrorSuccess) { 189 CGRect bounds = CGDisplayBounds(displayID); 190 rect.origin.x -= bounds.origin.x; 191 rect.origin.y -= bounds.origin.y; 192 193 CGLPixelFormatAttribute attributes[] = {kCGLPFAAccelerated, kCGLPFANoRecovery, kCGLPFAFullScreen, kCGLPFADisplayMask, (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID), (CGLPixelFormatAttribute)0}; 194 CGLPixelFormatObj pixelFormat; 195 GLint num; 196 if (CGLChoosePixelFormat(attributes, &pixelFormat, &num) == kCGLNoError) { 197 CGLContextObj cgl_ctx; 198 if (CGLCreateContext(pixelFormat, 0, &cgl_ctx) == kCGLNoError) { 199 if (CGLSetFullScreen(cgl_ctx) == kCGLNoError) { 200 void *flipBuffer = calloc(pixelsHigh, rowBytes); 201 if (flipBuffer) { 202 glPixelStorei(GL_PACK_ROW_LENGTH, rowBytes / 4); 203 glPixelStorei(GL_PACK_ALIGNMENT, 4); 204 #if __BIG_ENDIAN__ 205 glReadPixels(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, flipBuffer); 206 #else 207 glReadPixels(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, flipBuffer); 208 #endif 209 if (!glGetError()) { 210 for(size_t i = 0; i < pixelsHigh; ++i) 211 bcopy((char*)flipBuffer + rowBytes * i, (char*)buffer + rowBytes * (pixelsHigh - i - 1), pixelsWide * 4); 212 } 213 214 free(flipBuffer); 215 } 216 } 217 CGLDestroyContext(cgl_ctx); 218 } 219 CGLDestroyPixelFormat(pixelFormat); 220 } 221 } 222 223 [window setFrame:oldFrame display:NO animate:NO]; 224 [window setLevel:oldLevel]; 225 #endif 226 } else { 227 // Grab directly the contents of the window backing buffer (this ignores any surfaces on the window) 228 // FIXME: This path is suboptimal: data is read from window backing store, converted to RGB8 then drawn again into an RGBA8 bitmap 229 230 [view displayIfNeeded]; 231 [view lockFocus]; 232 NSBitmapImageRep *imageRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:[view frame]] autorelease]; 233 [view unlockFocus]; 234 235 RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext]; 236 [NSGraphicsContext setCurrentContext:nsContext]; 237 [imageRep draw]; 238 [NSGraphicsContext setCurrentContext:savedContext.get()]; 239 } 240 } 241 242 if (drawSelectionRect) { 243 NSView *documentView = [[mainFrame frameView] documentView]; 244 ASSERT([documentView conformsToProtocol:@protocol(WebDocumentSelection)]); 245 NSRect rect = [documentView convertRect:[(id <WebDocumentSelection>)documentView selectionRect] fromView:nil]; 246 CGContextSaveGState(context); 247 CGContextSetLineWidth(context, 1.0); 248 CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); 249 CGContextStrokeRect(context, CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)); 250 CGContextRestoreGState(context); 251 } 252 253 return bitmapContext.release(); 120 254 } 121 122 void initializeColorSpaceAndScreeBufferForPixelTests()123 {124 setDefaultColorProfileToRGB();125 screenCaptureBuffer = (unsigned char *)malloc(maxViewHeight * maxViewWidth * 4);126 sharedColorSpace = CGColorSpaceCreateDeviceRGB();127 }128 129 // Declared in PixelDumpSupportCG.h130 131 RetainPtr<CGContextRef> getBitmapContextFromWebView()132 {133 NSSize webViewSize = [[mainFrame webView] frame].size;134 return RetainPtr<CGContextRef>(AdoptCF, CGBitmapContextCreate(screenCaptureBuffer, static_cast<size_t>(webViewSize.width), static_cast<size_t>(webViewSize.height), 8, static_cast<size_t>(webViewSize.width) * 4, sharedColorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedLast));135 }136 137 void paintWebView(CGContextRef context)138 {139 RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext];140 141 NSGraphicsContext* nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];142 [NSGraphicsContext setCurrentContext:nsContext];143 144 WebView* view = [mainFrame webView];145 [view displayIfNeeded];146 [view lockFocus];147 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[view frame]];148 [view unlockFocus];149 [imageRep draw];150 [imageRep release];151 152 [NSGraphicsContext setCurrentContext:savedContext.get()];153 }154 155 void repaintWebView(CGContextRef context, bool horizontal)156 {157 RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext];158 159 NSGraphicsContext* nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];160 [NSGraphicsContext setCurrentContext:nsContext];161 162 WebView *view = [mainFrame webView];163 NSSize webViewSize = [view frame].size;164 165 if (horizontal) {166 for (NSRect column = NSMakeRect(0, 0, 1, webViewSize.height); column.origin.x < webViewSize.width; column.origin.x++)167 [view displayRectIgnoringOpacity:column inContext:nsContext];168 } else {169 for (NSRect line = NSMakeRect(0, 0, webViewSize.width, 1); line.origin.y < webViewSize.height; line.origin.y++)170 [view displayRectIgnoringOpacity:line inContext:nsContext];171 }172 173 [NSGraphicsContext setCurrentContext:savedContext.get()];174 }175 176 CGRect getSelectionRect()177 {178 NSView *documentView = [[mainFrame frameView] documentView];179 if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {180 NSRect rect = [documentView convertRect:[(id <WebDocumentSelection>)documentView selectionRect] fromView:nil];181 return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);182 }183 184 ASSERT_NOT_REACHED();185 return CGRectZero;186 } -
trunk/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp
r37633 r37928 39 39 #include "WorkQueueItem.h" 40 40 #include "WorkQueue.h" 41 42 #include <fcntl.h> 43 #include <io.h> 44 #include <math.h> 45 #include <pthread.h> 46 #include <shlwapi.h> 47 #include <stdio.h> 48 #include <string.h> 49 #include <tchar.h> 41 50 #include <wtf/RetainPtr.h> 42 51 #include <wtf/Vector.h> 52 #include <windows.h> 53 #include <CFNetwork/CFURLCachePriv.h> 54 #include <CoreFoundation/CoreFoundation.h> 55 #include <JavaScriptCore/JavaScriptCore.h> 43 56 #include <WebCore/COMPtr.h> 44 #include <CoreFoundation/CoreFoundation.h>45 #include <CFNetwork/CFURLCachePriv.h>46 #include <JavaScriptCore/JavaScriptCore.h>47 #include <math.h>48 #include <pthread.h>49 #include <string>50 #include <tchar.h>51 57 #include <WebKit/ForEachCoClass.h> 52 58 #include <WebKit/WebKit.h> 53 #include <fcntl.h> 54 #include <io.h> 55 #include <windows.h> 56 #include <stdio.h> 57 #include <shlwapi.h> 58 59 using std::wstring; 59 60 using namespace std; 60 61 61 62 #ifndef NDEBUG … … 78 79 static bool forceComplexText = false; 79 80 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation; 80 81 static const char* currentTest;82 81 83 82 volatile bool done; … … 563 562 resultString = SysAllocStringLen(result.data(), result.size()); 564 563 } else { 565 bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");564 bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg\\W3C-SVG-1.1") != string::npos); 566 565 unsigned width; 567 566 unsigned height; … … 609 608 if (dumpPixels) { 610 609 if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive()) 611 dumpWebViewAsPixelsAndCompareWithExpected( currentTest, dumpAllPixels);610 dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash()); 612 611 } 613 612 … … 676 675 } 677 676 678 static void runTest(const char* pathOrURL)677 static void runTest(const string& testPathOrURL) 679 678 { 680 679 static BSTR methodBStr = SysAllocString(TEXT("GET")); 681 680 681 // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows. 682 string pathOrURL(testPathOrURL); 683 string expectedPixelHash; 684 685 size_t separatorPos = pathOrURL.find("'"); 686 if (separatorPos != string::npos) { 687 pathOrURL = string(testPathOrURL, 0, separatorPos); 688 expectedPixelHash = string(testPathOrURL, separatorPos + 1); 689 } 690 682 691 BSTR urlBStr; 683 692 684 CFStringRef str = CFStringCreateWithCString(0, pathOrURL , kCFStringEncodingWindowsLatin1);693 CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1); 685 694 CFURLRef url = CFURLCreateWithString(0, str, 0); 686 695 … … 701 710 CFRelease(url); 702 711 703 currentTest = pathOrURL; 704 705 ::gLayoutTestController = new LayoutTestController(false, false); 712 ::gLayoutTestController = new LayoutTestController(pathOrURL, expectedPixelHash); 706 713 done = false; 707 714 topLoadingFrame = 0; 708 715 709 if (shouldLogFrameLoadDelegates(pathOrURL ))716 if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) 710 717 gLayoutTestController->setDumpFrameLoadCallbacks(true); 711 718 -
trunk/WebKitTools/DumpRenderTree/win/PixelDumpSupportWin.cpp
r37587 r37928 35 35 #include <wtf/RetainPtr.h> 36 36 37 RetainPtr<CGContextRef> getBitmapContextFromWebView()37 PassRefPtr<BitmapContext> getBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect) 38 38 { 39 39 RECT frame; … … 49 49 bmp.bmiHeader.biCompression = BI_RGB; 50 50 51 // FIXME: Currently we leak this HBITMAP because we don't have a good way52 // to destroy it when the CGBitmapContext gets destroyed.53 51 void* bits = 0; 54 52 HBITMAP bitmap = CreateDIBSection(0, &bmp, DIB_RGB_COLORS, &bits, 0, 0); … … 64 62 65 63 RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); 66 return RetainPtr<CGContextRef>(AdoptCF, CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, 67 info.bmWidthBytes, colorSpace.get(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst)); 64 CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8, 65 info.bmWidthBytes, colorSpace.get(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 66 67 return BitmapContext::createByAdoptingBitmapAndContext(bitmap, context); 68 68 } -
trunk/WebKitTools/Scripts/run-webkit-tests
r37675 r37928 113 113 my $pixelTests = ''; 114 114 my $quiet = ''; 115 my $repaintSweepHorizontally = '';116 my $repaintTests = '';117 115 my $report10Slowest = 0; 118 116 my $resetResults = 0; … … 124 122 my $testResultsDirectory = "/tmp/layout-test-results"; 125 123 my $threaded = 0; 126 my $t hreshold= 0;124 my $tolerance = 0; 127 125 my $treatSkipped = "default"; 128 126 my $verbose = 0; … … 181 179 -g|--guard-malloc Enable malloc guard 182 180 --help Show this help message 183 -h|--horizontal-sweep Change repaint to sweep horizontally instead of vertically (implies --repaint-tests)184 181 --[no-]http Run (or do not run) http tests (default: $httpDefault) 185 182 -i|--ignore-tests Comma-separated list of directories or tests to ignore … … 188 185 --[no-]new-test-results Generate results for new tests 189 186 -p|--pixel-tests Enable pixel tests 187 --tolerance t Ignore image differences less than this percentage (implies --pixel-tests) 190 188 --platform Override the detected platform to use for tests and results (default: $platform) 191 189 --port Web server port to use with http tests 192 190 -q|--quiet Less verbose output 193 -r|--repaint-tests Run repaint tests (implies --pixel-tests)194 191 --reset-results Reset ALL results (including pixel tests if --pixel-tests is set) 195 192 -o|--results-directory Output results directory (default: $testResultsDirectory) … … 207 204 --[no-]strip-editing-callbacks Remove editing callbacks from expected results 208 205 -t|--threaded Run a concurrent JavaScript thead with each test 209 --threshold t Ignore pixel value deviations less than or equal to t210 206 --valgrind Run DumpRenderTree inside valgrind (Qt/Linux only) 211 207 -v|--verbose More verbose output (overrides --quiet) … … 222 218 'guard-malloc|g' => \$guardMalloc, 223 219 'help' => \$showHelp, 224 'horizontal-sweep|h' => \$repaintSweepHorizontally,225 220 'http!' => \$testHTTP, 226 221 'ignore-tests|i=s' => \$ignoreTests, … … 231 226 'port=i' => \$httpdPort, 232 227 'quiet|q' => \$quiet, 233 'repaint-tests|r' => \$repaintTests,234 228 'reset-results' => \$resetResults, 235 229 'new-test-results!' => \$generateNewResults, … … 240 234 'slowest' => \$report10Slowest, 241 235 'threaded|t' => \$threaded, 242 't hreshold=i' => \$threshold,236 'tolerance=f' => \$tolerance, 243 237 'verbose|v' => \$verbose, 244 238 'valgrind' => \$useValgrind, … … 268 262 my $configurationOption = "--" . lc($configuration); 269 263 270 $repaintTests = 1 if $repaintSweepHorizontally; 271 272 $pixelTests = 1 if $repaintTests; 273 $pixelTests = 1 if $threshold > 0; 264 $pixelTests = 1 if $tolerance > 0; 274 265 275 266 $testsPerDumpTool = 1000 if !$testsPerDumpTool; … … 292 283 chdirWebKit(); 293 284 294 if (!defined($root)){285 if (!defined($root)) { 295 286 # Push the parameters to build-dumprendertree as an array 296 287 my @args; … … 455 446 my $leaksOutputFileNumber = 1; 456 447 my $totalLeaks = 0; 457 my $dumpAllPixels = $pixelTests && $resetResults;458 448 459 449 my @toolArgs = (); 460 push @toolArgs, "--dump-all-pixels" if $dumpAllPixels;461 450 push @toolArgs, "--pixel-tests" if $pixelTests; 462 push @toolArgs, "--repaint" if $repaintTests;463 push @toolArgs, "--horizontal-sweep" if $repaintSweepHorizontally;464 451 push @toolArgs, "--threaded" if $threaded; 465 452 push @toolArgs, "--complex-text" if $complexText; … … 467 454 468 455 my @diffToolArgs = (); 469 push @diffToolArgs, "--t hreshold", $threshold;456 push @diffToolArgs, "--tolerance", $tolerance; 470 457 471 458 $| = 1; … … 554 541 my $startTime = time if $report10Slowest; 555 542 543 # Try to read expected hash file for pixel tests 544 my $suffixExpectedHash = ""; 545 if ($pixelTests && !$resetResults) { 546 my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png"); 547 if (open EXPECTEDHASH, "$expectedPixelDir/$base-$expectedTag.checksum") { 548 my $expectedHash = <EXPECTEDHASH>; 549 chomp($expectedHash); 550 close EXPECTEDHASH; 551 552 # Format expected hash into a suffix string that is appended to the path / URL passed to DRT 553 $suffixExpectedHash = "'$expectedHash"; 554 } 555 } 556 556 557 if ($test !~ /^http\//) { 557 558 my $testPath = "$testDirectory/$test"; … … 561 562 $testPath = canonpath($testPath); 562 563 } 563 print OUT "$testPath \n";564 print OUT "$testPath$suffixExpectedHash\n"; 564 565 } else { 565 566 openHTTPDIfNeeded(); … … 567 568 my $path = canonpath($test); 568 569 $path =~ s/^http\/tests\///; 569 print OUT "http://127.0.0.1:$httpdPort/$path \n";570 print OUT "http://127.0.0.1:$httpdPort/$path$suffixExpectedHash\n"; 570 571 } elsif ($test =~ /^http\/tests\/ssl\//) { 571 572 my $path = canonpath($test); 572 573 $path =~ s/^http\/tests\///; 573 print OUT "https://127.0.0.1:$httpdSSLPort/$path \n";574 print OUT "https://127.0.0.1:$httpdSSLPort/$path$suffixExpectedHash\n"; 574 575 } else { 575 576 my $testPath = "$testDirectory/$test"; … … 579 580 $testPath = canonpath($testPath); 580 581 } 581 print OUT "$testPath \n";582 print OUT "$testPath$suffixExpectedHash\n"; 582 583 } 583 584 } … … 642 643 my $diffResult = "passed"; 643 644 644 # Look for the pixel block, and do an image diff if we find one645 645 my $actualHash = ""; 646 646 my $expectedHash = ""; … … 651 651 if (/ActualHash: ([a-f0-9]{32})/) { 652 652 $actualHash = $1; 653 } elsif (/ BaselineHash: ([a-f0-9]{32})/) {653 } elsif (/ExpectedHash: ([a-f0-9]{32})/) { 654 654 $expectedHash = $1; 655 655 } elsif (/Content-Length: (\d+)\s*/) { … … 659 659 } 660 660 661 # If we got some PNG data, diff with expected 661 if ($verbose && $pixelTests && !$resetResults && $actualPNGSize) { 662 if ($actualHash eq "" && $expectedHash eq "") { 663 printFailureMessageForTest($test, "WARNING: actual & expected pixel hashes are missing!"); 664 } elsif ($actualHash eq "") { 665 printFailureMessageForTest($test, "WARNING: actual pixel hash is missing!"); 666 } elsif ($expectedHash eq "") { 667 printFailureMessageForTest($test, "WARNING: expected pixel hash is missing!"); 668 } 669 } 670 662 671 if ($actualPNGSize > 0) { 663 my $expectedPixelDir = expectedDirectoryForTest($base, $isText, "png"); 664 665 if ($expectedHash ne $actualHash && -f "$expectedPixelDir/$base-$expectedTag.png") { 666 my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png"; 667 my $expectedPNG = ""; 668 open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png"; 669 read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize); 670 671 print DIFFOUT "Content-Length: $actualPNGSize\n"; 672 print DIFFOUT $actualPNG; 673 674 print DIFFOUT "Content-Length: $expectedPNGSize\n"; 675 print DIFFOUT $expectedPNG; 676 677 while (<DIFFIN>) { 678 last if /^error/ || /^diff:/; 679 if (/Content-Length: (\d+)\s*/) { 680 read(DIFFIN, $diffPNG, $1); 672 my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png"); 673 674 if (!$resetResults && ($expectedHash ne $actualHash || ($actualHash eq "" && $expectedHash eq ""))) { 675 if (-f "$expectedPixelDir/$base-$expectedTag.png") { 676 my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png"; 677 my $expectedPNG = ""; 678 open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png"; 679 read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize); 680 681 print DIFFOUT "Content-Length: $actualPNGSize\n"; 682 print DIFFOUT $actualPNG; 683 684 print DIFFOUT "Content-Length: $expectedPNGSize\n"; 685 print DIFFOUT $expectedPNG; 686 687 while (<DIFFIN>) { 688 last if /^error/ || /^diff:/; 689 if (/Content-Length: (\d+)\s*/) { 690 read(DIFFIN, $diffPNG, $1); 691 } 681 692 } 693 694 if (/^diff: (.+)% (passed|failed)/) { 695 $diffPercentage = $1; 696 $imageDifferences{$base} = $diffPercentage; 697 $diffResult = $2; 698 } 699 700 if ($diffPercentage == 0) { 701 printFailureMessageForTest($test, "pixel hash failed (but pixel test still passes)"); 702 } 703 } elsif ($verbose) { 704 printFailureMessageForTest($test, "WARNING: expected image is missing!"); 682 705 } 683 684 if (/^diff: (.+)% (passed|failed)/) { 685 $diffPercentage = $1; 686 $imageDifferences{$base} = $diffPercentage; 687 $diffResult = $2; 688 } 689 } 690 691 if ($actualPNGSize && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png")) { 706 } 707 708 if ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png") { 692 709 mkpath catfile($expectedPixelDir, dirname($base)) if $testDirectory ne $expectedPixelDir; 693 710 writeToFile("$expectedPixelDir/$base-$expectedTag.png", $actualPNG); 694 711 } 695 712 696 # update the expected hash if the image diff said that there was no difference697 713 if ($actualHash ne "" && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.checksum")) { 698 714 writeToFile("$expectedPixelDir/$base-$expectedTag.checksum", $actualHash); … … 742 758 743 759 $diffResult = "failed"; 744 if ($verbose) {760 if ($verbose) { 745 761 print "\n"; 746 762 system "cat /tmp/simplified.diff"; … … 818 834 writeToFile("$testResultsDirectory/$base-$diffsTag.png", $diffPNG); 819 835 820 my $expectedPixelDir = expectedDirectoryForTest($base, $isText, "png");836 my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png"); 821 837 copy("$expectedPixelDir/$base-$expectedTag.png", "$testResultsDirectory/$base-$expectedTag.png"); 822 838
Note: See TracChangeset
for help on using the changeset viewer.