From 6ccd05a927d30a8d5fb05fac50462305e6e7a8cf Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 23 Apr 2026 16:43:01 +0200 Subject: [PATCH 1/4] Modernize fitpanel UnitTesting.cxx Do not automatically terminate application Use gSystem->RedirectOutput --- gui/fitpanel/test/UnitTesting.cxx | 185 ++++++++++++++---------------- 1 file changed, 87 insertions(+), 98 deletions(-) diff --git a/gui/fitpanel/test/UnitTesting.cxx b/gui/fitpanel/test/UnitTesting.cxx index f4d47b69ec599..10cfd4a2e024c 100644 --- a/gui/fitpanel/test/UnitTesting.cxx +++ b/gui/fitpanel/test/UnitTesting.cxx @@ -2,12 +2,11 @@ #include "TApplication.h" #include "TROOT.h" -#include "TBenchmark.h" +#include "TSystem.h" #include "TCanvas.h" #include "TH1.h" -#include "TPluginManager.h" #include "TError.h" #include "TGComboBox.h" @@ -27,11 +26,8 @@ #include "../src/CommonDefs.h" -#ifdef WIN32 -#include "io.h" -#else -#include "unistd.h" -#endif + +TString gTmpfilename; // Function that compares to doubles up to an error limit int equals(Double_t n1, Double_t n2, double ERRORLIMIT = 1.E-4) @@ -102,10 +98,6 @@ class FitEditorUnitTesting // Pointer to the current (and only one) TFitEditor opened. TFitEditor* f; - // These two variables are here to redirect the standard output to - // a file. - int old_stdout; - FILE *out; public: // Exception thrown when any of the pointers managed by the @@ -121,21 +113,6 @@ class FitEditorUnitTesting // Constructor: Receives the instance of the TFitEditor FitEditorUnitTesting() { - // Redirect the stdout to a file outputUnitTesting.txt - #ifdef WIN32 - old_stdout = _dup (_fileno (stdout)); - #else - old_stdout = dup (fileno (stdout)); - #endif - auto res = freopen ("outputUnitTesting.txt", "w", stdout); - if (!res) { - throw InvalidPointer("In FitEditorUnitTesting constructor cannot freopen"); - } - #ifdef WIN32 - out = _fdopen (old_stdout, "w"); - #else - out = fdopen (old_stdout, "w"); - #endif // Execute the initial script TString scriptLine = TString(".x ") + TROOT::GetTutorialDir() + "/math/fit/FittingDemo.C+"; @@ -164,73 +141,6 @@ class FitEditorUnitTesting // then they should comment this method. ~FitEditorUnitTesting() { f->DoClose(); - gApplication->Terminate(); - } - - // This is a generic method to make the output of all the tests - // consistent. T is a function pointer to one of the tests - // function. It has been implemented through templates to permit - // more test types than the originally designed. - // @ str : Name of the test - // @ func : Member function pointer to the real implementation of - // the test. - template - int MakeTest(const char* str, T func ) - { - fprintf(stdout, "\n***** %s *****\n", str); - int status = (this->*func)(); - - fprintf(stdout, "%s..........", str); - fprintf(out, "%s..........", str); - if ( status == 0 ) { - fprintf(stdout, "OK\n"); - fprintf(out, "OK\n"); - } - else { - fprintf(stdout, "FAILED\n"); - fprintf(out, "FAILED\n"); - } - return status; - } - - // This is where all the tests are called. If the user wants to add - // new tests or avoid executing one of the existing ones, it is - // here where they should do it. - int UnitTesting() { - int result = 0; - - fprintf(out, "\n**STARTING TFitEditor Unit Tests**\n\n"); - - result += MakeTest("TestHistogramFit...", &FitEditorUnitTesting::TestHistogramFit); - - result += MakeTest("TestGSLFit.........", &FitEditorUnitTesting::TestGSLFit); - - result += MakeTest("TestUpdate.........", &FitEditorUnitTesting::TestUpdate); - - result += MakeTest("TestGraph..........", &FitEditorUnitTesting::TestGraph); - - result += MakeTest("TestGraphError.....", &FitEditorUnitTesting::TestGraphError); - - result += MakeTest("TestGraph2D........", &FitEditorUnitTesting::TestGraph2D); - - result += MakeTest("TestGraph2DError...", &FitEditorUnitTesting::TestGraph2DError); - - result += MakeTest("TestUpdateTree.....", &FitEditorUnitTesting::TestUpdateTree); - - // TODO: reenable in batch once stack smashing issue is fixed - if (!gROOT->IsBatch()) - result += MakeTest("TestTree1D.........", &FitEditorUnitTesting::TestTree1D); - - // TODO: reenable once fit results are fixed - // result += MakeTest("TestTree2D.........", &FitEditorUnitTesting::TestTree2D); - - // TODO: reenable once fit results are fixed - // result += MakeTest("TestTreeND.........", &FitEditorUnitTesting::TestTreeND); - - fprintf(out, "\nRemember to also check outputUnitTesting.txt for " - "more detailed information\n\n"); - - return result; } // This is a debuggin method used to print the parameter values @@ -239,9 +149,9 @@ class FitEditorUnitTesting void PrintFuncPars() { static int counter = 0; - fprintf(out, "Printing the Func Pars (%d)\n", ++counter); - for ( unsigned int i = 0; i < f->fFuncPars.size(); ++i ) { - fprintf(out, "%30.20f %30.20f %30.20f\n", f->fFuncPars[i][0], f->fFuncPars[i][1], f->fFuncPars[i][2]); + fprintf(stdout, "Printing the Func Pars (%d)\n", ++counter); + for (unsigned int i = 0; i < f->fFuncPars.size(); ++i ) { + fprintf(stdout, "%30.20f %30.20f %30.20f\n", f->fFuncPars[i][0], f->fFuncPars[i][1], f->fFuncPars[i][2]); } } @@ -256,7 +166,7 @@ class FitEditorUnitTesting for ( unsigned int j = 0; j < 3; ++j) { int internalStatus = equals(pars[i][j], f->fFuncPars[i][j]); if (internalStatus != 0) { - fprintf(out, "i: %d, j: %d, e: %d, diff %g\n", i, j, internalStatus, (pars[i][j] - f->fFuncPars[i][j])); + fprintf(stdout, "i: %d, j: %d, e: %d, diff %g\n", i, j, internalStatus, (pars[i][j] - f->fFuncPars[i][j])); } status += internalStatus; } @@ -498,6 +408,73 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } + + // This is a generic method to make the output of all the tests + // consistent. T is a function pointer to one of the tests + // function. It has been implemented through templates to permit + // more test types than the originally designed. + // @ str : Name of the test + // @ func : Member function pointer to the real implementation of + // the test. + template + int MakeTest(const char* str, T func) + { + RedirectHandle_t gRH; + + gSystem->RedirectOutput(gTmpfilename.Data(), "w", &gRH); + + fprintf(stdout, "\n***** %s *****\n", str); + + int status = (this->*func)(); + + gSystem->RedirectOutput(0, 0, &gRH); + + fprintf(stdout, "%s..........%s\n", str, status == 0 ? "OK" : "FAILED"); + + return status; + } + + // This is where all the tests are called. If the user wants to add + // new tests or avoid executing one of the existing ones, it is + // here where they should do it. + int UnitTesting() + { + int result = 0; + + fprintf(stdout, "\n**STARTING TFitEditor Unit Tests**\n\n"); + + result += MakeTest("TestHistogramFit...", &FitEditorUnitTesting::TestHistogramFit); + + result += MakeTest("TestGSLFit.........", &FitEditorUnitTesting::TestGSLFit); + + result += MakeTest("TestUpdate.........", &FitEditorUnitTesting::TestUpdate); + + result += MakeTest("TestGraph..........", &FitEditorUnitTesting::TestGraph); + + result += MakeTest("TestGraphError.....", &FitEditorUnitTesting::TestGraphError); + + result += MakeTest("TestGraph2D........", &FitEditorUnitTesting::TestGraph2D); + + result += MakeTest("TestGraph2DError...", &FitEditorUnitTesting::TestGraph2DError); + + result += MakeTest("TestUpdateTree.....", &FitEditorUnitTesting::TestUpdateTree); + + // TODO: reenable in batch once stack smashing issue is fixed + if (!gROOT->IsBatch()) + result += MakeTest("TestTree1D.........", &FitEditorUnitTesting::TestTree1D); + + // TODO: reenable once fit results are fixed + // result += MakeTest("TestTree2D.........", &FitEditorUnitTesting::TestTree2D); + + // TODO: reenable once fit results are fixed + // result += MakeTest("TestTreeND.........", &FitEditorUnitTesting::TestTreeND); + + fprintf(stdout, "\nRemember to also check outputUnitTesting.txt for " + "more detailed information\n\n"); + + return result; + } + }; // Runs the basic script and pops out the fit panel. Then it will @@ -507,11 +484,21 @@ int UnitTesting() { gROOT->SetWebDisplay("off"); + gTmpfilename = "UnitTesting.log"; + auto f = gSystem->TempFileName(gTmpfilename); + fclose(f); + FitEditorUnitTesting fUT; - return fUT.UnitTesting(); + auto res = fUT.UnitTesting(); + + gSystem->Unlink(gTmpfilename.Data()); + + return res; } +#ifndef __ROOTCLING__ + // The main function. It is VERY important that it is run using the // TApplication. int main(int argc, char** argv) @@ -528,3 +515,5 @@ int main(int argc, char** argv) return ret; } + +#endif \ No newline at end of file From 41e95c02e447db1153a4a860e485c053df18aca3 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 24 Apr 2026 07:08:53 +0200 Subject: [PATCH 2/4] Provide special virtualx for fitpanel UnitTesting To correctly run gui classes, gVirtualX->CreateGC should return every time unique value. Otherwise cleanup of many classes does not work correctly. --- gui/fitpanel/test/UnitTesting.cxx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/gui/fitpanel/test/UnitTesting.cxx b/gui/fitpanel/test/UnitTesting.cxx index 10cfd4a2e024c..3966e56d09984 100644 --- a/gui/fitpanel/test/UnitTesting.cxx +++ b/gui/fitpanel/test/UnitTesting.cxx @@ -10,6 +10,7 @@ #include "TError.h" #include "TGComboBox.h" +#include "TVirtualX.h" #include "TF2.h" #include "TMath.h" @@ -26,6 +27,17 @@ #include "../src/CommonDefs.h" +class TTestVirtualX : public TVirtualX { + Long_t fCounter = 1; + public: + + TTestVirtualX(const char *name, const char *title) : TVirtualX(name, title) {} + + GContext_t CreateGC(Drawable_t, GCValues_t *) override + { + return (GContext_t) fCounter++; + } +}; TString gTmpfilename; @@ -482,6 +494,9 @@ class FitEditorUnitTesting // tests int UnitTesting() { + if (gROOT->IsBatch()) + gVirtualX = new TTestVirtualX("BatchTest", "ROOT Interface to batch graphics"); + gROOT->SetWebDisplay("off"); gTmpfilename = "UnitTesting.log"; From 02e3e4b57f6ef41e05c89dfce4c8df677d0454b3 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 24 Apr 2026 07:10:14 +0200 Subject: [PATCH 3/4] Enable fitpanel UnitTesting again --- gui/fitpanel/test/CMakeLists.txt | 5 +-- gui/fitpanel/test/UnitTesting.cxx | 67 ++++++++++++++++--------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/gui/fitpanel/test/CMakeLists.txt b/gui/fitpanel/test/CMakeLists.txt index 6141c3950e432..4b5dca1c614e1 100644 --- a/gui/fitpanel/test/CMakeLists.txt +++ b/gui/fitpanel/test/CMakeLists.txt @@ -4,12 +4,11 @@ # For the licensing terms see $ROOTSYS/LICENSE. # For the list of contributors see $ROOTSYS/README/CREDITS. -# @author Danilo Piparo CERN +# @author Sergey Linev ROOT_EXECUTABLE(fitPanelUnitTesting UnitTesting.cxx LIBRARIES FitPanel Gui Tree Hist MathCore) -# batch usage is instable - need more investigations -#ROOT_ADD_TEST(test-fitpanel-UnitTesting-batch COMMAND fitPanelUnitTesting -b FAILREGEX "FAILED|Error in") +ROOT_ADD_TEST(test-fitpanel-UnitTesting-batch COMMAND fitPanelUnitTesting -b FAILREGEX "FAILED|Error in") find_program(XVFB_RUN_EXECUTABLE NAMES xvfb-run) diff --git a/gui/fitpanel/test/UnitTesting.cxx b/gui/fitpanel/test/UnitTesting.cxx index 3966e56d09984..ca625f68fb736 100644 --- a/gui/fitpanel/test/UnitTesting.cxx +++ b/gui/fitpanel/test/UnitTesting.cxx @@ -39,8 +39,6 @@ class TTestVirtualX : public TVirtualX { } }; -TString gTmpfilename; - // Function that compares to doubles up to an error limit int equals(Double_t n1, Double_t n2, double ERRORLIMIT = 1.E-4) { @@ -124,7 +122,8 @@ class FitEditorUnitTesting }; // Constructor: Receives the instance of the TFitEditor - FitEditorUnitTesting() { + FitEditorUnitTesting() + { // Execute the initial script TString scriptLine = TString(".x ") + TROOT::GetTutorialDir() + "/math/fit/FittingDemo.C+"; @@ -151,7 +150,8 @@ class FitEditorUnitTesting // trying to retrieve the TFitEditor singleton. If the user wants // to play a bit with the fitpanel once the tests have finised, // then they should comment this method. - ~FitEditorUnitTesting() { + ~FitEditorUnitTesting() + { f->DoClose(); } @@ -191,7 +191,8 @@ class FitEditorUnitTesting // of the test should be enough to know what they are testing, as // these tests are meant to be as simple as possible. - int TestHistogramFit() { + int TestHistogramFit() + { f->fTypeFit->Select(kFP_UFUNC, kTRUE); f->fFuncList->Select(kFP_ALTFUNC, kTRUE); f->DoFit(); @@ -207,7 +208,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestGSLFit() { + int TestGSLFit() + { f->fTypeFit->Select(kFP_PREVFIT, kTRUE); f->fLibGSL->Toggled(kTRUE); f->fMinMethodList->Select(kFP_BFGS2, kTRUE); @@ -224,7 +226,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestUpdate() { + int TestUpdate() + { TString scriptLine = TString(".x ") + TROOT::GetTutorialsDir() + "/math/fit/ConfidenceIntervals.C+"; gROOT->ProcessLine(scriptLine.Data()); f->DoUpdate(); @@ -232,7 +235,8 @@ class FitEditorUnitTesting return 0; } - int TestGraph() { + int TestGraph() + { SelectEntry(f->fDataSet, "TGraph::GraphNoError"); f->fLibMinuit2->Toggled(kTRUE); @@ -249,7 +253,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestGraphError() { + int TestGraphError() + { SelectEntry(f->fDataSet, "TGraphErrors::Graph"); f->fLibMinuit2->Toggled(kTRUE); @@ -266,7 +271,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestGraph2D() { + int TestGraph2D() + { SelectEntry(f->fDataSet, "TGraph2D::Graph2DNoError"); f->fLibMinuit2->Toggled(kTRUE); @@ -290,7 +296,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestGraph2DError() { + int TestGraph2DError() + { SelectEntry(f->fDataSet, "TGraph2DErrors::Graph2D"); f->fLibMinuit2->Toggled(kTRUE); @@ -314,13 +321,15 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestUpdateTree() { + int TestUpdateTree() + { createTree(); f->DoUpdate(); return 0; } - int TestTree1D() { + int TestTree1D() + { TObject* objSelected = gROOT->FindObject("tree"); if ( !objSelected ) throw InvalidPointer("In TestUpdateTree"); @@ -345,7 +354,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestTree2D() { + int TestTree2D() + { TObject* objSelected = gROOT->FindObject("tree"); if ( !objSelected ) throw InvalidPointer("In TestUpdateTree"); @@ -374,7 +384,8 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - int TestTreeND() { + int TestTreeND() + { TObject* objSelected = gROOT->FindObject("tree"); if ( !objSelected ) throw InvalidPointer("In TestUpdateTree"); @@ -421,7 +432,7 @@ class FitEditorUnitTesting return CompareFuncPars(pars); } - // This is a generic method to make the output of all the tests + // This is a generic method to make the output of all the tests // consistent. T is a function pointer to one of the tests // function. It has been implemented through templates to permit // more test types than the originally designed. @@ -429,11 +440,11 @@ class FitEditorUnitTesting // @ func : Member function pointer to the real implementation of // the test. template - int MakeTest(const char* str, T func) + int MakeTest(const char *str, T func, Bool_t first = kFALSE) { RedirectHandle_t gRH; - gSystem->RedirectOutput(gTmpfilename.Data(), "w", &gRH); + gSystem->RedirectOutput("outputUnitTesting.txt", first ? "w" : "a", &gRH); fprintf(stdout, "\n***** %s *****\n", str); @@ -455,7 +466,7 @@ class FitEditorUnitTesting fprintf(stdout, "\n**STARTING TFitEditor Unit Tests**\n\n"); - result += MakeTest("TestHistogramFit...", &FitEditorUnitTesting::TestHistogramFit); + result += MakeTest("TestHistogramFit...", &FitEditorUnitTesting::TestHistogramFit, kTRUE); result += MakeTest("TestGSLFit.........", &FitEditorUnitTesting::TestGSLFit); @@ -471,9 +482,7 @@ class FitEditorUnitTesting result += MakeTest("TestUpdateTree.....", &FitEditorUnitTesting::TestUpdateTree); - // TODO: reenable in batch once stack smashing issue is fixed - if (!gROOT->IsBatch()) - result += MakeTest("TestTree1D.........", &FitEditorUnitTesting::TestTree1D); + result += MakeTest("TestTree1D.........", &FitEditorUnitTesting::TestTree1D); // TODO: reenable once fit results are fixed // result += MakeTest("TestTree2D.........", &FitEditorUnitTesting::TestTree2D); @@ -482,7 +491,7 @@ class FitEditorUnitTesting // result += MakeTest("TestTreeND.........", &FitEditorUnitTesting::TestTreeND); fprintf(stdout, "\nRemember to also check outputUnitTesting.txt for " - "more detailed information\n\n"); + "more detailed information\n\n"); return result; } @@ -499,17 +508,9 @@ int UnitTesting() gROOT->SetWebDisplay("off"); - gTmpfilename = "UnitTesting.log"; - auto f = gSystem->TempFileName(gTmpfilename); - fclose(f); - FitEditorUnitTesting fUT; - auto res = fUT.UnitTesting(); - - gSystem->Unlink(gTmpfilename.Data()); - - return res; + return fUT.UnitTesting(); } #ifndef __ROOTCLING__ @@ -531,4 +532,4 @@ int main(int argc, char** argv) return ret; } -#endif \ No newline at end of file +#endif From b67cd711222b7925e2b4d9d4c8cbc52445020e13 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 24 Apr 2026 09:37:25 +0200 Subject: [PATCH 4/4] [fitpanel test] Add extra check in CompareFuncPars --- gui/fitpanel/test/UnitTesting.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gui/fitpanel/test/UnitTesting.cxx b/gui/fitpanel/test/UnitTesting.cxx index ca625f68fb736..7a02a0a3bbee5 100644 --- a/gui/fitpanel/test/UnitTesting.cxx +++ b/gui/fitpanel/test/UnitTesting.cxx @@ -174,6 +174,12 @@ class FitEditorUnitTesting int CompareFuncPars(std::vector& pars) { int status = 0; + + if (f->fFuncPars.size() != pars.size()) { + fprintf(stderr, "ERROR: mismatch of parameters size fitpanel: %u refs: %u\n", (unsigned) f->fFuncPars.size(), (unsigned) pars.size()); + return 111; + } + for ( unsigned int i = 0; i < f->fFuncPars.size(); ++i ) { for ( unsigned int j = 0; j < 3; ++j) { int internalStatus = equals(pars[i][j], f->fFuncPars[i][j]);