diff --git a/test/alignmenttest.cpp b/test/alignmenttest.cpp index 5a9c2fd..1a6ceb1 100644 --- a/test/alignmenttest.cpp +++ b/test/alignmenttest.cpp @@ -1,491 +1,495 @@ -// vim:sw=3:ts=3:expandtab +/* + SPDX-FileCopyrightText: 2002-2007 Joachim Eibl, joachim.eibl at gmx.de + SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com + SPDX-License-Identifier: GPL-2.0-or-later +*/ #include #include #include #include #include #include "diff.h" #include "gnudiff_diff.h" #include "options.h" #include "progress.h" #define i18n(s) s bool verbose = false; QSharedPointer m_pOptions; ManualDiffHelpList m_manualDiffHelpList; void printDiffList(const QString caption, const DiffList &diffList) { QTextStream out(stdout); DiffList::const_iterator i; out << "Printing difflist " << caption << ":" << endl; out << " nofEquals, diff1, diff2" << endl; for(i = diffList.begin(); i != diffList.end(); i++) { out << " " << i->numberOfEquals() << "," << i->diff1() << "," << i->diff2() << endl; } } void printDiff3List(const Diff3LineList &diff3LineList, const SourceData &sd1, const SourceData &sd2, const SourceData &sd3, bool forceVerbosity=false) { const int columnsize = 30; const int linenumsize = 6; Diff3LineList::const_iterator i; for ( i=diff3LineList.begin(); i!=diff3LineList.end(); ++i ) { QTextStream out(stdout); QString lineAText; QString lineBText; QString lineCText; const Diff3Line& d3l = *i; if(d3l.getLineA().isValid()) { const LineData *pLineData = &sd1.getLineDataForDiff()->at(d3l.getLineA()); lineAText = pLineData->getLine(); lineAText.replace(QString("\r"), QString("\\r")); lineAText.replace(QString("\n"), QString("\\n")); lineAText = QString("%1 %2").arg(d3l.getLineA(), linenumsize).arg(lineAText.left(columnsize - linenumsize - 1)); } if(d3l.getLineB().isValid()) { const LineData *pLineData = &sd2.getLineDataForDiff()->at(d3l.getLineB()); lineBText = pLineData->getLine(); lineBText.replace(QString("\r"), QString("\\r")); lineBText.replace(QString("\n"), QString("\\n")); lineBText = QString("%1 %2").arg(d3l.getLineB(), linenumsize).arg(lineBText.left(columnsize - linenumsize - 1)); } if(d3l.getLineC().isValid()) { const LineData *pLineData = &sd3.getLineDataForDiff()->at(d3l.getLineC()); lineCText = pLineData->getLine(); lineCText.replace(QString("\r"), QString("\\r")); lineCText.replace(QString("\n"), QString("\\n")); lineCText = QString("%1 %2").arg(d3l.getLineC(), linenumsize).arg(lineCText.left(columnsize - linenumsize - 1)); } out << QString("%1 %2 %3").arg(lineAText, -columnsize) .arg(lineBText, -columnsize) .arg(lineCText, -columnsize); if(verbose || forceVerbosity) { out << " " << d3l.isEqualAB() << " " << d3l.isEqualBC() << " " << d3l.isEqualAC(); } out << endl; } } void printDiff3List(QString caption, const Diff3LineList &diff3LineList, const SourceData &sd1, const SourceData &sd2, const SourceData &sd3, bool forceVerbosity=false) { QTextStream out(stdout); out << "Printing diff3list " << caption << ":" << endl; printDiff3List(diff3LineList, sd1, sd2, sd3, forceVerbosity); } void determineFileAlignment(SourceData &m_sd1, SourceData &m_sd2, SourceData &m_sd3, Diff3LineList &m_diff3LineList) { DiffList m_diffList12; DiffList m_diffList23; DiffList m_diffList13; m_diff3LineList.clear(); // Run the diff. if ( m_sd3.isEmpty() ) { m_manualDiffHelpList.runDiff( m_sd1.getLineDataForDiff(), m_sd1.getSizeLines(), m_sd2.getLineDataForDiff(), m_sd2.getSizeLines(), m_diffList12,A,B, m_pOptions); m_diff3LineList.calcDiff3LineListUsingAB( &m_diffList12); m_diff3LineList.fineDiff(A, m_sd1.getLineDataForDisplay(), m_sd2.getLineDataForDisplay() ); } else { m_manualDiffHelpList.runDiff( m_sd1.getLineDataForDiff(), m_sd1.getSizeLines(), m_sd2.getLineDataForDiff(), m_sd2.getSizeLines(), m_diffList12,A,B, m_pOptions); m_manualDiffHelpList.runDiff( m_sd2.getLineDataForDiff(), m_sd2.getSizeLines(), m_sd3.getLineDataForDiff(), m_sd3.getSizeLines(), m_diffList23,B,C, m_pOptions); m_manualDiffHelpList.runDiff( m_sd1.getLineDataForDiff(), m_sd1.getSizeLines(), m_sd3.getLineDataForDiff(), m_sd3.getSizeLines(), m_diffList13,A,C, m_pOptions); if (verbose) { printDiffList("m_diffList12", m_diffList12); printDiffList("m_diffList23", m_diffList23); printDiffList("m_diffList13", m_diffList13); } m_diff3LineList.calcDiff3LineListUsingAB( &m_diffList12); if (verbose) printDiff3List("after calcDiff3LineListUsingAB", m_diff3LineList, m_sd1, m_sd2, m_sd3); m_diff3LineList.calcDiff3LineListUsingAC( &m_diffList13); if (verbose) printDiff3List("after calcDiff3LineListUsingAC", m_diff3LineList, m_sd1, m_sd2, m_sd3); m_diff3LineList.correctManualDiffAlignment(&m_manualDiffHelpList ); m_diff3LineList.calcDiff3LineListTrim(m_sd1.getLineDataForDiff(), m_sd2.getLineDataForDiff(), m_sd3.getLineDataForDiff(), &m_manualDiffHelpList ); if (verbose) printDiff3List("after 1st calcDiff3LineListTrim", m_diff3LineList, m_sd1, m_sd2, m_sd3); if ( m_pOptions->m_bDiff3AlignBC ) { m_diff3LineList.calcDiff3LineListUsingBC( &m_diffList23); if (verbose) printDiff3List("after calcDiff3LineListUsingBC", m_diff3LineList, m_sd1, m_sd2, m_sd3); m_diff3LineList.correctManualDiffAlignment( &m_manualDiffHelpList ); m_diff3LineList.calcDiff3LineListTrim(m_sd1.getLineDataForDiff(), m_sd2.getLineDataForDiff(), m_sd3.getLineDataForDiff(), &m_manualDiffHelpList ); if (verbose) printDiff3List("after 2nd calcDiff3LineListTrim", m_diff3LineList, m_sd1, m_sd2, m_sd3); } m_diff3LineList.fineDiff(A, m_sd1.getLineDataForDisplay(), m_sd2.getLineDataForDisplay() ); m_diff3LineList.fineDiff(B, m_sd2.getLineDataForDisplay(), m_sd3.getLineDataForDisplay() ); m_diff3LineList.fineDiff(C, m_sd3.getLineDataForDisplay(), m_sd1.getLineDataForDisplay() ); } m_diff3LineList.calcWhiteDiff3Lines( m_sd1.getLineDataForDiff(), m_sd2.getLineDataForDiff(), m_sd3.getLineDataForDiff() ); } QString getLineFromSourceData(const SourceData &sd, int line) { const LineData *pLineData = &sd.getLineDataForDiff()->at(line); QString lineText = pLineData->getLine(); lineText.replace(QString("\r"), QString("\\r")); lineText.replace(QString("\n"), QString("\\n")); return lineText; } void loadExpectedAlignmentFile(QString expectedResultFileName, Diff3LineList &expectedDiff3LineList) { Diff3Line d3l; expectedDiff3LineList.clear(); QFile file(expectedResultFileName); QString line; if ( file.open(QIODevice::ReadOnly) ) { QTextStream t( &file ); while ( !t.atEnd() ) { QStringList lst = t.readLine().split(QRegExp("\\s+")); d3l.setLineA(lst.at(0).toInt()); d3l.setLineB(lst.at(1).toInt()); d3l.setLineC(lst.at(2).toInt()); expectedDiff3LineList.push_back( d3l ); } file.close(); } } void writeActualAlignmentFile(QString actualResultFileName, const Diff3LineList &actualDiff3LineList) { Diff3LineList::const_iterator p_d3l; QFile file(actualResultFileName); if ( file.open(QIODevice::WriteOnly) ) { { QTextStream t( &file ); for(p_d3l = actualDiff3LineList.begin(); p_d3l != actualDiff3LineList.end(); p_d3l++) { t << p_d3l->getLineA() << " " << p_d3l->getLineB() << " " << p_d3l->getLineC() << endl; } } file.close(); } } bool dataIsConsistent(int line1, QString &line1Text, int line2, QString &line2Text, bool equal) { bool consistent = false; if(line1 == -1 || line2 == -1) { consistent = !equal; } else { /* If the equal boolean is true the line content must be the same, * if the line content is different the boolean should be false, * but other than that we can't be sure: * - if the line content is the same the boolean may not be true because * GNU diff may have put that line as a removal in the first file and * an addition in the second. * - also the comparison this test does between lines considers all * whitespace equal, while GNU diff doesn't (for instance U+0020 vs U+00A0) */ if(equal) { consistent = (line1Text == line2Text); } else if (line1Text != line2Text) { consistent = !equal; } else { consistent = true; } } return consistent; } bool runTest(QString file1, QString file2, QString file3, QString expectedResultFile, QString actualResultFile, int maxLength) { m_pOptions = QSharedPointer::create(); Diff3LineList actualDiff3LineList, expectedDiff3LineList; QTextCodec *p_codec = QTextCodec::codecForName("UTF-8"); QTextStream out(stdout); m_pOptions->m_bIgnoreCase = false; m_pOptions->m_bPreserveCarriageReturn = false; m_pOptions->m_bDiff3AlignBC = true; SourceData m_sd1, m_sd2, m_sd3; QString msgprefix = "Running test with "; QString filepattern = QString(file1).replace("_base.", "_*."); QString msgsuffix = QString("...%1").arg("", maxLength - filepattern.length()); out << msgprefix << filepattern << msgsuffix; if(verbose) { out << endl; } out.flush(); m_sd1.setOptions(m_pOptions); m_sd1.setFilename(file1); m_sd1.readAndPreprocess(p_codec, false); m_sd2.setOptions(m_pOptions); m_sd2.setFilename(file2); m_sd2.readAndPreprocess(p_codec, false); m_sd3.setOptions(m_pOptions); m_sd3.setFilename(file3); m_sd3.readAndPreprocess(p_codec, false); determineFileAlignment(m_sd1, m_sd2, m_sd3, actualDiff3LineList); loadExpectedAlignmentFile(expectedResultFile, expectedDiff3LineList); Diff3LineList::iterator p_actual = actualDiff3LineList.begin(); Diff3LineList::iterator p_expected = expectedDiff3LineList.begin(); bool equal = true; bool sequenceError = false; bool consistencyError = false; equal = (actualDiff3LineList.size() == expectedDiff3LineList.size()); int latestLineA = -1; int latestLineB = -1; int latestLineC = -1; while(equal && (p_actual != actualDiff3LineList.end())) { /* Check if all line numbers are in sequence */ if(p_actual->getLineA().isValid()) { if(p_actual->getLineA() <= latestLineA) { sequenceError = true; } else { latestLineA = p_actual->getLineA(); } } if(p_actual->getLineB().isValid()) { if(p_actual->getLineB() <= latestLineB) { sequenceError = true; } else { latestLineB = p_actual->getLineB(); } } if(p_actual->getLineC().isValid()) { if(p_actual->getLineC() <= latestLineC) { sequenceError = true; } else { latestLineC = p_actual->getLineC(); } } /* Check if the booleans that indicate if lines are equal are consistent with the content of the lines */ QString lineAText = (!p_actual->getLineA().isValid()) ? "" : getLineFromSourceData(m_sd1, p_actual->getLineA()).simplified().replace(" ", ""); QString lineBText = (!p_actual->getLineB().isValid()) ? "" : getLineFromSourceData(m_sd2, p_actual->getLineB()).simplified().replace(" ", ""); QString lineCText = (!p_actual->getLineC().isValid()) ? "" : getLineFromSourceData(m_sd3, p_actual->getLineC()).simplified().replace(" ", ""); if(!dataIsConsistent(p_actual->getLineA(), lineAText, p_actual->getLineB(), lineBText, p_actual->isEqualAB())) { if(verbose) out << "inconsistency: line " << p_actual->getLineA() << " of A vs line " << p_actual->getLineB() << " of B" << endl; consistencyError = true; } if(!dataIsConsistent(p_actual->getLineB(), lineBText, p_actual->getLineC(), lineCText, p_actual->isEqualBC())) { if(verbose) out << "inconsistency: line " << p_actual->getLineB() << " of B vs line " << p_actual->getLineC() << " of C" << endl; consistencyError = true; } if(!dataIsConsistent(p_actual->getLineA(), lineAText, p_actual->getLineC(), lineCText, p_actual->isEqualAC())) { if(verbose) out << "inconsistency: line " << p_actual->getLineA() << " of A vs line " << p_actual->getLineC() << " of C" << endl; consistencyError = true; } /* Check if the actual output of the algorithm is equal to the expected output */ equal = (p_actual->getLineA() == p_expected->getLineA()) && (p_actual->getLineB() == p_expected->getLineB()) && (p_actual->getLineC() == p_expected->getLineC()); p_actual++; p_expected++; } if(sequenceError) { out << "NOK" << endl; out << "Actual result has incorrectly sequenced line numbers:" << endl; out << "----------------------------------------------------------------------------------------------" << endl; printDiff3List(actualDiff3LineList, m_sd1, m_sd2, m_sd3); } else if(consistencyError) { out << "NOK" << endl; out << "Actual result has inconsistent equality booleans:" << endl; out << "----------------------------------------------------------------------------------------------" << endl; printDiff3List(actualDiff3LineList, m_sd1, m_sd2, m_sd3, true); } else if(equal) { out << "OK" << endl; } else { out << "NOK" << endl; writeActualAlignmentFile(actualResultFile, actualDiff3LineList); out << "Actual result (written to " << actualResultFile << "):" << endl; out << "----------------------------------------------------------------------------------------------" << endl; printDiff3List(actualDiff3LineList, m_sd1, m_sd2, m_sd3); out << "----------------------------------------------------------------------------------------------" << endl; out << "Expected result:" << endl; out << "----------------------------------------------------------------------------------------------" << endl; printDiff3List(expectedDiff3LineList, m_sd1, m_sd2, m_sd3); out << "----------------------------------------------------------------------------------------------" << endl; } return equal; } QStringList gettestdatafiles(QString testdir) { QStringList baseFilePaths; QTextStream out(stdout); QStringList nameFilter; nameFilter << "*_base.*"; QDir testdatadir(testdir); QStringList baseFileNames = testdatadir.entryList(nameFilter, QDir::Files, QDir::Name); QListIterator file_it(baseFileNames); while(file_it.hasNext()) { baseFilePaths.append(testdir + "/" + file_it.next()); } out << testdir << ": " << baseFilePaths.size() << " files" << endl; QStringList subdirs = testdatadir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); QListIterator dir_it(subdirs); while (dir_it.hasNext()) { QString subdir = dir_it.next(); QStringList subdirBaseFilePaths = gettestdatafiles(testdir + "/" + subdir); baseFilePaths.append(subdirBaseFilePaths); } return baseFilePaths; } int main(int argc, char *argv[]) { bool allOk = true; int maxLength = 0; QTextStream out(stdout); QDir testdatadir("testdata"); /* Print data at various steps in the algorithm to get an idea where to look for the root cause of a failing test */ if((argc == 2) && (!strcmp(argv[1], "-v"))) { verbose = true; } QStringList baseFiles = gettestdatafiles("testdata"); QListIterator it(baseFiles); for (int i = 0; i < baseFiles.size(); i++) { maxLength = std::max(baseFiles.at(i).length(), maxLength); } maxLength += testdatadir.path().length() + 1; while (it.hasNext()) { QString fileName = it.next(); QRegExp baseFileRegExp("(.*)_base\\.(.*)"); baseFileRegExp.exactMatch(fileName); QString prefix = baseFileRegExp.cap(1); QString suffix = baseFileRegExp.cap(2); QString contrib1FileName(prefix + "_contrib1." + suffix); QString contrib2FileName(prefix + "_contrib2." + suffix); QString expectedResultFileName(prefix + "_expected_result." + suffix); QString actualResultFileName(prefix + "_actual_result." + suffix); if(QFile(contrib1FileName).exists() && QFile(contrib2FileName).exists() && QFile(expectedResultFileName).exists()) { bool ok = runTest(fileName, contrib1FileName, contrib2FileName, expectedResultFileName, actualResultFileName, maxLength); allOk = allOk && ok; } else { out << "Skipping " << fileName << " " << contrib1FileName << " " << contrib2FileName << " " << expectedResultFileName << " " << endl; } } out << (allOk ? "All OK" : "Not all OK") << endl; return allOk ? 0 : -1; } diff --git a/test/fakefileaccess.cpp b/test/fakefileaccess.cpp index 189a050..8b838fb 100644 --- a/test/fakefileaccess.cpp +++ b/test/fakefileaccess.cpp @@ -1,104 +1,110 @@ +/* + SPDX-FileCopyrightText: 2002-2007 Joachim Eibl, joachim.eibl at gmx.de + SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com + SPDX-License-Identifier: GPL-2.0-or-later +*/ + #include "fileaccess.h" #include FileAccess::FileAccess() { } FileAccess::FileAccess(const QString& name, bool bWantToWrite) { Q_ASSERT(!bWantToWrite); m_name = name; } FileAccess::~FileAccess() { } // FileAccess( const QString& name, bool bWantToWrite=false ); // name: local file or dirname or url (when supported) // void setFile( const QString& name, bool bWantToWrite=false ); // bool FileAccess::isValid() const { return m_name.length() != 0; } // bool isFile() const; // bool isDir() const; // bool isSymLink() const; bool FileAccess::exists() const { return true; } qint64 FileAccess::size() const { return 64; } qint64 FileAccess::sizeForReading() { return 64; } // bool isReadable() const; // bool isWritable() const; // bool isExecutable() const; // bool isHidden() const; // QString readLink() const; // // QDateTime created() const; // QDateTime lastModified() const; // QDateTime lastRead() const; // // QString fileName() const; // Just the name-part of the path, without parent directories // QString filePath() const; // The path-string that was used during construction QString FileAccess::prettyAbsPath() const { return QString(""); } // KUrl url() const; QString FileAccess::absoluteFilePath() const { return ""; } bool FileAccess::isLocal() const { return true; } bool FileAccess::readFile(void* pDestBuffer, qint64 maxLength ) { Q_UNUSED(pDestBuffer) Q_UNUSED(maxLength); return true; } bool FileAccess::writeFile(const void* pSrcBuffer, qint64 length ) { Q_UNUSED(pSrcBuffer); Q_UNUSED(length); return true; } // bool listDir( t_DirectoryList* pDirList, bool bRecursive, bool bFindHidden, // const QString& filePattern, const QString& fileAntiPattern, // const QString& dirAntiPattern, bool bFollowDirLinks, bool bUseCvsIgnore ); bool FileAccess::copyFile( const QString& destUrl ) { Q_UNUSED(destUrl); return true; } // bool createBackup( const QString& bakExtension ); // QString FileAccess::getTempName() const { return QString(""); } bool FileAccess::removeFile() { return true; } diff --git a/test/fakekdiff3_part.cpp b/test/fakekdiff3_part.cpp index 2c47b5f..6f59fc9 100644 --- a/test/fakekdiff3_part.cpp +++ b/test/fakekdiff3_part.cpp @@ -1,7 +1,13 @@ +/* + SPDX-FileCopyrightText: 2002-2007 Joachim Eibl, joachim.eibl at gmx.de + SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com + SPDX-License-Identifier: GPL-2.0-or-later +*/ + extern "C" { void* init_libkdiff3part() { return 0; } } diff --git a/test/fakeprogressproxy.cpp b/test/fakeprogressproxy.cpp index dd03044..b340435 100644 --- a/test/fakeprogressproxy.cpp +++ b/test/fakeprogressproxy.cpp @@ -1,90 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + #include #include "progress.h" void ProgressDialog::delayedHide() { } void ProgressDialog::slotAbort() { } void ProgressDialog::reject() { } void ProgressDialog::timerEvent(QTimerEvent*) { } ProgressProxy::ProgressProxy() { } ProgressProxy::~ProgressProxy() { } void ProgressProxy::setInformation( const QString& info, bool bRedrawUpdate ) { /* Suppress warning about unused parameters */ Q_UNUSED(info); Q_UNUSED(bRedrawUpdate); } void ProgressProxy::setInformation( const QString& info, int current, bool bRedrawUpdate ) { /* Suppress warning about unused parameters */ Q_UNUSED(info); Q_UNUSED(current); Q_UNUSED(bRedrawUpdate); } void ProgressProxy::setCurrent( qint64 current, bool bRedrawUpdate ) { /* Suppress warning about unused parameters */ Q_UNUSED(current); Q_UNUSED(bRedrawUpdate); } void ProgressProxy::step( bool bRedrawUpdate ) { /* Suppress warning about unused parameters */ Q_UNUSED(bRedrawUpdate); } void ProgressProxy::setMaxNofSteps( qint64 dMaxNofSteps ) { /* Suppress warning about unused parameters */ Q_UNUSED(dMaxNofSteps); } bool ProgressProxy::wasCancelled() { return false; } void ProgressProxy::enterEventLoop( KJob* pJob, const QString& jobInfo ) { /* Suppress warning about unused parameters */ Q_UNUSED(pJob); Q_UNUSED(jobInfo); } void ProgressProxy::exitEventLoop() { } void ProgressDialog::recalc(bool bUpdate) { /* Suppress warning about unused parameters */ Q_UNUSED(bUpdate); } QDialog *ProgressProxy::getDialog() { return NULL; } diff --git a/test/generate_testdata_from_git_merges.py b/test/generate_testdata_from_git_merges.py index 2ddb59e..33b2f35 100755 --- a/test/generate_testdata_from_git_merges.py +++ b/test/generate_testdata_from_git_merges.py @@ -1,89 +1,91 @@ #!/usr/bin/env python +# SPDX-FileCopyrightText: 2002-2007 Joachim Eibl, joachim.eibl at gmx.de +# SPDX-License-Identifier: GPL-2.0-or-later + import argparse import glob import os import subprocess as sp import sys parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description='Generate input files for alignmenttest from the files merged for each merge commit in a git repository.\n\n' + 'This script finds all merge commits in the clone where it is run, checks which files were modified in both\n' + 'parents of the merge commit and then finds the common ancestor of these files to get the merge base.\n\n' 'Example:\n' ' cd ~/git/linux\n' ' ~/kdiff3/test/%s -d ~/kdiff3/test/testdata/linux\n' % os.path.basename(sys.argv[0])) parser.add_argument('-d', metavar='destination_path', nargs=1, default=['testdata_from_git/'], help='specify the folder where to save the test input files. If the folder does not exist it will be created.') args = parser.parse_args() dirname=args.d[0] print 'Generating input files in %s ...' % dirname sys.stdout.flush() if not os.path.exists(dirname): os.makedirs(dirname) merges = sp.check_output('git rev-list --merges --parents master'.split()).strip() for entry in merges.splitlines(): fields = entry.split() if len(fields) > 3: print 'merge %s had more than 2 parents: %s' % (fields[0], fields) merge, contrib1, contrib2 = fields[:3] if glob.glob('%s/%s_*' % (dirname, merge)): print 'skipping merge %s because files for this merge already present' % merge continue base = sp.check_output(('git merge-base %s %s' % (contrib1, contrib2)).split()).strip() fileschanged1 = sp.check_output(('git diff --name-only %s %s' % (base, contrib1)).split()).strip().splitlines() fileschanged2 = sp.check_output(('git diff --name-only %s %s' % (base, contrib2)).split()).strip().splitlines() fileschangedboth = set(fileschanged1) & set(fileschanged2) if not fileschangedboth: print 'No files overlapped for merge %s' % merge else: print 'Overlapping files for merge %s with base %s: %s' % (merge, base, fileschangedboth) for filename in fileschangedboth: simplified_filename = filename.replace('/', '_').replace('.', '_') try: base_content = sp.check_output(('git show %s:%s' % (base, filename)).split()) contrib1_content = sp.check_output(('git show %s:%s' % (contrib1, filename)).split()) contrib2_content = sp.check_output(('git show %s:%s' % (contrib2, filename)).split()) if base_content == contrib1_content or \ base_content == contrib2_content or \ contrib1_content == contrib2_content: print 'this merge was trivial. Skipping.' else: basefilename = '%s/%s_%s_base.txt' % (dirname, merge, simplified_filename) contrib1filename = '%s/%s_%s_contrib1.txt' % (dirname, merge, simplified_filename) contrib2filename = '%s/%s_%s_contrib2.txt' % (dirname, merge, simplified_filename) for filename, content in [(basefilename, base_content), (contrib1filename, contrib1_content), (contrib2filename, contrib2_content)]: with open(filename, 'wb') as f: f.write(content) with open('%s/%s_%s_expected_result.txt' % (dirname, merge, simplified_filename), 'a') as f: pass except sp.CalledProcessError: print 'error from git show, continuing with next file' print 'Input files generated.' print '' print 'To create a reference set of expected_result.txt files, run alignmenttest and copy/move all %s/*_actual_result.txt files to %s/*_expected_result.txt:' % (dirname, dirname) print ' ./alignmenttest > /dev/null' print ' cd %s' % dirname print ' for file in *_actual_result.txt; do mv ${file} ${file/actual/expected}; done' print 'If you\'ve already modified the algorithm, you can run the alignment test of an older version of kdiff3 and copy those result files over' - diff --git a/test/generate_testdata_from_permutations.py b/test/generate_testdata_from_permutations.py index d3bf602..b4bc285 100755 --- a/test/generate_testdata_from_permutations.py +++ b/test/generate_testdata_from_permutations.py @@ -1,104 +1,107 @@ #!/usr/bin/env python +# SPDX-FileCopyrightText: 2002-2007 Joachim Eibl, joachim.eibl at gmx.de +# SPDX-License-Identifier: GPL-2.0-or-later + import argparse import os import random import sys dirname = 'testdata/permutations' defaultlines = ['aaa\n', 'bbb\n', 'ccc\n', 'ddd\n', 'eee\n'] # For the lines of the A file only consider removing them because modifying # them ("diff") would be equivalent to modifying both B and C, so that will # be covered anyway. options = [ ('1','1','1'), ('1','1','2'), ('1','1',None), ('1','2','1'), ('1','2','2'), ('1','2','3'), ('1','2',None), (None,'1','1'), (None,'1','2'), (None,'1',None), (None,None,'1') ] def permutations(nr_of_options, count, currentlist): if count == 0: filename = ''.join([format(i, '1x') for i in currentlist]) baselines = [] contrib1lines = [] contrib2lines = [] for optionindex, defaultline in zip(currentlist, defaultlines): option = options[optionindex] if option[0]: baselines.append(defaultline) if option[1] == '1': contrib1lines.append(defaultline) elif option[1] == '2': contrib1lines.append('xxx' + defaultline) if option[2] == '1': contrib2lines.append(defaultline) elif option[2] == '2': contrib2lines.append('xxx' + defaultline) elif option[2] == '3': contrib2lines.append('yyy' + defaultline) with open('%s/perm_%s_base.txt' % (dirname, filename), 'wb') as f: f.writelines(baselines) with open('%s/perm_%s_contrib1.txt' % (dirname, filename), 'wb') as f: f.writelines(contrib1lines) with open('%s/perm_%s_contrib2.txt' % (dirname, filename), 'wb') as f: f.writelines(contrib2lines) with open('%s/perm_%s_expected_result.txt' % (dirname, filename), 'a') as f: pass else: optionindices = random.sample(range(len(options)), nr_of_options) for optionindex in optionindices: permutations(nr_of_options, count - 1, [optionindex] + currentlist) parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description='Generate input files for alignmenttest in ./testdata/permutations/ containing some or all permutations of 3 sets of 5 lines.\n\n' + 'Everything is based on a default set of 5 different lines: aaa, bbb, ccc, ddd and eee.\n' + 'For the base file each line will be either equal to the default set or removed.\n' + 'For contributor 1 each line will either be equal to the default set, different than the default set (\'xxx\' prepended) or removed.\n' + 'For contributor 2 each line will either be equal to the default set, equal to contributor 1, different (\'yyy\' prepended) or removed.\n' + 'This results in %d possible permutations. The -r option can be used to make a smaller \'random\' selection (the same seed is used each time).' % (len(options) ** len(defaultlines))) parser.add_argument('-r', metavar='num', nargs='?', type=int, default=len(options), const=len(options), help='instead of generating all %d permutations for each line, generate randomly chosen ones. The number of test cases will become num^5.' % len(options)) parser.add_argument('-s', metavar='num', nargs='?', type=int, default=0, const=0, help='specify the seed to use for the random number generator (default=0). This only makes sense when the -r option is specified.') args = parser.parse_args() if not os.path.exists(dirname): os.makedirs(dirname) print 'Generating input files in %s ...' % dirname sys.stdout.flush() random.seed(args.s) permutations(args.r, len(defaultlines), []) print 'Input files generated.' print '' print 'To create a reference set of expected_result.txt files, run alignmenttest and copy/move all %s/*_actual_result.txt files to %s/*_expected_result.txt:' % (dirname, dirname) print ' ./alignmenttest > /dev/null' print ' cd %s' % dirname print ' for file in *_actual_result.txt; do mv ${file} ${file/actual/expected}; done' print 'If you\'ve already modified the algorithm, you can run the alignment test of an older version of kdiff3 and copy those result files over'