📄 test_regression.cpp
字号:
case Node::ELEMENT_NODE: { // Sort strings to ensure consistent output QStringList attrNames; NamedNodeMap attrs = node.attributes(); for (uint a = 0; a < attrs.length(); a++) attrNames.append(attrs.item(a).nodeName().string()); attrNames.sort(); QStringList::iterator it; Element elem(node); for (it = attrNames.begin(); it != attrNames.end(); ++it) { QString name = *it; QString value = elem.getAttribute(*it).string(); outputStream << " " << name << "=\"" << value << "\""; } if ( node.handle()->id() == ID_FRAME ) { outputStream << endl; QString frameName = static_cast<DOM::HTMLFrameElementImpl *>( node.handle() )->name.string(); KHTMLPart* frame = part->findFrame( frameName ); Q_ASSERT( frame ); if ( frame ) getPartDOMOutput( outputStream, frame, indent ); } break; } case Node::ATTRIBUTE_NODE: // Should not be present in tree assert(false); break; case Node::TEXT_NODE: outputStream << " \"" << Text(node).data().string() << "\""; break; case Node::CDATA_SECTION_NODE: outputStream << " \"" << CDATASection(node).data().string() << "\""; break; case Node::ENTITY_REFERENCE_NODE: break; case Node::ENTITY_NODE: break; case Node::PROCESSING_INSTRUCTION_NODE: break; case Node::COMMENT_NODE: outputStream << " \"" << Comment(node).data().string() << "\""; break; case Node::DOCUMENT_NODE: break; case Node::DOCUMENT_TYPE_NODE: break; case Node::DOCUMENT_FRAGMENT_NODE: // Should not be present in tree assert(false); break; case Node::NOTATION_NODE: break; default: assert(false); break; } outputStream << endl; if (!node.firstChild().isNull()) { node = node.firstChild(); indent++; } else if (!node.nextSibling().isNull()) { node = node.nextSibling(); } else { while (!node.isNull() && node.nextSibling().isNull()) { node = node.parentNode(); indent--; } if (!node.isNull()) node = node.nextSibling(); } }}void RegressionTest::dumpRenderTree( QTextStream &outputStream, KHTMLPart* part ){ DOM::DocumentImpl* doc = static_cast<DocumentImpl*>( part->document().handle() ); if ( !doc || !doc->renderer() ) return; doc->renderer()->layer()->dump( outputStream ); // Dump frames if any // Get list of names instead of frames() to sort the list alphabetically QStringList names = part->frameNames(); names.sort(); for ( QStringList::iterator it = names.begin(); it != names.end(); ++it ) { outputStream << "FRAME: " << (*it) << "\n"; KHTMLPart* frame = part->findFrame( (*it) ); Q_ASSERT( frame ); if ( frame ) dumpRenderTree( outputStream, frame ); }}QString RegressionTest::getPartOutput( OutputType type){ // dump out the contents of the rendering & DOM trees QString dump; QTextStream outputStream(dump,IO_WriteOnly); if ( type == RenderTree ) { dumpRenderTree( outputStream, m_part ); } else { assert( type == DOMTree ); getPartDOMOutput( outputStream, m_part, 0 ); } dump.replace( m_baseDir + "/tests", QString::fromLatin1( "REGRESSION_SRCDIR" ) ); return dump;}QImage RegressionTest::renderToImage(){ int ew = m_part->view()->contentsWidth(); int eh = m_part->view()->contentsHeight(); if (ew * eh > 4000 * 4000) // don't DoS us return QImage(); QImage img( ew, eh, 32 ); img.fill( 0xff0000 ); if (!m_paintBuffer ) m_paintBuffer = new QPixmap( 512, 128, -1, QPixmap::MemoryOptim ); for ( int py = 0; py < eh; py += 128 ) { for ( int px = 0; px < ew; px += 512 ) { QPainter* tp = new QPainter; tp->begin( m_paintBuffer ); tp->translate( -px, -py ); tp->fillRect(px, py, 512, 128, Qt::magenta); m_part->document().handle()->renderer()->layer()->paint( tp, QRect( px, py, 512, 128 ) ); tp->end(); delete tp; // now fill the chunk into our image QImage chunk = m_paintBuffer->convertToImage(); assert( chunk.depth() == 32 ); for ( int y = 0; y < 128 && py + y < eh; ++y ) memcpy( img.scanLine( py+y ) + px*4, chunk.scanLine( y ), kMin( 512, ew-px )*4 ); } } assert( img.depth() == 32 ); return img;}bool RegressionTest::imageEqual( const QImage &lhsi, const QImage &rhsi ){ if ( lhsi.width() != rhsi.width() || lhsi.height() != rhsi.height() ) { kdDebug() << "dimensions different " << lhsi.size() << " " << rhsi.size() << endl; return false; } int w = lhsi.width(); int h = lhsi.height(); int bytes = lhsi.bytesPerLine(); for ( int y = 0; y < h; ++y ) { QRgb* ls = ( QRgb* ) lhsi.scanLine( y ); QRgb* rs = ( QRgb* ) rhsi.scanLine( y ); if ( memcmp( ls, rs, bytes ) ) { for ( int x = 0; x < w; ++x ) { QRgb l = ls[x]; QRgb r = rs[x]; if ( ( abs( qRed( l ) - qRed(r ) ) < 20 ) && ( abs( qGreen( l ) - qGreen(r ) ) < 20 ) && ( abs( qBlue( l ) - qBlue(r ) ) < 20 ) ) continue; kdDebug() << "pixel (" << x << ", " << y << ") is different " << QColor( lhsi.pixel ( x, y ) ) << " " << QColor( rhsi.pixel ( x, y ) ) << endl; return false; } } } return true;}void RegressionTest::createLink( const QString& test, int failures ){ createMissingDirs( m_outputDir + "/" + test + "-compare.html" ); QFile list( m_outputDir + "/links.html" ); list.open( IO_WriteOnly|IO_Append ); QString link; link = QString( "<a href=\"%1\" target=\"content\" title=\"%2\">" ) .arg( test + "-compare.html" ) .arg( test ); link += m_currentTest; link += "</a> ["; if ( failures & DomFailure ) link += "D"; if ( failures & RenderFailure ) link += "R"; if ( failures & PaintFailure ) link += "P"; link += "]<br>\n"; list.writeBlock( link.latin1(), link.length() ); list.close();}void RegressionTest::doJavascriptReport( const QString &test ){ QFile compare( m_outputDir + "/" + test + "-compare.html" ); if ( !compare.open( IO_WriteOnly|IO_Truncate ) ) kdDebug() << "failed to open " << m_outputDir + "/" + test + "-compare.html" << endl; QString cl; cl = QString( "<html><head><title>%1</title>" ).arg( test ); cl += "<body><tt>"; QString text = "\n" + m_currentOutput; text.replace( '<', "<" ); text.replace( '>', ">" ); text.replace( QRegExp( "\nFAILED" ), "\n<span style='color: red'>FAILED</span>" ); text.replace( QRegExp( "\nFAIL" ), "\n<span style='color: red'>FAIL</span>" ); text.replace( QRegExp( "\nPASSED" ), "\n<span style='color: green'>PASSED</span>" ); text.replace( QRegExp( "\nPASS" ), "\n<span style='color: green'>PASS</span>" ); if ( text.at( 0 ) == '\n' ) text = text.mid( 1, text.length() ); text.replace( '\n', "<br>\n" ); cl += text; cl += "</tt></body></html>"; compare.writeBlock( cl.latin1(), cl.length() ); compare.close();}/** returns the path in a way that is relatively reachable from base. * @param base base directory (must not include trailing slash) * @param path directory/file to be relatively reached by base * @return path with all elements replaced by .. and concerning path elements * to be relatively reachable from base. */static QString makeRelativePath(const QString &base, const QString &path){ QString absBase = QFileInfo(base).absFilePath(); QString absPath = QFileInfo(path).absFilePath();// kdDebug() << "absPath: \"" << absPath << "\"" << endl;// kdDebug() << "absBase: \"" << absBase << "\"" << endl; // walk up to common ancestor directory int pos = 0; do { pos++; int newpos = absBase.find('/', pos); if (newpos == -1) newpos = absBase.length(); QConstString cmpPathComp(absPath.unicode() + pos, newpos - pos); QConstString cmpBaseComp(absBase.unicode() + pos, newpos - pos);// kdDebug() << "cmpPathComp: \"" << cmpPathComp.string() << "\"" << endl;// kdDebug() << "cmpBaseComp: \"" << cmpBaseComp.string() << "\"" << endl;// kdDebug() << "pos: " << pos << " newpos: " << newpos << endl; if (cmpPathComp.string() != cmpBaseComp.string()) { pos--; break; } pos = newpos; } while (pos < (int)absBase.length() && pos < (int)absPath.length()); int basepos = pos < (int)absBase.length() ? pos + 1 : pos; int pathpos = pos < (int)absPath.length() ? pos + 1 : pos;// kdDebug() << "basepos " << basepos << " pathpos " << pathpos << endl; QString rel; { QConstString relBase(absBase.unicode() + basepos, absBase.length() - basepos); QConstString relPath(absPath.unicode() + pathpos, absPath.length() - pathpos); // generate as many .. as there are path elements in relBase if (relBase.string().length() > 0) { for (int i = relBase.string().contains('/'); i > 0; --i) rel += "../"; rel += ".."; if (relPath.string().length() > 0) rel += "/"; } rel += relPath.string(); } return rel;}void RegressionTest::doFailureReport( const QString& test, int failures ){ if ( failures == NoFailure ) { ::unlink( QFile::encodeName( m_outputDir + "/" + test + "-compare.html" ) ); return; } createLink( test, failures ); if ( failures & JSFailure ) { doJavascriptReport( test ); return; // no support for both kind } QFile compare( m_outputDir + "/" + test + "-compare.html" ); QString testFile = QFileInfo(test).fileName(); QString renderDiff; QString domDiff; QString relOutputDir = makeRelativePath(m_baseDir, m_outputDir); // are blocking reads possible with KProcess? char pwd[PATH_MAX]; (void) getcwd( pwd, PATH_MAX ); chdir( QFile::encodeName( m_baseDir ) ); if ( failures & RenderFailure ) { renderDiff += "<pre>"; FILE *pipe = popen( QString::fromLatin1( "diff -u baseline/%1-render %3/%2-render" ) .arg ( test, test, relOutputDir ).latin1(), "r" ); QTextIStream *is = new QTextIStream( pipe ); for ( int line = 0; line < 100 && !is->eof(); ++line ) { QString line = is->readLine(); line = line.replace( '<', "<" ); line = line.replace( '>', ">" ); renderDiff += line + "\n"; } delete is; pclose( pipe ); renderDiff += "</pre>"; } if ( failures & DomFailure ) { domDiff += "<pre>"; FILE *pipe = popen( QString::fromLatin1( "diff -u baseline/%1-dom %3/%2-dom" ) .arg ( test, test, relOutputDir ).latin1(), "r" ); QTextIStream *is = new QTextIStream( pipe ); for ( int line = 0; line < 100 && !is->eof(); ++line ) { QString line = is->readLine(); line = line.replace( '<', "<" ); line = line.replace( '>', ">" ); domDiff += line + "\n"; } delete is; pclose( pipe ); domDiff += "</pre>"; } chdir( pwd ); // create a relative path so that it works via web as well. ugly QString relpath = makeRelativePath(m_outputDir + "/" + QFileInfo(test).dirPath(), m_baseDir); compare.open( IO_WriteOnly|IO_Truncate ); QString cl; cl = QString( "<html><head><title>%1</title>" ).arg( test ); cl += QString( "<script>\n" "var pics = new Array();\n" "pics[0]=new Image();\n" "pics[0].src = '%1';\n" "pics[1]=new Image();\n" "pics[1].src = '%2';\n" "var doflicker = 1;\n" "var t = 1;\n" "var lastb=0;\n" ) .arg( relpath+"/baseline/"+test+"-dump.png" ) .arg( testFile+"-dump.png" ); cl += QString( "function toggleVisible(visible) {\n" " document.getElementById('render').style.visibility= visible == 'render' ? 'visible' : 'hidden';\n" " document.getElementById('image').style.visibility= visible == 'image' ? 'visible' : 'hidden';\n" " document.getElementById('dom').style.visibility= visible == 'dom' ? 'visible' : 'hidden';\n" "}\n" "function show() { document.getElementById('image').src = pics[t].src; " "document.getElementById('image').style.borderColor = t && !doflicker ? 'red' : 'gray';\n" "toggleVisible('image');\n" "}" ); cl += QString ( "function runSlideShow(){\n" " document.getElementById('image').src = pics[t].src;\n" " if (doflicker)\n" " t = 1 - t;\n" " setTimeout('runSlideShow()', 200);\n" "}\n" "function m(b) { if (b == lastb) return; document.getElementById('b'+b).className='buttondown';\n" " var e = document.getElementById('b'+lastb);\n" " if(e) e.className='button';\n" " lastb = b;\n" "}\n" "function showRender() { doflicker=0;toggleVisible('render')\n" "}\n" "function showDom() { doflicker=0;toggleVisible('dom')\n" "}\n" "</script>\n"); cl += QString ("<style>\n" ".buttondown { cursor: pointer; padding: 0px 20px; color: white; background-color: blue; border: inset blue 2px;}\n" ".button { cursor: pointer; padding: 0px 20px; color: black; background-color: white; border: outset blue 2px;}\n" ".diff { position: absolute; left: 10px; top: 100px; visibility: hidden; border: 1px black solid; background-color: white; color: black; /* width: 800; height: 600; overflow: scroll; */ }\n" "</style>\n" ); if ( failures & PaintFailure ) cl += QString( "<body onload=\"m(1); show(); runSlideShow();\"" ); else if ( failures & RenderFailure ) cl += QString( "<body onload=\"m(4); toggleVisible('render');\"" ); else cl += QString( "<body onload=\"m(5); toggleVisible('dom');\"" ); cl += QString(" text=black bgcolor=gray>\n<h1>%3</h1>\n" ).arg( test ); if ( failures & PaintFailure ) cl += QString ( "<span id='b1' class='buttondown' onclick=\"doflicker=1;show();m(1)\">FLICKER</span> \n" "<span id='b2' class='button' onclick=\"doflicker=0;t=0;show();m(2)\">BASE</span> \n" "<span id='b3' class='button' onclick=\"doflicker=0;t=1;show();m(3)\">OUT</span> \n" ); if ( renderDiff.length() ) cl += "<span id='b4' class='button' onclick='showRender();m(4)'>R-DIFF</span> \n"; if ( domDiff.length() )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -