diff --git a/bindings/pyroot/pythonizations/test/CMakeLists.txt b/bindings/pyroot/pythonizations/test/CMakeLists.txt index 6d705259f5af8..a4baa0c51b9c7 100644 --- a/bindings/pyroot/pythonizations/test/CMakeLists.txt +++ b/bindings/pyroot/pythonizations/test/CMakeLists.txt @@ -15,9 +15,6 @@ endif() ROOT_ADD_PYUNITTEST(pyroot_root_module root_module.py) ROOT_ADD_PYUNITTEST(pyroot_root_no_implicit_cppyy root_no_implicit_cppyy.py) -# @pythonization decorator -ROOT_ADD_PYUNITTEST(pyroot_pyz_decorator pythonization_decorator.py) - # General pythonizations ROOT_ADD_PYUNITTEST(pyroot_pyz_pretty_printing pretty_printing.py) ROOT_ADD_PYUNITTEST(pyroot_pyz_array_interface array_interface.py PYTHON_DEPS numpy) @@ -29,88 +26,64 @@ if(Python3_VERSION VERSION_GREATER_EQUAL 3.9) ROOT_ADD_PYUNITTEST(uhi_test_suite uhi_test_suite.py GENERIC PYTHON_DEPS uhi numpy) endif() -# STL containers pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_stl_vector stl_vector.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_stl_set stl_set.py) - -# TObject and subclasses pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tobject_contains tobject_contains.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tobject_comparisonops tobject_comparisonops.py) - -# TClass pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tclass_dynamiccast tclass_dynamiccast.py) +# STL containers pythonizations (std::vector and std::set) +ROOT_ADD_PYUNITTEST(pyroot_pyz_stl stl.py) -# TContext pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tcontext_contextmanager tcontext_contextmanager.py) +# TObject and subclasses pythonizations (__contains__ and comparison operators) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tobject tobject.py) -# TDirectory and subclasses pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tdirectory_attrsyntax tdirectory_attrsyntax.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tdirectoryfile_attrsyntax_get tdirectoryfile_attrsyntax_get.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tfile_attrsyntax_get_writeobject_open tfile_attrsyntax_get_writeobject_open.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tfile_constructor tfile_constructor.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tfile_context_manager tfile_context_manager.py) +# TDirectory and TFile pythonizations (attr/item syntax, Get, WriteObject, Open, +# constructor and context manager) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tdirectory_tfile tdirectory_tfile.py) -# TTree and subclasses pythonizations +# TTree and subclasses pythonizations (branch attr access, iteration and +# SetBranchAddress). ttree_branch stays separate: it declares its own MyStruct, +# which would clash with the TreeHelper.h definition used by the others. file(COPY TreeHelper.h DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -ROOT_ADD_PYUNITTEST(pyroot_pyz_ttree_branch_attr ttree_branch_attr.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_ttree_iterable ttree_iterable.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_ttree_setbranchaddress ttree_setbranchaddress.py PYTHON_DEPS numpy) +ROOT_ADD_PYUNITTEST(pyroot_pyz_ttree ttree.py PYTHON_DEPS numpy) ROOT_ADD_PYUNITTEST(pyroot_pyz_ttree_branch ttree_branch.py PYTHON_DEPS numpy) -# TColor-related pythonizations +# TColor-related pythonizations (regression_20018 lives in tcolor.py) ROOT_ADD_PYUNITTEST(pyroot_pyz_tcolor tcolor.py) -ROOT_ADD_PYUNITTEST(regression_20018 regression_20018.py) # TH1 and subclasses pythonizations ROOT_ADD_PYUNITTEST(pyroot_pyz_th1 th1.py) # The above tests a deadlock. It should complete in about 1s. If we don't reduce the timeout, we need to wait 1500 s. set_tests_properties(pyunittests-bindings-pyroot-pythonizations-pyroot-pyz-th1 PROPERTIES TIMEOUT 30) ROOT_ADD_PYUNITTEST(pyroot_pyz_th1_fillN th1_fillN.py PYTHON_DEPS numpy) -ROOT_ADD_PYUNITTEST(pyroot_pyz_th1_addfunction th1_addfunction.py) ROOT_ADD_PYUNITTEST(pyroot_pyz_th2_fillN th2_fillN.py PYTHON_DEPS numpy) -ROOT_ADD_PYUNITTEST(pyroot_pyz_th2 th2.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_th3 th3.py) +# th1_addfunction, th2 and th3 merged into thist.py +ROOT_ADD_PYUNITTEST(pyroot_pyz_thist thist.py) -# TGraph, TGraph2D and error subclasses pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tgraph_getters tgraph_getters.py) +# TCollection and subclasses pythonizations (len, list methods, operators, iteration) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tcollection tcollection.py) -# TCollection and subclasses pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tcollection_len tcollection_len.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tcollection_listmethods tcollection_listmethods.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tcollection_operators tcollection_operators.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tcollection_iterable tcollection_iterable.py) +# TSeqCollection and subclasses pythonizations (item access and list methods) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tseqcollection tseqcollection.py) -# TSeqCollection and subclasses pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tseqcollection_itemaccess tseqcollection_itemaccess.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tseqcollection_listmethods tseqcollection_listmethods.py) +# TArray and subclasses pythonizations (len and item access) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tarray tarray.py) -# TIter pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_titer_iterator titer_iterator.py) +# TVectorT pythonizations (len and item access) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tvectort tvectort.py) -# TArray and subclasses pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tarray_len tarray_len.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tarray_getitem tarray_getitem.py) +# TVector3 pythonizations (len and item access) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tvector3 tvector3.py) -# TVectorT pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tvectort_len tvectort_len.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tvectort_getitem tvectort_getitem.py) +# TString pythonizations (len, str/repr, comparison operators and converter) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tstring tstring.py) -# TVector3 pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tvector3_len tvector3_len.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tvector3_getitem tvector3_getitem.py) +# TObjString pythonizations (len, str/repr and comparison operators) +ROOT_ADD_PYUNITTEST(pyroot_pyz_tobjstring tobjstring.py) -# TString pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tstring_len tstring_len.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tstring_str_repr tstring_str_repr.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tstring_comparisonops tstring_comparisonops.py) - -# TString converter -ROOT_ADD_PYUNITTEST(pyroot_conv_tstring tstring_converter.py) +# @pythonization decorator (kept standalone: it inspects the global set of +# pythonized classes, so it must not share a process with other tests) +ROOT_ADD_PYUNITTEST(pyroot_pyz_decorator pythonization_decorator.py) -# TObjString pythonizations -ROOT_ADD_PYUNITTEST(pyroot_pyz_tobjstring_len tobjstring_len.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tobjstring_str_repr tobjstring_str_repr.py) -ROOT_ADD_PYUNITTEST(pyroot_pyz_tobjstring_comparisonops tobjstring_comparisonops.py) +# Miscellaneous small pythonizations grouped in a single file to avoid paying the +# Python-startup + import-ROOT overhead once per tiny test: TClass::DynamicCast, +# TContext context manager, TComplex operators, TIter iteration and TGraph getters. +ROOT_ADD_PYUNITTEST(pyroot_pyz_misc misc_pyz.py) # RVec and subclasses pythonizations ROOT_ADD_PYUNITTEST(pyroot_pyz_rvec rvec.py) @@ -187,9 +160,6 @@ if (dataframe) endif() endif() -# TComplex pythonizations -ROOT_ADD_PYUNITTEST(pyroot_tcomplex tcomplex_operators.py) - # Tests with memory usage ROOT_ADD_PYUNITTEST(pyroot_memory memory.py) diff --git a/bindings/pyroot/pythonizations/test/TreeHelper.h b/bindings/pyroot/pythonizations/test/TreeHelper.h index 25203d95e5263..30906aebd74d2 100644 --- a/bindings/pyroot/pythonizations/test/TreeHelper.h +++ b/bindings/pyroot/pythonizations/test/TreeHelper.h @@ -1,3 +1,5 @@ +#ifndef PYROOT_TEST_TREEHELPER_H +#define PYROOT_TEST_TREEHELPER_H // Helper struct for this test struct MyStruct { @@ -120,3 +122,5 @@ class MyTree : public TTree { } }; } + +#endif // PYROOT_TEST_TREEHELPER_H diff --git a/bindings/pyroot/pythonizations/test/tgraph_getters.py b/bindings/pyroot/pythonizations/test/misc_pyz.py similarity index 58% rename from bindings/pyroot/pythonizations/test/tgraph_getters.py rename to bindings/pyroot/pythonizations/test/misc_pyz.py index 9e436dc6c20bb..d732af025ba59 100644 --- a/bindings/pyroot/pythonizations/test/tgraph_getters.py +++ b/bindings/pyroot/pythonizations/test/misc_pyz.py @@ -1,7 +1,185 @@ -import unittest import array +import os +import unittest import ROOT +from ROOT import TClass, TComplex, TDirectory, TObject, TObjString + + +class TClassDynamicCast(unittest.TestCase): + """ + Test for the pythonization of TClass::DynamicCast, which adds an + an extra cast before returning the Python proxy to the user so that + it has the right type. + """ + + # Tests + def test_dynamiccast(self): + tobj_class = TClass.GetClass("TObject") + tobjstr_class = TClass.GetClass("TObjString") + + o = TObjString("a") + + # Upcast: TObject <- TObjString + o_upcast = tobjstr_class.DynamicCast(tobj_class, o) + self.assertEqual(type(o_upcast), TObject) + + # Downcast: TObject -> TObjString + o_downcast = tobjstr_class.DynamicCast(tobj_class, o_upcast, False) + self.assertEqual(type(o_downcast), TObjString) + + +class TContextContextManager(unittest.TestCase): + """ + Test of TContext used as context manager + """ + + def default_constructor(self): + """ + Check status of gDirectory with default constructor. + """ + filename = "TContextContextManager_test_default_constructor.root" + self.assertEqual(ROOT.gDirectory, ROOT.gROOT) + + with TDirectory.TContext(): + # Create a file to change gDirectory + testfile = ROOT.TFile(filename, "recreate") + self.assertEqual(ROOT.gDirectory, testfile) + testfile.Close() + + self.assertEqual(ROOT.gDirectory, ROOT.gROOT) + os.remove(filename) + + def constructor_onearg(self): + """ + Check status of gDirectory with constructor taking a new directory. + """ + filenames = ["TContextContextManager_test_constructor_onearg_{}.root".format(i) for i in range(2)] + + file0 = ROOT.TFile(filenames[0], "recreate") + file1 = ROOT.TFile(filenames[1], "recreate") + self.assertEqual(ROOT.gDirectory, file1) + + with TDirectory.TContext(file0): + self.assertEqual(ROOT.gDirectory, file0) + + self.assertEqual(ROOT.gDirectory, file1) + file0.Close() + file1.Close() + for filename in filenames: + os.remove(filename) + + def constructor_twoargs(self): + """ + Check status of gDirectory with constructor taking the previous directory and a new one. + """ + filenames = ["TContextContextManager_test_constructor_onearg_{}.root".format(i) for i in range(3)] + + file0 = ROOT.TFile(filenames[0], "recreate") + file1 = ROOT.TFile(filenames[1], "recreate") + file2 = ROOT.TFile(filenames[2], "recreate") + self.assertEqual(ROOT.gDirectory, file2) + + with TDirectory.TContext(file0, file1): + self.assertEqual(ROOT.gDirectory, file1) + + self.assertEqual(ROOT.gDirectory, file0) + file0.Close() + file1.Close() + file2.Close() + for filename in filenames: + os.remove(filename) + + def test_all(self): + """ + Run all tests of this class sequentially. + The tests of this class rely on the current directory, which can be changed + unpredictably if they are run concurrently. + """ + self.default_constructor() + self.constructor_onearg() + self.constructor_twoargs() + + +class TestTComplexOperators(unittest.TestCase): + """ + Test for the operators of TComplex: + __radd__, __rsub__, __rmul__, __rtruediv__/__rdiv__. + """ + + c = TComplex(4.,0) + + d = 2. + + s = 'string' + + # check the expected result for d + c and that Re(c + d) == Re(d + c) + def test_radd(self): + self.assertEqual((self.d + self.c).Re(), 6.0) + self.assertEqual((self.c + self.d).Re(), (self.d + self.c).Re()) + + # check the expected result for d - c and that Re(c - d) == -Re(d - c) + def test_rsub(self): + self.assertEqual((self.d - self.c).Re(), -2.0) + self.assertEqual((self.c - self.d).Re(), -((self.d - self.c).Re())) + + # check the expected result for d * c and that Re(c * d) == Re(d * c) + def test_rmul(self): + self.assertEqual((self.d * self.c).Re(), 8.0) + self.assertEqual((self.c * self.d).Re(), (self.d * self.c).Re()) + + # check the expected result for d / c + def test_rdiv(self): + self.assertEqual((self.d / self.c).Re(), 0.5) + with self.assertRaises(TypeError): + self.s / self.c + + +class TIterIterator(unittest.TestCase): + """ + Test for the pythonization that allows instances of TIter to + behave as Python iterators. + """ + + num_elems = 3 + + # Helpers + def create_tcollection(self): + c = ROOT.TList() + for _ in range(self.num_elems): + o = ROOT.TObject() + # Prevent immediate deletion of C++ TObjects + ROOT.SetOwnership(o, False) + c.Add(o) + + return c + + # Tests + def test_iterable(self): + # Check that TIter instances are iterable + c = self.create_tcollection() + + itc = ROOT.TIter(c) + # An iterator of an iterator is itself + self.assertEqual(itc, iter(itc)) + + def test_iterator(self): + # Check that TIter instances are iterators + c = self.create_tcollection() + + itc1 = ROOT.TIter(c) + itc2 = ROOT.TIter(c) + for _ in range(c.GetEntries()): + self.assertIs(next(itc1), itc2.Next()) + + def test_for_loop_syntax(self): + # Somehow redundant, but good to test with real syntax + c = self.create_tcollection() + + itc1 = ROOT.TIter(c) + itc2 = ROOT.TIter(c) + for elem1, elem2 in zip(itc1, itc2): + self.assertIs(elem1, elem2) class TGraphGetters(unittest.TestCase): diff --git a/bindings/pyroot/pythonizations/test/regression_20018.py b/bindings/pyroot/pythonizations/test/regression_20018.py deleted file mode 100644 index 7a685d95bac04..0000000000000 --- a/bindings/pyroot/pythonizations/test/regression_20018.py +++ /dev/null @@ -1,45 +0,0 @@ -import unittest - -import ROOT - - -class Regression20018(unittest.TestCase): - """ - Regression test for https://github.com/root-project/root/issues/20018 - - Calling ``TColor.DefinedColors(1)`` puts the global color bookkeeping into - the "always store colors" mode (``gLastDefinedColors = -1``). When a canvas - is afterwards serialized to JSON for the web display - which is exactly what - JupyROOT does to show a canvas inline in a notebook - ``TCanvas::Streamer`` - used to unconditionally call ``fPrimitives->Add(...)`` to attach the list of - colors. During the web-canvas snapshot the pad primitives are temporarily - detached (``fPrimitives`` is set to ``nullptr``, see - ``TWebCanvas::CreatePadSnapshot``), so this dereferenced a null pointer and - crashed with a segmentation violation. - """ - - def test_definedcolors_web_canvas_json(self): - # TWebCanvas (and thus CreateCanvasJSON) is only available when ROOT is - # built with the web display (root7/webgui). Without it, JupyROOT uses a - # different code path that is not affected by this issue. - if not hasattr(ROOT, "TWebCanvas") or not hasattr(ROOT.TWebCanvas, "CreateCanvasJSON"): - self.skipTest("ROOT was built without the web display (webgui)") - - ROOT.gROOT.SetBatch(True) - - # This is what triggers the bug: force the "always store colors" mode. - ROOT.TColor.DefinedColors(1) - - c = ROOT.TCanvas("c_regression_20018", "Basic ROOT Plot", 800, 600) - h = ROOT.TH1F("h_regression_20018", "Example Histogram;X axis;Entries", 100, 0, 10) - h.FillRandom("gaus", 1000) - h.Draw() - c.Update() - - # Used to segfault in TCanvas::Streamer due to a null fPrimitives. - json = ROOT.TWebCanvas.CreateCanvasJSON(c, 23, True) - self.assertGreater(len(json.Data()), 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/stl_vector.py b/bindings/pyroot/pythonizations/test/stl.py similarity index 72% rename from bindings/pyroot/pythonizations/test/stl_vector.py rename to bindings/pyroot/pythonizations/test/stl.py index 99170082fc442..1292ef9248060 100644 --- a/bindings/pyroot/pythonizations/test/stl_vector.py +++ b/bindings/pyroot/pythonizations/test/stl.py @@ -21,7 +21,7 @@ def assertVec(vec, arr): class STL_vector(unittest.TestCase): """ - Tests for the pythonizations of std::vector. + Tests for the pythonizations of std::vector. """ def test_vec_char_data(self): @@ -171,5 +171,79 @@ def test_tree_with_containers(self): self.assertEqual(entry_array_i, entry_root_i) +class STL_set(unittest.TestCase): + """ + Tests for the pythonizations of std::set. + """ + + def test_set_char_data(self): + """ + Test that a std::set of char behaves as a Python set. + """ + + elems = ['a', 'b', 'b', 'c'] + s = ROOT.std.set['char'](elems) + self.assertEqual(set(s), set(elems)) + self.assertTrue(s) + + for entry in s: + self.assertTrue(entry in set(elems)) + + def test_set_types(self): + """ + Instantiate std::set with different types. + """ + + for entry_type in ['int', 'float', 'double', 'char', 'const char*', 'std::string']: + ROOT.std.set[entry_type]() + + def test_stl_set_boolean(self): + """ + Test that the boolean conversion of a std::set works as expected. + https://github.com/root-project/root/issues/14573 + """ + + for entry_type in ['int', 'float', 'double']: + s = ROOT.std.set[entry_type]() + self.assertTrue(s.empty()) + self.assertFalse(bool(s)) + + s.insert(1) + self.assertFalse(s.empty()) + self.assertTrue(bool(s)) + + def test_stl_set_tree(self): + """ + Test that a TTree with a std::set branch behaves as expected. + """ + + tree = ROOT.TTree("tree", "Tree with std::vector") + + entries_to_fill = [ + set(), + {1}, + {1, 2}, + ] + + # Create variables to store std::vector elements + entry_root = ROOT.std.set(int)() + + # Create branches in the TTree + tree.Branch("set", entry_root) + + for entry in entries_to_fill: + entry_root.clear() + for element in entry: + entry_root.insert(element) + tree.Fill() + + for i in range(tree.GetEntries()): + tree.GetEntry(i) + entry_python = entries_to_fill[i] + self.assertEqual(entry_python, set(entry_root)) + self.assertEqual(bool(entry_python), bool(entry_root)) + self.assertEqual(len(entry_python), len(entry_root)) + + if __name__ == '__main__': unittest.main() diff --git a/bindings/pyroot/pythonizations/test/stl_set.py b/bindings/pyroot/pythonizations/test/stl_set.py deleted file mode 100644 index fbbf10a3bb443..0000000000000 --- a/bindings/pyroot/pythonizations/test/stl_set.py +++ /dev/null @@ -1,80 +0,0 @@ -import unittest -import ROOT - - -class STL_set(unittest.TestCase): - """ - Tests for the pythonizations of std::set. - """ - - def test_set_char_data(self): - """ - Test that a std::set of char behaves as a Python set. - """ - - elems = ['a', 'b', 'b', 'c'] - s = ROOT.std.set['char'](elems) - self.assertEqual(set(s), set(elems)) - self.assertTrue(s) - - for entry in s: - self.assertTrue(entry in set(elems)) - - def test_set_types(self): - """ - Instantiate std::set with different types. - """ - - for entry_type in ['int', 'float', 'double', 'char', 'const char*', 'std::string']: - ROOT.std.set[entry_type]() - - def test_stl_set_boolean(self): - """ - Test that the boolean conversion of a std::set works as expected. - https://github.com/root-project/root/issues/14573 - """ - - for entry_type in ['int', 'float', 'double']: - s = ROOT.std.set[entry_type]() - self.assertTrue(s.empty()) - self.assertFalse(bool(s)) - - s.insert(1) - self.assertFalse(s.empty()) - self.assertTrue(bool(s)) - - def test_stl_set_tree(self): - """ - Test that a TTree with a std::set branch behaves as expected. - """ - - tree = ROOT.TTree("tree", "Tree with std::vector") - - entries_to_fill = [ - set(), - {1}, - {1, 2}, - ] - - # Create variables to store std::vector elements - entry_root = ROOT.std.set(int)() - - # Create branches in the TTree - tree.Branch("set", entry_root) - - for entry in entries_to_fill: - entry_root.clear() - for element in entry: - entry_root.insert(element) - tree.Fill() - - for i in range(tree.GetEntries()): - tree.GetEntry(i) - entry_python = entries_to_fill[i] - self.assertEqual(entry_python, set(entry_root)) - self.assertEqual(bool(entry_python), bool(entry_root)) - self.assertEqual(len(entry_python), len(entry_root)) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tarray_getitem.py b/bindings/pyroot/pythonizations/test/tarray.py similarity index 62% rename from bindings/pyroot/pythonizations/test/tarray_getitem.py rename to bindings/pyroot/pythonizations/test/tarray.py index 3e666c917474d..c494fc8713df0 100644 --- a/bindings/pyroot/pythonizations/test/tarray_getitem.py +++ b/bindings/pyroot/pythonizations/test/tarray.py @@ -3,6 +3,27 @@ import ROOT +class TArrayLen(unittest.TestCase): + """ + Test for the pythonization that allows to access the number of elements of a + TArray (or subclass) by calling `len` on it. + """ + + num_elems = 3 + + # Helpers + def check_len(self, a): + self.assertEqual(len(a), self.num_elems) + self.assertEqual(len(a), a.GetSize()) + + # Tests + def test_tarrayi(self): + self.check_len(ROOT.TArrayI(self.num_elems)) + + def test_tarrayd(self): + self.check_len(ROOT.TArrayD(self.num_elems)) + + class TArrayGetItem(unittest.TestCase): """ Test for the pythonization that allows to: (i) get an item of a diff --git a/bindings/pyroot/pythonizations/test/tarray_len.py b/bindings/pyroot/pythonizations/test/tarray_len.py deleted file mode 100644 index eb5075b2fcbbb..0000000000000 --- a/bindings/pyroot/pythonizations/test/tarray_len.py +++ /dev/null @@ -1,29 +0,0 @@ -import unittest - -import ROOT - - -class TArrayLen(unittest.TestCase): - """ - Test for the pythonization that allows to access the number of elements of a - TArray (or subclass) by calling `len` on it. - """ - - num_elems = 3 - - # Helpers - def check_len(self, a): - self.assertEqual(len(a), self.num_elems) - self.assertEqual(len(a), a.GetSize()) - - # Tests - def test_tarrayi(self): - self.check_len(ROOT.TArrayI(self.num_elems)) - - def test_tarrayd(self): - self.check_len(ROOT.TArrayD(self.num_elems)) - - -if __name__ == '__main__': - unittest.main() - diff --git a/bindings/pyroot/pythonizations/test/tclass_dynamiccast.py b/bindings/pyroot/pythonizations/test/tclass_dynamiccast.py deleted file mode 100644 index 2d7a7760845ff..0000000000000 --- a/bindings/pyroot/pythonizations/test/tclass_dynamiccast.py +++ /dev/null @@ -1,30 +0,0 @@ -import unittest - -from ROOT import TClass, TObject, TObjString - - -class TClassDynamicCast(unittest.TestCase): - """ - Test for the pythonization of TClass::DynamicCast, which adds an - an extra cast before returning the Python proxy to the user so that - it has the right type. - """ - - # Tests - def test_dynamiccast(self): - tobj_class = TClass.GetClass("TObject") - tobjstr_class = TClass.GetClass("TObjString") - - o = TObjString("a") - - # Upcast: TObject <- TObjString - o_upcast = tobjstr_class.DynamicCast(tobj_class, o) - self.assertEqual(type(o_upcast), TObject) - - # Downcast: TObject -> TObjString - o_downcast = tobjstr_class.DynamicCast(tobj_class, o_upcast, False) - self.assertEqual(type(o_downcast), TObjString) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tcollection.py b/bindings/pyroot/pythonizations/test/tcollection.py new file mode 100644 index 0000000000000..685bc2648e299 --- /dev/null +++ b/bindings/pyroot/pythonizations/test/tcollection.py @@ -0,0 +1,258 @@ +import unittest + +import ROOT + + +class TCollectionLen(unittest.TestCase): + """ + Test for the pythonization that allows to access the number of elements of a + TCollection (or subclass) by calling `len` on it. + """ + + num_elems = 3 + tobject_list = [ ROOT.TObject() for _ in range(num_elems) ] + + # Helpers + def add_elems_check_len(self, c): + for elem in self.tobject_list: + c.Add(elem) + + self.assertEqual(len(c), self.num_elems) + self.assertEqual(len(c), c.GetEntries()) + + # Tests + def test_tlist(self): + self.add_elems_check_len(ROOT.TList()) + + def test_tobjarray(self): + self.add_elems_check_len(ROOT.TObjArray()) + + def test_thashtable(self): + self.add_elems_check_len(ROOT.THashTable()) + + +class TCollectionListMethods(unittest.TestCase): + """ + Test for the Python-list-like methods added to TCollection (and subclasses): + append, remove, extend, count + """ + + num_elems = 3 + + _global_objects = [] + + # Helpers + def create_tcollection(self): + c = ROOT.TList() + for _ in range(self.num_elems): + o = ROOT.TObject() + c.Add(o) + # To prevent deletion of the objects (TList is by default non-owning) + self._global_objects.append(o) + + return c + + # Tests + def test_append(self): + c = self.create_tcollection() + + o = ROOT.TObject() + self.assertFalse(c.Contains(o)) + len1 = c.GetEntries() + + c.append(o) + + len2 = c.GetEntries() + self.assertEqual(len1 + 1, len2) + self.assertTrue(c.Contains(o)) + + # Skip elements that were already there + itc = ROOT.TIter(c) + for _ in range(len1): + itc.Next() + + # Check that `o` is indeed the last element + self.assertIs(o, itc.Next()) + + # Clear before the added element might be garbage collected, + # to avoid dangling pointer access. + c.Clear() + + def test_remove(self): + c = ROOT.TList() + + o1 = ROOT.TObject() + o2 = ROOT.TObject() + + c.Add(o1) + c.Add(o2) + c.Add(o1) + + self.assertTrue(c.Contains(o1)) + self.assertEqual(c.GetEntries(), 3) + + c.remove(o1) + + self.assertEqual(c.GetEntries(), 2) + + c.remove(o1) + + self.assertEqual(c.GetEntries(), 1) + self.assertFalse(c.Contains(o1)) + + with self.assertRaises(ValueError): + c.remove(o1) + + c.Clear() + + def test_extend(self): + c1 = self.create_tcollection() + c2 = self.create_tcollection() + + len1 = c1.GetEntries() + len2 = c2.GetEntries() + + c1.extend(c2) + + len1_final = c1.GetEntries() + + self.assertEqual(len1_final, len1 + len2) + + # Skip elements that were already there + itc1 = ROOT.TIter(c1) + for _ in range(len1): + itc1.Next() + + # Compare with elements of second collection + itc2 = ROOT.TIter(c2) + for _ in range(len2): + self.assertIs(itc1.Next(), itc2.Next()) + + +class TCollectionOperators(unittest.TestCase): + """ + Test for the Python operators defined in TCollection (and subclasses): + __add__, __mul__, __rmul__, __imul__ + """ + + num_elems = 3 + factor = 2 + + # Helpers + def create_tcollection(self): + c = ROOT.TList() + for _ in range(self.num_elems): + o = ROOT.TObject() + # Prevent immediate deletion of C++ TObjects + ROOT.SetOwnership(o, False) + c.Add(o) + + return c + + def check_mul_result(self, c, cmul): + lenc = c.GetEntries() + + self.assertEqual(cmul.GetEntries(), lenc * self.factor) + + itmul = ROOT.TIter(cmul) + for _ in range(self.factor): + itc = ROOT.TIter(c) + for _ in range(lenc): + oc = itc.Next() + omul = itmul.Next() + self.assertIs(oc, omul) + + # Tests + def test_add(self): + c1 = self.create_tcollection() + c2 = self.create_tcollection() + + len1 = c1.GetEntries() + len2 = c2.GetEntries() + + cadd = c1 + c2 + + len_add = cadd.GetEntries() + + self.assertEqual(len_add, len1 + len2) + + # Compare with elements of first collection + itc1 = ROOT.TIter(c1) + itadd = ROOT.TIter(cadd) + for _ in range(len1): + oc1 = itc1.Next() + oadd = itadd.Next() + self.assertIs(oc1, oadd) + + # Compare with elements of second collection + itc2 = ROOT.TIter(c2) + for _ in range(len2): + oc2 = itc2.Next() + oadd = itadd.Next() + self.assertIs(oc2, oadd) + + def test_mul(self): + c = self.create_tcollection() + + cmul = c * self.factor + + self.check_mul_result(c, cmul) + + def test_rmul(self): + c = self.create_tcollection() + + cmul = self.factor * c + + self.check_mul_result(c, cmul) + + def test_imul(self): + c = self.create_tcollection() + lenc = c.GetEntries() + + c *= self.factor + + self.assertEqual(c.GetEntries(), lenc * self.factor) + + it = ROOT.TIter(c) + subc = [] + for _ in range(lenc): + subc.append(it.Next()) + + for _ in range(self.factor - 1): + for o in subc: + self.assertIs(o, it.Next()) + + +class TCollectionIterable(unittest.TestCase): + """ + Test for the pythonization that makes instances of TCollection subclasses + iterable in Python. + For example, this allows to do: + `for elem in collection:` + `...` + """ + + num_elems = 3 + + # Helpers + def create_tcollection(self): + c = ROOT.TList() + for _ in range(self.num_elems): + o = ROOT.TObject() + # Prevent immediate deletion of C++ TObjects + ROOT.SetOwnership(o, False) + c.Add(o) + + return c + + # Tests + def test_iterable(self): + c = self.create_tcollection() + + itc = ROOT.TIter(c) + for elem in c: + self.assertIs(elem, itc.Next()) + + +if __name__ == '__main__': + unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tcollection_iterable.py b/bindings/pyroot/pythonizations/test/tcollection_iterable.py deleted file mode 100644 index 455420aa2bff3..0000000000000 --- a/bindings/pyroot/pythonizations/test/tcollection_iterable.py +++ /dev/null @@ -1,39 +0,0 @@ -import unittest - -import ROOT - - -class TCollectionIterable(unittest.TestCase): - """ - Test for the pythonization that makes instances of TCollection subclasses - iterable in Python. - For example, this allows to do: - `for elem in collection:` - `...` - """ - - num_elems = 3 - - # Helpers - def create_tcollection(self): - c = ROOT.TList() - for _ in range(self.num_elems): - o = ROOT.TObject() - # Prevent immediate deletion of C++ TObjects - ROOT.SetOwnership(o, False) - c.Add(o) - - return c - - # Tests - def test_iterable(self): - c = self.create_tcollection() - - itc = ROOT.TIter(c) - for elem in c: - self.assertIs(elem, itc.Next()) - - -if __name__ == '__main__': - unittest.main() - diff --git a/bindings/pyroot/pythonizations/test/tcollection_len.py b/bindings/pyroot/pythonizations/test/tcollection_len.py deleted file mode 100644 index 7cf2aa995989a..0000000000000 --- a/bindings/pyroot/pythonizations/test/tcollection_len.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest - -import ROOT - - -class TCollectionLen(unittest.TestCase): - """ - Test for the pythonization that allows to access the number of elements of a - TCollection (or subclass) by calling `len` on it. - """ - - num_elems = 3 - tobject_list = [ ROOT.TObject() for _ in range(num_elems) ] - - # Helpers - def add_elems_check_len(self, c): - for elem in self.tobject_list: - c.Add(elem) - - self.assertEqual(len(c), self.num_elems) - self.assertEqual(len(c), c.GetEntries()) - - # Tests - def test_tlist(self): - self.add_elems_check_len(ROOT.TList()) - - def test_tobjarray(self): - self.add_elems_check_len(ROOT.TObjArray()) - - def test_thashtable(self): - self.add_elems_check_len(ROOT.THashTable()) - - -if __name__ == '__main__': - unittest.main() - diff --git a/bindings/pyroot/pythonizations/test/tcollection_listmethods.py b/bindings/pyroot/pythonizations/test/tcollection_listmethods.py deleted file mode 100644 index 7063354a28ce1..0000000000000 --- a/bindings/pyroot/pythonizations/test/tcollection_listmethods.py +++ /dev/null @@ -1,106 +0,0 @@ -import unittest - -import ROOT - - -class TCollectionListMethods(unittest.TestCase): - """ - Test for the Python-list-like methods added to TCollection (and subclasses): - append, remove, extend, count - """ - - num_elems = 3 - - _global_objects = [] - - # Helpers - def create_tcollection(self): - c = ROOT.TList() - for _ in range(self.num_elems): - o = ROOT.TObject() - c.Add(o) - # To prevent deletion of the objects (TList is by default non-owning) - self._global_objects.append(o) - - return c - - # Tests - def test_append(self): - c = self.create_tcollection() - - o = ROOT.TObject() - self.assertFalse(c.Contains(o)) - len1 = c.GetEntries() - - c.append(o) - - len2 = c.GetEntries() - self.assertEqual(len1 + 1, len2) - self.assertTrue(c.Contains(o)) - - # Skip elements that were already there - itc = ROOT.TIter(c) - for _ in range(len1): - itc.Next() - - # Check that `o` is indeed the last element - self.assertIs(o, itc.Next()) - - # Clear before the added element might be garbage collected, - # to avoid dangling pointer access. - c.Clear() - - def test_remove(self): - c = ROOT.TList() - - o1 = ROOT.TObject() - o2 = ROOT.TObject() - - c.Add(o1) - c.Add(o2) - c.Add(o1) - - self.assertTrue(c.Contains(o1)) - self.assertEqual(c.GetEntries(), 3) - - c.remove(o1) - - self.assertEqual(c.GetEntries(), 2) - - c.remove(o1) - - self.assertEqual(c.GetEntries(), 1) - self.assertFalse(c.Contains(o1)) - - with self.assertRaises(ValueError): - c.remove(o1) - - c.Clear() - - def test_extend(self): - c1 = self.create_tcollection() - c2 = self.create_tcollection() - - len1 = c1.GetEntries() - len2 = c2.GetEntries() - - c1.extend(c2) - - len1_final = c1.GetEntries() - - self.assertEqual(len1_final, len1 + len2) - - # Skip elements that were already there - itc1 = ROOT.TIter(c1) - for _ in range(len1): - itc1.Next() - - # Compare with elements of second collection - itc2 = ROOT.TIter(c2) - for _ in range(len2): - self.assertIs(itc1.Next(), itc2.Next()) - - -if __name__ == '__main__': - unittest.main() - diff --git a/bindings/pyroot/pythonizations/test/tcollection_operators.py b/bindings/pyroot/pythonizations/test/tcollection_operators.py deleted file mode 100644 index dadd1c6551b6b..0000000000000 --- a/bindings/pyroot/pythonizations/test/tcollection_operators.py +++ /dev/null @@ -1,102 +0,0 @@ -import unittest - -import ROOT - - -class TCollectionOperators(unittest.TestCase): - """ - Test for the Python operators defined in TCollection (and subclasses): - __add__, __mul__, __rmul__, __imul__ - """ - - num_elems = 3 - factor = 2 - - # Helpers - def create_tcollection(self): - c = ROOT.TList() - for _ in range(self.num_elems): - o = ROOT.TObject() - # Prevent immediate deletion of C++ TObjects - ROOT.SetOwnership(o, False) - c.Add(o) - - return c - - def check_mul_result(self, c, cmul): - lenc = c.GetEntries() - - self.assertEqual(cmul.GetEntries(), lenc * self.factor) - - itmul = ROOT.TIter(cmul) - for _ in range(self.factor): - itc = ROOT.TIter(c) - for _ in range(lenc): - oc = itc.Next() - omul = itmul.Next() - self.assertIs(oc, omul) - - # Tests - def test_add(self): - c1 = self.create_tcollection() - c2 = self.create_tcollection() - - len1 = c1.GetEntries() - len2 = c2.GetEntries() - - cadd = c1 + c2 - - len_add = cadd.GetEntries() - - self.assertEqual(len_add, len1 + len2) - - # Compare with elements of first collection - itc1 = ROOT.TIter(c1) - itadd = ROOT.TIter(cadd) - for _ in range(len1): - oc1 = itc1.Next() - oadd = itadd.Next() - self.assertIs(oc1, oadd) - - # Compare with elements of second collection - itc2 = ROOT.TIter(c2) - for _ in range(len2): - oc2 = itc2.Next() - oadd = itadd.Next() - self.assertIs(oc2, oadd) - - def test_mul(self): - c = self.create_tcollection() - - cmul = c * self.factor - - self.check_mul_result(c, cmul) - - def test_rmul(self): - c = self.create_tcollection() - - cmul = self.factor * c - - self.check_mul_result(c, cmul) - - def test_imul(self): - c = self.create_tcollection() - lenc = c.GetEntries() - - c *= self.factor - - self.assertEqual(c.GetEntries(), lenc * self.factor) - - it = ROOT.TIter(c) - subc = [] - for _ in range(lenc): - subc.append(it.Next()) - - for _ in range(self.factor - 1): - for o in subc: - self.assertIs(o, it.Next()) - - -if __name__ == '__main__': - unittest.main() - diff --git a/bindings/pyroot/pythonizations/test/tcolor.py b/bindings/pyroot/pythonizations/test/tcolor.py index 1c48b7c73cc32..72d92cf6f45dd 100644 --- a/bindings/pyroot/pythonizations/test/tcolor.py +++ b/bindings/pyroot/pythonizations/test/tcolor.py @@ -67,5 +67,43 @@ def test_implicit_tcolor_from_string(self): assert enum_val == lgnd.GetFillColor() +class Regression20018(unittest.TestCase): + """ + Regression test for https://github.com/root-project/root/issues/20018 + + Calling ``TColor.DefinedColors(1)`` puts the global color bookkeeping into + the "always store colors" mode (``gLastDefinedColors = -1``). When a canvas + is afterwards serialized to JSON for the web display - which is exactly what + JupyROOT does to show a canvas inline in a notebook - ``TCanvas::Streamer`` + used to unconditionally call ``fPrimitives->Add(...)`` to attach the list of + colors. During the web-canvas snapshot the pad primitives are temporarily + detached (``fPrimitives`` is set to ``nullptr``, see + ``TWebCanvas::CreatePadSnapshot``), so this dereferenced a null pointer and + crashed with a segmentation violation. + """ + + def test_definedcolors_web_canvas_json(self): + # TWebCanvas (and thus CreateCanvasJSON) is only available when ROOT is + # built with the web display (root7/webgui). Without it, JupyROOT uses a + # different code path that is not affected by this issue. + if not hasattr(ROOT, "TWebCanvas") or not hasattr(ROOT.TWebCanvas, "CreateCanvasJSON"): + self.skipTest("ROOT was built without the web display (webgui)") + + ROOT.gROOT.SetBatch(True) + + # This is what triggers the bug: force the "always store colors" mode. + ROOT.TColor.DefinedColors(1) + + c = ROOT.TCanvas("c_regression_20018", "Basic ROOT Plot", 800, 600) + h = ROOT.TH1F("h_regression_20018", "Example Histogram;X axis;Entries", 100, 0, 10) + h.FillRandom("gaus", 1000) + h.Draw() + c.Update() + + # Used to segfault in TCanvas::Streamer due to a null fPrimitives. + json = ROOT.TWebCanvas.CreateCanvasJSON(c, 23, True) + self.assertGreater(len(json.Data()), 0) + + if __name__ == "__main__": unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tcomplex_operators.py b/bindings/pyroot/pythonizations/test/tcomplex_operators.py deleted file mode 100644 index 4c1f5049fe1f9..0000000000000 --- a/bindings/pyroot/pythonizations/test/tcomplex_operators.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest -import ROOT -from ROOT import TComplex - -class TestTComplexOperators(unittest.TestCase): - """ - Test for the operators of TComplex: - __radd__, __rsub__, __rmul__, __rtruediv__/__rdiv__. - """ - - c = TComplex(4.,0) - - d = 2. - - s = 'string' - - # check the expected result for d + c and that Re(c + d) == Re(d + c) - def test_radd(self): - self.assertEqual((self.d + self.c).Re(), 6.0) - self.assertEqual((self.c + self.d).Re(), (self.d + self.c).Re()) - - # check the expected result for d - c and that Re(c - d) == -Re(d - c) - def test_rsub(self): - self.assertEqual((self.d - self.c).Re(), -2.0) - self.assertEqual((self.c - self.d).Re(), -((self.d - self.c).Re())) - - # check the expected result for d * c and that Re(c * d) == Re(d * c) - def test_rmul(self): - self.assertEqual((self.d * self.c).Re(), 8.0) - self.assertEqual((self.c * self.d).Re(), (self.d * self.c).Re()) - - # check the expected result for d / c - def test_rdiv(self): - self.assertEqual((self.d / self.c).Re(), 0.5) - with self.assertRaises(TypeError): - self.s / self.c - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tcontext_contextmanager.py b/bindings/pyroot/pythonizations/test/tcontext_contextmanager.py deleted file mode 100644 index b442055ec746e..0000000000000 --- a/bindings/pyroot/pythonizations/test/tcontext_contextmanager.py +++ /dev/null @@ -1,82 +0,0 @@ -import os -import unittest - -import ROOT - -from ROOT import TDirectory - - -class TContextContextManager(unittest.TestCase): - """ - Test of TContext used as context manager - """ - - def default_constructor(self): - """ - Check status of gDirectory with default constructor. - """ - filename = "TContextContextManager_test_default_constructor.root" - self.assertEqual(ROOT.gDirectory, ROOT.gROOT) - - with TDirectory.TContext(): - # Create a file to change gDirectory - testfile = ROOT.TFile(filename, "recreate") - self.assertEqual(ROOT.gDirectory, testfile) - testfile.Close() - - self.assertEqual(ROOT.gDirectory, ROOT.gROOT) - os.remove(filename) - - def constructor_onearg(self): - """ - Check status of gDirectory with constructor taking a new directory. - """ - filenames = ["TContextContextManager_test_constructor_onearg_{}.root".format(i) for i in range(2)] - - file0 = ROOT.TFile(filenames[0], "recreate") - file1 = ROOT.TFile(filenames[1], "recreate") - self.assertEqual(ROOT.gDirectory, file1) - - with TDirectory.TContext(file0): - self.assertEqual(ROOT.gDirectory, file0) - - self.assertEqual(ROOT.gDirectory, file1) - file0.Close() - file1.Close() - for filename in filenames: - os.remove(filename) - - def constructor_twoargs(self): - """ - Check status of gDirectory with constructor taking the previous directory and a new one. - """ - filenames = ["TContextContextManager_test_constructor_onearg_{}.root".format(i) for i in range(3)] - - file0 = ROOT.TFile(filenames[0], "recreate") - file1 = ROOT.TFile(filenames[1], "recreate") - file2 = ROOT.TFile(filenames[2], "recreate") - self.assertEqual(ROOT.gDirectory, file2) - - with TDirectory.TContext(file0, file1): - self.assertEqual(ROOT.gDirectory, file1) - - self.assertEqual(ROOT.gDirectory, file0) - file0.Close() - file1.Close() - file2.Close() - for filename in filenames: - os.remove(filename) - - def test_all(self): - """ - Run all tests of this class sequentially. - The tests of this class rely on the current directory, which can be changed - unpredictably if they are run concurrently. - """ - self.default_constructor() - self.constructor_onearg() - self.constructor_twoargs() - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tdirectory_attrsyntax.py b/bindings/pyroot/pythonizations/test/tdirectory_attrsyntax.py deleted file mode 100644 index 7687d9e9c373b..0000000000000 --- a/bindings/pyroot/pythonizations/test/tdirectory_attrsyntax.py +++ /dev/null @@ -1,58 +0,0 @@ -import unittest - -import ROOT - - -class TDirectoryReadWrite(unittest.TestCase): - """ - Test for the getitem syntax of TDirectory. - """ - - nbins = 8 - xmin = 0 - xmax = 4 - - # Setup - @classmethod - def setUpClass(cls): - cls.dir0 = ROOT.TDirectory("dir0", "dir0") - h = ROOT.TH1F("h", "h", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h, False) - # this must be there otherwise the histogram is not attached to dir0 - h.SetDirectory(cls.dir0) - - dir1 = cls.dir0.mkdir("dir1") - dir1.cd() - h1 = ROOT.TH1F("h1", "h1", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h1, False) - h1.SetDirectory(dir1) - - dir2 = dir1.mkdir("dir2") - dir2.cd() - h2 = ROOT.TH1F("h2", "h2", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h2, False) - h2.SetDirectory(dir2) - - def checkHisto(self, h): - xaxis = h.GetXaxis() - self.assertEqual(self.nbins, h.GetNbinsX()) - self.assertEqual(self.xmin, xaxis.GetXmin()) - self.assertEqual(self.xmax, xaxis.GetXmax()) - - # Tests - def test_readHisto_itemsyntax(self): - self.checkHisto(self.dir0["h"]) - self.checkHisto(self.dir0["dir1"]["h1"]) - self.checkHisto(self.dir0["dir1"]["dir2"]["h2"]) - - def test_caching_getitem(self): - # check that object is not cached initially - self.assertFalse("h" in self.dir0.__dict__) - self.dir0["h"] - # check that the cached value in is actually the object - # inside the directory - self.assertTrue(self.dir0._cached_items["h"] is self.dir0["h"]) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tdirectory_tfile.py b/bindings/pyroot/pythonizations/test/tdirectory_tfile.py new file mode 100644 index 0000000000000..64ec169f7881d --- /dev/null +++ b/bindings/pyroot/pythonizations/test/tdirectory_tfile.py @@ -0,0 +1,315 @@ +import os +import unittest + +import ROOT +from ROOT import TFile + + +class TDirectoryReadWrite(unittest.TestCase): + """ + Test for the getitem syntax of TDirectory. + """ + + nbins = 8 + xmin = 0 + xmax = 4 + + # Setup + @classmethod + def setUpClass(cls): + cls.dir0 = ROOT.TDirectory("dir0", "dir0") + h = ROOT.TH1F("h", "h", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h, False) + # this must be there otherwise the histogram is not attached to dir0 + h.SetDirectory(cls.dir0) + + dir1 = cls.dir0.mkdir("dir1") + dir1.cd() + h1 = ROOT.TH1F("h1", "h1", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h1, False) + h1.SetDirectory(dir1) + + dir2 = dir1.mkdir("dir2") + dir2.cd() + h2 = ROOT.TH1F("h2", "h2", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h2, False) + h2.SetDirectory(dir2) + + @classmethod + def tearDownClass(cls): + # Release the directory (and the histograms it owns) now, while ROOT is + # fully initialised, instead of leaving it to interpreter shutdown, + # where we risk colliding with other directory objects of the same name + # from other tests. + cls.dir0 = None + + def checkHisto(self, h): + xaxis = h.GetXaxis() + self.assertEqual(self.nbins, h.GetNbinsX()) + self.assertEqual(self.xmin, xaxis.GetXmin()) + self.assertEqual(self.xmax, xaxis.GetXmax()) + + # Tests + def test_readHisto_itemsyntax(self): + self.checkHisto(self.dir0["h"]) + self.checkHisto(self.dir0["dir1"]["h1"]) + self.checkHisto(self.dir0["dir1"]["dir2"]["h2"]) + + def test_caching_getitem(self): + # check that object is not cached initially + self.assertFalse("h" in self.dir0.__dict__) + self.dir0["h"] + # check that the cached value in is actually the object + # inside the directory + self.assertTrue(self.dir0._cached_items["h"] is self.dir0["h"]) + + +class TDirectoryFileReadWrite(unittest.TestCase): + """ + Test for the getitem syntax and Get method of TDirectoryFile. + """ + + nbins = 8 + xmin = 0 + xmax = 4 + + # Setup + @classmethod + def setUpClass(cls): + cls.dir0 = ROOT.TDirectoryFile("dir0", "dir0") + h = ROOT.TH1F("h", "h", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h, False) + # this must be there otherwise the histogram is not attached to dir0 + h.SetDirectory(cls.dir0) + + dir1 = cls.dir0.mkdir("dir1") + dir1.cd() + h1 = ROOT.TH1F("h1", "h1", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h1, False) + h1.SetDirectory(dir1) + + dir2 = dir1.mkdir("dir2") + dir2.cd() + h2 = ROOT.TH1F("h2", "h2", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h2, False) + h2.SetDirectory(dir2) + + @classmethod + def tearDownClass(cls): + # Release the directory (and the histograms it owns) now, while ROOT is + # fully initialised, instead of leaving it to interpreter shutdown, + # where we risk colliding with other directory objects of the same name + # from other tests. + cls.dir0 = None + + def checkHisto(self, h): + xaxis = h.GetXaxis() + self.assertEqual(self.nbins, h.GetNbinsX()) + self.assertEqual(self.xmin, xaxis.GetXmin()) + self.assertEqual(self.xmax, xaxis.GetXmax()) + + # Tests + def test_readHisto_itemsyntax(self): + self.checkHisto(self.dir0["h"]) + self.checkHisto(self.dir0["dir1"]["h1"]) + self.checkHisto(self.dir0["dir1"]["dir2"]["h2"]) + + def test_readHisto(self): + self.checkHisto(self.dir0.Get("h")) + self.checkHisto(self.dir0.Get("dir1/h1")) + self.checkHisto(self.dir0.Get("dir1/dir2/h2")) + + def test_caching_getitem(self): + # check that object is not cached initially + self.assertFalse(hasattr(self.dir0, "_cached_items")) + self.dir0["h"] + # check that the value in __dict__ is actually the object + # inside the directory + self.assertTrue(self.dir0._cached_items['h'] is self.dir0["h"]) + + +class TFileOpenReadWrite(unittest.TestCase): + """ + Test for the TFile.Open factory like creation of TFile + """ + + filename = 'tfileopenreadwrite.root' + nbins = 8 + xmin = 0 + xmax = 4 + + # Setup + @classmethod + def setUpClass(cls): + f = ROOT.TFile.Open(cls.filename, "RECREATE") + h = ROOT.TH1F("h", "h", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h, False) + f.WriteObject(h, "h") + + dir1 = f.mkdir("dir1") + dir1.cd() + h1 = ROOT.TH1F("h1", "h1", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h1, False) + h1.Write() + + dir2 = dir1.mkdir("dir2") + dir2.cd() + h2 = ROOT.TH1F("h2", "h2", cls.nbins, cls.xmin, cls.xmax) + ROOT.SetOwnership(h2, False) + h2.Write() + + f.Close() + + def checkHisto(self, h): + xaxis = h.GetXaxis() + self.assertEqual(self.nbins, h.GetNbinsX()) + self.assertEqual(self.xmin, xaxis.GetXmin()) + self.assertEqual(self.xmax, xaxis.GetXmax()) + + # Tests + def test_readHisto_itemsyntax(self): + f = ROOT.TFile.Open(self.filename) + self.checkHisto(f["h"]) + self.checkHisto(f["dir1"]["h1"]) + self.checkHisto(f["dir1"]["dir2"]["h2"]) + + def test_readHisto(self): + f = ROOT.TFile.Open(self.filename) + self.checkHisto(f.Get("h")) + self.checkHisto(f.Get("dir1/h1")) + self.checkHisto(f.Get("dir1/dir2/h2")) + + def test_caching_getitem(self): + f = ROOT.TFile.Open(self.filename) + # check that object is not cached initially + self.assertFalse(hasattr(f, "_cached_items")) + f["h"] + # check that the value in __dict__ is actually the object + # inside the directory + self.assertTrue(f._cached_items['h'] is f["h"]) + + def test_oserror(self): + # check that an OSError is raised when an inexistent file is opened + # both with a string and an instance of TFileOpenHandle as arguments + self.assertRaises(OSError, ROOT.TFile.Open, 'inexistent_file.root') + handle = ROOT.TFile.AsyncOpen("inexistent_file.root") + self.assertRaises(OSError, ROOT.TFile.Open, handle) + + def test_keys_title(self): + """ + Test that the TKey related to a histogram in the file contains the + histogram title as described in #9989. + """ + finput = ROOT.TFile.Open(self.filename) + key1 = finput.GetListOfKeys().At(0) + key2 = finput.Get("dir1").GetListOfKeys().At(0) + key3 = finput.Get("dir1/dir2").GetListOfKeys().At(0) + self.assertEqual(key1.GetTitle(), "h") + self.assertEqual(key2.GetTitle(), "h1") + self.assertEqual(key3.GetTitle(), "h2") + finput.Close() + + +class TFileConstructor(unittest.TestCase): + """ + Test for the TFile constructor + """ + + def test_oserror(self): + # check that an OSError is raised when the string passed as argument + # refers to an inexistent file + self.assertRaises(OSError, ROOT.TFile, 'inexistent_file.root') + + +class TFileContextManager(unittest.TestCase): + """ + Test of TFile used as context manager + """ + + NBINS = 123 + XMIN = 10 + XMAX = 242 + + def check_file_data(self, tfile, filename, histoname): + """ + Check status of the TFile after the context manager and correctness of + the data it contains. + """ + self.assertTrue(tfile) # The TFile object is still there + self.assertFalse(tfile.IsOpen()) # And it is correctly closed + + with TFile(filename, "read") as infile: + hin = infile.Get(histoname) + xaxis = hin.GetXaxis() + self.assertEqual(self.NBINS, hin.GetNbinsX()) + self.assertEqual(self.XMIN, xaxis.GetXmin()) + self.assertEqual(self.XMAX, xaxis.GetXmax()) + + os.remove(filename) + + def test_writeobject(self): + """ + Write a histogram in a file within a context manager, using TDirectory::WriteObject. + """ + filename = "TFileContextManager_test_writeobject.root" + histoname = "myhisto" + with TFile(filename, "recreate") as outfile: + hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) + outfile.WriteObject(hout, "myhisto") + + self.check_file_data(outfile, filename, histoname) + + def test_histowrite(self): + """ + Write a histogram in a file within a context manager, using TH1::Write. + """ + filename = "TFileContextManager_test_histowrite.root" + histoname = "myhisto_2" + with TFile(filename, "recreate") as outfile: + hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) + hout.Write() + + self.check_file_data(outfile, filename, histoname) + + def test_filewrite(self): + """ + Write a histogram in a file within a context manager, using TFile::Write. + """ + filename = "TFileContextManager_test_filewrite.root" + histoname = "myhisto_3" + with TFile(filename, "recreate") as outfile: + hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) + hout.SetDirectory(outfile) + outfile.Write() + + self.check_file_data(outfile, filename, histoname) + + def test_detachhisto(self): + """ + Detach histogram from file and access it outside of the context, both when writing and reading. + """ + filename = "TFileContextManager_test_detachhisto.root" + with TFile(filename, "recreate") as outfile: + hout = ROOT.TH1F("myhisto", "myhisto", self.NBINS, self.XMIN, self.XMAX) + hout.SetDirectory(ROOT.nullptr) + outfile.WriteObject(hout, "myhisto") + + self.assertTrue(hout) + self.assertEqual(hout.GetName(), "myhisto") + + with TFile(filename, "read") as infile: + hin = infile.Get("myhisto") + hin.SetDirectory(ROOT.nullptr) + xaxis = hin.GetXaxis() + self.assertEqual(self.NBINS, hin.GetNbinsX()) + self.assertEqual(self.XMIN, xaxis.GetXmin()) + self.assertEqual(self.XMAX, xaxis.GetXmax()) + + self.assertTrue(hin) + self.assertEqual(hin.GetName(), "myhisto") + + os.remove(filename) + + +if __name__ == '__main__': + unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tdirectoryfile_attrsyntax_get.py b/bindings/pyroot/pythonizations/test/tdirectoryfile_attrsyntax_get.py deleted file mode 100644 index e4ba206ba2d50..0000000000000 --- a/bindings/pyroot/pythonizations/test/tdirectoryfile_attrsyntax_get.py +++ /dev/null @@ -1,63 +0,0 @@ -import unittest - -import ROOT - - -class TDirectoryFileReadWrite(unittest.TestCase): - """ - Test for the getitem syntax and Get method of TDirectoryFile. - """ - - nbins = 8 - xmin = 0 - xmax = 4 - - # Setup - @classmethod - def setUpClass(cls): - cls.dir0 = ROOT.TDirectoryFile("dir0", "dir0") - h = ROOT.TH1F("h", "h", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h, False) - # this must be there otherwise the histogram is not attached to dir0 - h.SetDirectory(cls.dir0) - - dir1 = cls.dir0.mkdir("dir1") - dir1.cd() - h1 = ROOT.TH1F("h1", "h1", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h1, False) - h1.SetDirectory(dir1) - - dir2 = dir1.mkdir("dir2") - dir2.cd() - h2 = ROOT.TH1F("h2", "h2", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h2, False) - h2.SetDirectory(dir2) - - def checkHisto(self, h): - xaxis = h.GetXaxis() - self.assertEqual(self.nbins, h.GetNbinsX()) - self.assertEqual(self.xmin, xaxis.GetXmin()) - self.assertEqual(self.xmax, xaxis.GetXmax()) - - # Tests - def test_readHisto_itemsyntax(self): - self.checkHisto(self.dir0["h"]) - self.checkHisto(self.dir0["dir1"]["h1"]) - self.checkHisto(self.dir0["dir1"]["dir2"]["h2"]) - - def test_readHisto(self): - self.checkHisto(self.dir0.Get("h")) - self.checkHisto(self.dir0.Get("dir1/h1")) - self.checkHisto(self.dir0.Get("dir1/dir2/h2")) - - def test_caching_getitem(self): - # check that object is not cached initially - self.assertFalse(hasattr(self.dir0, "_cached_items")) - self.dir0["h"] - # check that the value in __dict__ is actually the object - # inside the directory - self.assertTrue(self.dir0._cached_items['h'] is self.dir0["h"]) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tfile_attrsyntax_get_writeobject_open.py b/bindings/pyroot/pythonizations/test/tfile_attrsyntax_get_writeobject_open.py deleted file mode 100644 index 1ebd643643239..0000000000000 --- a/bindings/pyroot/pythonizations/test/tfile_attrsyntax_get_writeobject_open.py +++ /dev/null @@ -1,89 +0,0 @@ -import unittest - -import ROOT - - -class TFileOpenReadWrite(unittest.TestCase): - """ - Test for the TFile.Open factory like creation of TFile - """ - - filename = 'tfileopenreadwrite.root' - nbins = 8 - xmin = 0 - xmax = 4 - - # Setup - @classmethod - def setUpClass(cls): - f = ROOT.TFile.Open(cls.filename, "RECREATE") - h = ROOT.TH1F("h", "h", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h, False) - f.WriteObject(h, "h") - - dir1 = f.mkdir("dir1") - dir1.cd() - h1 = ROOT.TH1F("h1", "h1", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h1, False) - h1.Write() - - dir2 = dir1.mkdir("dir2") - dir2.cd() - h2 = ROOT.TH1F("h2", "h2", cls.nbins, cls.xmin, cls.xmax) - ROOT.SetOwnership(h2, False) - h2.Write() - - f.Close() - - def checkHisto(self, h): - xaxis = h.GetXaxis() - self.assertEqual(self.nbins, h.GetNbinsX()) - self.assertEqual(self.xmin, xaxis.GetXmin()) - self.assertEqual(self.xmax, xaxis.GetXmax()) - - # Tests - def test_readHisto_itemsyntax(self): - f = ROOT.TFile.Open(self.filename) - self.checkHisto(f["h"]) - self.checkHisto(f["dir1"]["h1"]) - self.checkHisto(f["dir1"]["dir2"]["h2"]) - - def test_readHisto(self): - f = ROOT.TFile.Open(self.filename) - self.checkHisto(f.Get("h")) - self.checkHisto(f.Get("dir1/h1")) - self.checkHisto(f.Get("dir1/dir2/h2")) - - def test_caching_getitem(self): - f = ROOT.TFile.Open(self.filename) - # check that object is not cached initially - self.assertFalse(hasattr(f, "_cached_items")) - f["h"] - # check that the value in __dict__ is actually the object - # inside the directory - self.assertTrue(f._cached_items['h'] is f["h"]) - - def test_oserror(self): - # check that an OSError is raised when an inexistent file is opened - # both with a string and an instance of TFileOpenHandle as arguments - self.assertRaises(OSError, ROOT.TFile.Open, 'inexistent_file.root') - handle = ROOT.TFile.AsyncOpen("inexistent_file.root") - self.assertRaises(OSError, ROOT.TFile.Open, handle) - - def test_keys_title(self): - """ - Test that the TKey related to a histogram in the file contains the - histogram title as described in #9989. - """ - finput = ROOT.TFile.Open(self.filename) - key1 = finput.GetListOfKeys().At(0) - key2 = finput.Get("dir1").GetListOfKeys().At(0) - key3 = finput.Get("dir1/dir2").GetListOfKeys().At(0) - self.assertEqual(key1.GetTitle(), "h") - self.assertEqual(key2.GetTitle(), "h1") - self.assertEqual(key3.GetTitle(), "h2") - finput.Close() - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tfile_constructor.py b/bindings/pyroot/pythonizations/test/tfile_constructor.py deleted file mode 100644 index 52f828b7d21fb..0000000000000 --- a/bindings/pyroot/pythonizations/test/tfile_constructor.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest - -import ROOT - - -class TFileConstructor(unittest.TestCase): - """ - Test for the TFile constructor - """ - - def test_oserror(self): - # check that an OSError is raised when the string passed as argument - # refers to an inexistent file - self.assertRaises(OSError, ROOT.TFile, 'inexistent_file.root') - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tfile_context_manager.py b/bindings/pyroot/pythonizations/test/tfile_context_manager.py deleted file mode 100644 index 57a3fba2796c3..0000000000000 --- a/bindings/pyroot/pythonizations/test/tfile_context_manager.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import unittest - -import ROOT -from ROOT import TFile - - -class TFileContextManager(unittest.TestCase): - """ - Test of TFile used as context manager - """ - - NBINS = 123 - XMIN = 10 - XMAX = 242 - - def check_file_data(self, tfile, filename, histoname): - """ - Check status of the TFile after the context manager and correctness of - the data it contains. - """ - self.assertTrue(tfile) # The TFile object is still there - self.assertFalse(tfile.IsOpen()) # And it is correctly closed - - with TFile(filename, "read") as infile: - hin = infile.Get(histoname) - xaxis = hin.GetXaxis() - self.assertEqual(self.NBINS, hin.GetNbinsX()) - self.assertEqual(self.XMIN, xaxis.GetXmin()) - self.assertEqual(self.XMAX, xaxis.GetXmax()) - - os.remove(filename) - - def test_writeobject(self): - """ - Write a histogram in a file within a context manager, using TDirectory::WriteObject. - """ - filename = "TFileContextManager_test_writeobject.root" - histoname = "myhisto" - with TFile(filename, "recreate") as outfile: - hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) - outfile.WriteObject(hout, "myhisto") - - self.check_file_data(outfile, filename, histoname) - - def test_histowrite(self): - """ - Write a histogram in a file within a context manager, using TH1::Write. - """ - filename = "TFileContextManager_test_histowrite.root" - histoname = "myhisto_2" - with TFile(filename, "recreate") as outfile: - hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) - hout.Write() - - self.check_file_data(outfile, filename, histoname) - - def test_filewrite(self): - """ - Write a histogram in a file within a context manager, using TFile::Write. - """ - filename = "TFileContextManager_test_filewrite.root" - histoname = "myhisto_3" - with TFile(filename, "recreate") as outfile: - hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) - hout.SetDirectory(outfile) - outfile.Write() - - self.check_file_data(outfile, filename, histoname) - - def test_detachhisto(self): - """ - Detach histogram from file and access it outside of the context, both when writing and reading. - """ - filename = "TFileContextManager_test_detachhisto.root" - with TFile(filename, "recreate") as outfile: - hout = ROOT.TH1F("myhisto", "myhisto", self.NBINS, self.XMIN, self.XMAX) - hout.SetDirectory(ROOT.nullptr) - outfile.WriteObject(hout, "myhisto") - - self.assertTrue(hout) - self.assertEqual(hout.GetName(), "myhisto") - - with TFile(filename, "read") as infile: - hin = infile.Get("myhisto") - hin.SetDirectory(ROOT.nullptr) - xaxis = hin.GetXaxis() - self.assertEqual(self.NBINS, hin.GetNbinsX()) - self.assertEqual(self.XMIN, xaxis.GetXmin()) - self.assertEqual(self.XMAX, xaxis.GetXmax()) - - self.assertTrue(hin) - self.assertEqual(hin.GetName(), "myhisto") - - os.remove(filename) - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/th1_addfunction.py b/bindings/pyroot/pythonizations/test/th1_addfunction.py deleted file mode 100644 index 1764ce9acda5a..0000000000000 --- a/bindings/pyroot/pythonizations/test/th1_addfunction.py +++ /dev/null @@ -1,17 +0,0 @@ -import unittest - -import ROOT - - -class TH1AddFunction(unittest.TestCase): - def test_adding_a_function_does_not_segfault(self): - """ - This test verifies that box is only freed once - """ - h = ROOT.TH1F("h", "h", 10, 0, 1) - box = ROOT.TBox(0.1, 0.1, 0.9, 0.9) - h.GetListOfFunctions().Add(box) - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/th3.py b/bindings/pyroot/pythonizations/test/th3.py deleted file mode 100644 index 6e5672070b9ce..0000000000000 --- a/bindings/pyroot/pythonizations/test/th3.py +++ /dev/null @@ -1,44 +0,0 @@ -import unittest - -import ROOT - - -class TH3Operations(unittest.TestCase): - """ - Test TH3D arithmetic operations - """ - - def setUp(self): - self.h1 = ROOT.TH3D("h1", "h1", 2, 0, 2, 2, 0, 2, 2, 0, 2) - self.h2 = ROOT.TH3D("h2", "h2", 2, 0, 2, 2, 0, 2, 2, 0, 2) - - self.h1.Fill(0.5, 0.5, 0.5, 2.0) - self.h2.Fill(0.5, 0.5, 0.5, 3.0) - - def test_addition(self): - hsum = self.h1 + self.h2 - self.assertAlmostEqual(hsum.GetBinContent(1, 1, 1), 5.0) - - def test_subtraction(self): - hdiff = self.h2 - self.h1 - self.assertAlmostEqual(hdiff.GetBinContent(1, 1, 1), 1.0) - - def test_multiplication(self): - hprod = self.h1 * self.h2 - self.assertAlmostEqual(hprod.GetBinContent(1, 1, 1), 6.0) - - def test_division(self): - hdiv = self.h2 / self.h1 - self.assertAlmostEqual(hdiv.GetBinContent(1, 1, 1), 1.5) - - def test_scalar_multiplication_left(self): - hscaled = 2.0 * self.h1 - self.assertAlmostEqual(hscaled.GetBinContent(1, 1, 1), 4.0) - - def test_scalar_multiplication_right(self): - hscaled = self.h1 * 2.0 - self.assertAlmostEqual(hscaled.GetBinContent(1, 1, 1), 4.0) - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/th2.py b/bindings/pyroot/pythonizations/test/thist.py similarity index 63% rename from bindings/pyroot/pythonizations/test/th2.py rename to bindings/pyroot/pythonizations/test/thist.py index 102cd1086e817..3d1d0f381abfb 100644 --- a/bindings/pyroot/pythonizations/test/th2.py +++ b/bindings/pyroot/pythonizations/test/thist.py @@ -3,6 +3,16 @@ import ROOT +class TH1AddFunction(unittest.TestCase): + def test_adding_a_function_does_not_segfault(self): + """ + This test verifies that box is only freed once + """ + h = ROOT.TH1F("h", "h", 10, 0, 1) + box = ROOT.TBox(0.1, 0.1, 0.9, 0.9) + h.GetListOfFunctions().Add(box) + + class TH2UsingDecls(unittest.TestCase): """ Test that the method pulled in via using decls from TH1 are accessible @@ -91,5 +101,42 @@ def add_bins(): h2p.Fill(0.5, 1.5, 3) +class TH3Operations(unittest.TestCase): + """ + Test TH3D arithmetic operations + """ + + def setUp(self): + self.h1 = ROOT.TH3D("h1", "h1", 2, 0, 2, 2, 0, 2, 2, 0, 2) + self.h2 = ROOT.TH3D("h2", "h2", 2, 0, 2, 2, 0, 2, 2, 0, 2) + + self.h1.Fill(0.5, 0.5, 0.5, 2.0) + self.h2.Fill(0.5, 0.5, 0.5, 3.0) + + def test_addition(self): + hsum = self.h1 + self.h2 + self.assertAlmostEqual(hsum.GetBinContent(1, 1, 1), 5.0) + + def test_subtraction(self): + hdiff = self.h2 - self.h1 + self.assertAlmostEqual(hdiff.GetBinContent(1, 1, 1), 1.0) + + def test_multiplication(self): + hprod = self.h1 * self.h2 + self.assertAlmostEqual(hprod.GetBinContent(1, 1, 1), 6.0) + + def test_division(self): + hdiv = self.h2 / self.h1 + self.assertAlmostEqual(hdiv.GetBinContent(1, 1, 1), 1.5) + + def test_scalar_multiplication_left(self): + hscaled = 2.0 * self.h1 + self.assertAlmostEqual(hscaled.GetBinContent(1, 1, 1), 4.0) + + def test_scalar_multiplication_right(self): + hscaled = self.h1 * 2.0 + self.assertAlmostEqual(hscaled.GetBinContent(1, 1, 1), 4.0) + + if __name__ == "__main__": unittest.main() diff --git a/bindings/pyroot/pythonizations/test/titer_iterator.py b/bindings/pyroot/pythonizations/test/titer_iterator.py deleted file mode 100644 index 57dcb74db67a6..0000000000000 --- a/bindings/pyroot/pythonizations/test/titer_iterator.py +++ /dev/null @@ -1,54 +0,0 @@ -import unittest - -import ROOT - - -class TIterIterator(unittest.TestCase): - """ - Test for the pythonization that allows instances of TIter to - behave as Python iterators. - """ - - num_elems = 3 - - # Helpers - def create_tcollection(self): - c = ROOT.TList() - for _ in range(self.num_elems): - o = ROOT.TObject() - # Prevent immediate deletion of C++ TObjects - ROOT.SetOwnership(o, False) - c.Add(o) - - return c - - # Tests - def test_iterable(self): - # Check that TIter instances are iterable - c = self.create_tcollection() - - itc = ROOT.TIter(c) - # An iterator of an iterator is itself - self.assertEqual(itc, iter(itc)) - - def test_iterator(self): - # Check that TIter instances are iterators - c = self.create_tcollection() - - itc1 = ROOT.TIter(c) - itc2 = ROOT.TIter(c) - for _ in range(c.GetEntries()): - self.assertIs(next(itc1), itc2.Next()) - - def test_for_loop_syntax(self): - # Somehow redundant, but good to test with real syntax - c = self.create_tcollection() - - itc1 = ROOT.TIter(c) - itc2 = ROOT.TIter(c) - for elem1, elem2 in zip(itc1, itc2): - self.assertIs(elem1, elem2) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tobject_comparisonops.py b/bindings/pyroot/pythonizations/test/tobject.py similarity index 76% rename from bindings/pyroot/pythonizations/test/tobject_comparisonops.py rename to bindings/pyroot/pythonizations/test/tobject.py index 4f39f8ea90d67..c923e2b60c8b3 100644 --- a/bindings/pyroot/pythonizations/test/tobject_comparisonops.py +++ b/bindings/pyroot/pythonizations/test/tobject.py @@ -4,6 +4,42 @@ from ROOT import TUrl +class TObjectContains(unittest.TestCase): + """ + Test for the __contains__ pythonization of TObject and subclasses. + Such pythonization relies on TObject::FindObject, which is redefined + in some of its subclasses, such as TCollection. + Thanks to this pythonization, we can use the syntax `obj in col` + to know if col contains obj. + """ + + num_elems = 3 + + # Helpers + def create_tlist(self): + l = ROOT.TList() + for _ in range(self.num_elems): + o = ROOT.TObject() + # Prevent immediate deletion of C++ TObjects + ROOT.SetOwnership(o, False) + l.Add(o) + + return l + + # Tests + def test_contains(self): + l = self.create_tlist() + + for elem in l: + self.assertTrue(elem in l) + # Make sure it does not work just because of __iter__ + self.assertTrue(l.__contains__(elem)) + + o = ROOT.TObject() + self.assertFalse(o in l) + self.assertFalse(l.__contains__(o)) + + class TObjectComparisonOps(unittest.TestCase): """ Test for the comparison operators of TObject and subclasses: diff --git a/bindings/pyroot/pythonizations/test/tobject_contains.py b/bindings/pyroot/pythonizations/test/tobject_contains.py deleted file mode 100644 index 46a7032c6ec18..0000000000000 --- a/bindings/pyroot/pythonizations/test/tobject_contains.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest - -import ROOT - - -class TObjectContains(unittest.TestCase): - """ - Test for the __contains__ pythonization of TObject and subclasses. - Such pythonization relies on TObject::FindObject, which is redefined - in some of its subclasses, such as TCollection. - Thanks to this pythonization, we can use the syntax `obj in col` - to know if col contains obj. - """ - - num_elems = 3 - - # Helpers - def create_tlist(self): - l = ROOT.TList() - for _ in range(self.num_elems): - o = ROOT.TObject() - # Prevent immediate deletion of C++ TObjects - ROOT.SetOwnership(o, False) - l.Add(o) - - return l - - # Tests - def test_contains(self): - l = self.create_tlist() - - for elem in l: - self.assertTrue(elem in l) - # Make sure it does not work just because of __iter__ - self.assertTrue(l.__contains__(elem)) - - o = ROOT.TObject() - self.assertFalse(o in l) - self.assertFalse(l.__contains__(o)) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tobjstring_comparisonops.py b/bindings/pyroot/pythonizations/test/tobjstring.py similarity index 85% rename from bindings/pyroot/pythonizations/test/tobjstring_comparisonops.py rename to bindings/pyroot/pythonizations/test/tobjstring.py index 7d34d6f113f4b..d789afa78b814 100644 --- a/bindings/pyroot/pythonizations/test/tobjstring_comparisonops.py +++ b/bindings/pyroot/pythonizations/test/tobjstring.py @@ -1,7 +1,40 @@ import unittest +import ROOT from ROOT import TObjString, TString + +class TObjStringLen(unittest.TestCase): + """ + Test for the pythonization that provides the length of a + TObjString instance `s` via `len(s)`. + """ + + # Tests + def test_len(self): + s = 'test' + tos = ROOT.TObjString(s) + self.assertEqual(len(tos), len(s)) + + +class TObjStringStrRepr(unittest.TestCase): + """ + Test for the pythonizations that provide a string representation + for instances of TObjString (__str__, __repr__). + """ + + # Tests + def test_str(self): + s = 'test' + tos = ROOT.TObjString(s) + self.assertEqual(str(tos), s) + + def test_repr(self): + s = 'test' + tos = ROOT.TObjString(s) + self.assertEqual(repr(tos), repr(s)) + + class TObjStringComparisonOps(unittest.TestCase): """ Test for the comparison operators of TObjString: diff --git a/bindings/pyroot/pythonizations/test/tobjstring_len.py b/bindings/pyroot/pythonizations/test/tobjstring_len.py deleted file mode 100644 index af9e2021a9bf9..0000000000000 --- a/bindings/pyroot/pythonizations/test/tobjstring_len.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -import ROOT - - -class TObjStringLen(unittest.TestCase): - """ - Test for the pythonization that provides the length of a - TObjString instance `s` via `len(s)`. - """ - - # Tests - def test_len(self): - s = 'test' - tos = ROOT.TObjString(s) - self.assertEqual(len(tos), len(s)) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tobjstring_str_repr.py b/bindings/pyroot/pythonizations/test/tobjstring_str_repr.py deleted file mode 100644 index b489b5d587e72..0000000000000 --- a/bindings/pyroot/pythonizations/test/tobjstring_str_repr.py +++ /dev/null @@ -1,25 +0,0 @@ -import unittest - -import ROOT - - -class TObjStringStrRepr(unittest.TestCase): - """ - Test for the pythonizations that provide a string representation - for instances of TObjString (__str__, __repr__). - """ - - # Tests - def test_str(self): - s = 'test' - tos = ROOT.TObjString(s) - self.assertEqual(str(tos), s) - - def test_repr(self): - s = 'test' - tos = ROOT.TObjString(s) - self.assertEqual(repr(tos), repr(s)) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tseqcollection_itemaccess.py b/bindings/pyroot/pythonizations/test/tseqcollection.py similarity index 61% rename from bindings/pyroot/pythonizations/test/tseqcollection_itemaccess.py rename to bindings/pyroot/pythonizations/test/tseqcollection.py index 15e412a02bf3a..31a889e8e8e9e 100644 --- a/bindings/pyroot/pythonizations/test/tseqcollection_itemaccess.py +++ b/bindings/pyroot/pythonizations/test/tseqcollection.py @@ -286,5 +286,165 @@ def test_delitem_slice(self): sc6[::0] + +class TSeqCollectionListMethods(unittest.TestCase): + """ + Test for the Python-list-like methods added to TSeqCollection + (and subclasses): insert, pop, reverse, sort, index + """ + + num_elems = 3 + + _global_objects = [] + + # Helpers + def create_tseqcollection(self): + sc = ROOT.TList() + for i in reversed(range(self.num_elems)): + o = ROOT.TObjString(str(i)) + sc.Add(o) + # To prevent deletion of the objects (TList is by default non-owning) + self._global_objects.append(o) + + return sc + + # Tests + def test_insert(self): + sc = self.create_tseqcollection() + + # Insert with positive index + o1 = ROOT.TObject() + sc.insert(1, o1) + self.assertEqual(sc.GetEntries(), self.num_elems + 1) + self.assertIs(sc.At(1), o1) + + # Insert with negative index (starts from end) + o2 = ROOT.TObject() + sc.insert(-1, o2) + self.assertEqual(sc.GetEntries(), self.num_elems + 2) + self.assertIs(sc.At(self.num_elems), o2) + + # Insert with index beyond lower boundary. + # Inserts at the beginning + o3 = ROOT.TObject() + sc.insert(-(self.num_elems + 3), o3) + self.assertEqual(sc.GetEntries(), self.num_elems + 3) + self.assertIs(sc.At(0), o3) + + # Insert with index beyond upper boundary. + # Inserts at the end + o4 = ROOT.TObject() + sc.insert(self.num_elems + 4, o4) + self.assertEqual(sc.GetEntries(), self.num_elems + 4) + self.assertIs(sc.At(self.num_elems + 3), o4) + + # Clear before the added element might be garbage collected, + # to avoid dangling pointer access. + sc.Clear() + + def test_pop(self): + sc = self.create_tseqcollection() + l1 = [elem for elem in sc] + + # No arguments, pop last item + self.assertEqual(sc.pop(), l1[-1]) + self.assertEqual(sc.GetEntries(), self.num_elems - 1) + + # Pop first item, positive index + self.assertEqual(sc.pop(0), l1[0]) + self.assertEqual(sc.GetEntries(), self.num_elems - 2) + + # Pop last item, negative index + self.assertEqual(sc.pop(-1), l1[1]) + self.assertEqual(sc.GetEntries(), self.num_elems - 3) + + # Pop from empty collection + with self.assertRaises(IndexError): + sc.pop() + + # Index out of range, positive + sc2 = self.create_tseqcollection() + with self.assertRaises(IndexError): + sc2.pop(self.num_elems) + + # Index out of range, negative + with self.assertRaises(IndexError): + sc2.pop(-(self.num_elems + 1)) + + # Pop with non-integer argument + with self.assertRaises(TypeError): + sc2.pop(1.0) + + # Pop a repeated element. + # Keep Python reference so the added element lives beyond the sc2.pop() call: + new_elem = ROOT.TObjString("2") + sc2.append(new_elem) + elem = sc2.pop() + self.assertEqual(sc2.At(0), elem) + + def test_reverse(self): + sc = self.create_tseqcollection() + l1 = [elem for elem in sc] + + sc.reverse() + + self.assertEqual(sc.GetEntries(), self.num_elems) + for i,elem in zip(range(self.num_elems), sc): + self.assertEqual(elem, l1[-(i + 1)]) + + # Empty collection + sc2 = ROOT.TList() + sc2.reverse() + self.assertEqual(sc2.GetEntries(), 0) + + def test_sort(self): + sc = self.create_tseqcollection() + l1 = [elem for elem in sc] + + # Regular sort, rely on TList::Sort + sc.sort() + # We need to set `key` until the pythonization to + # make TObjString comparable is there + l1.sort(key=lambda s: s.GetName()) + + self.assertEqual(sc.GetEntries(), self.num_elems) + self.assertEqual(l1[0], sc[0]) + for el1, el2 in zip(sc, l1): + self.assertEqual(el1, el2) + + # Python sort, key and reverse arguments. + # Sort by hash in reverse order + sc2 = self.create_tseqcollection() + l2 = [ elem for elem in sc2 ] + + def fsort(elem): + return elem.Hash() + + rev = True + sc2.sort(key = fsort, reverse = rev) + l2.sort(key = fsort, reverse = rev) + + self.assertEqual(sc2.GetEntries(), self.num_elems) + for el1, el2 in zip(sc2, l2): + self.assertEqual(el1, el2) + + # Empty collection + sc4 = ROOT.TList() + sc4.sort() + self.assertEqual(sc4.GetEntries(), 0) + + def test_index(self): + sc = self.create_tseqcollection() + + # Check all elements of collection + for i, elem in zip(range(self.num_elems), sc): + self.assertEqual(sc.index(elem), i) + + # Check element not in collection + o1 = ROOT.TObjString(str(self.num_elems)) + with self.assertRaises(ValueError): + sc.index(o1) + + if __name__ == '__main__': unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tseqcollection_listmethods.py b/bindings/pyroot/pythonizations/test/tseqcollection_listmethods.py deleted file mode 100644 index c297b6cafad77..0000000000000 --- a/bindings/pyroot/pythonizations/test/tseqcollection_listmethods.py +++ /dev/null @@ -1,166 +0,0 @@ -import unittest - -import ROOT - - -class TSeqCollectionListMethods(unittest.TestCase): - """ - Test for the Python-list-like methods added to TSeqCollection - (and subclasses): insert, pop, reverse, sort, index - """ - - num_elems = 3 - - _global_objects = [] - - # Helpers - def create_tseqcollection(self): - sc = ROOT.TList() - for i in reversed(range(self.num_elems)): - o = ROOT.TObjString(str(i)) - sc.Add(o) - # To prevent deletion of the objects (TList is by default non-owning) - self._global_objects.append(o) - - return sc - - # Tests - def test_insert(self): - sc = self.create_tseqcollection() - - # Insert with positive index - o1 = ROOT.TObject() - sc.insert(1, o1) - self.assertEqual(sc.GetEntries(), self.num_elems + 1) - self.assertIs(sc.At(1), o1) - - # Insert with negative index (starts from end) - o2 = ROOT.TObject() - sc.insert(-1, o2) - self.assertEqual(sc.GetEntries(), self.num_elems + 2) - self.assertIs(sc.At(self.num_elems), o2) - - # Insert with index beyond lower boundary. - # Inserts at the beginning - o3 = ROOT.TObject() - sc.insert(-(self.num_elems + 3), o3) - self.assertEqual(sc.GetEntries(), self.num_elems + 3) - self.assertIs(sc.At(0), o3) - - # Insert with index beyond upper boundary. - # Inserts at the end - o4 = ROOT.TObject() - sc.insert(self.num_elems + 4, o4) - self.assertEqual(sc.GetEntries(), self.num_elems + 4) - self.assertIs(sc.At(self.num_elems + 3), o4) - - # Clear before the added element might be garbage collected, - # to avoid dangling pointer access. - sc.Clear() - - def test_pop(self): - sc = self.create_tseqcollection() - l1 = [elem for elem in sc] - - # No arguments, pop last item - self.assertEqual(sc.pop(), l1[-1]) - self.assertEqual(sc.GetEntries(), self.num_elems - 1) - - # Pop first item, positive index - self.assertEqual(sc.pop(0), l1[0]) - self.assertEqual(sc.GetEntries(), self.num_elems - 2) - - # Pop last item, negative index - self.assertEqual(sc.pop(-1), l1[1]) - self.assertEqual(sc.GetEntries(), self.num_elems - 3) - - # Pop from empty collection - with self.assertRaises(IndexError): - sc.pop() - - # Index out of range, positive - sc2 = self.create_tseqcollection() - with self.assertRaises(IndexError): - sc2.pop(self.num_elems) - - # Index out of range, negative - with self.assertRaises(IndexError): - sc2.pop(-(self.num_elems + 1)) - - # Pop with non-integer argument - with self.assertRaises(TypeError): - sc2.pop(1.0) - - # Pop a repeated element. - # Keep Python reference so the added element lives beyond the sc2.pop() call: - new_elem = ROOT.TObjString("2") - sc2.append(new_elem) - elem = sc2.pop() - self.assertEqual(sc2.At(0), elem) - - def test_reverse(self): - sc = self.create_tseqcollection() - l1 = [elem for elem in sc] - - sc.reverse() - - self.assertEqual(sc.GetEntries(), self.num_elems) - for i,elem in zip(range(self.num_elems), sc): - self.assertEqual(elem, l1[-(i + 1)]) - - # Empty collection - sc2 = ROOT.TList() - sc2.reverse() - self.assertEqual(sc2.GetEntries(), 0) - - def test_sort(self): - sc = self.create_tseqcollection() - l1 = [elem for elem in sc] - - # Regular sort, rely on TList::Sort - sc.sort() - # We need to set `key` until the pythonization to - # make TObjString comparable is there - l1.sort(key=lambda s: s.GetName()) - - self.assertEqual(sc.GetEntries(), self.num_elems) - self.assertEqual(l1[0], sc[0]) - for el1, el2 in zip(sc, l1): - self.assertEqual(el1, el2) - - # Python sort, key and reverse arguments. - # Sort by hash in reverse order - sc2 = self.create_tseqcollection() - l2 = [ elem for elem in sc2 ] - - def fsort(elem): - return elem.Hash() - - rev = True - sc2.sort(key = fsort, reverse = rev) - l2.sort(key = fsort, reverse = rev) - - self.assertEqual(sc2.GetEntries(), self.num_elems) - for el1, el2 in zip(sc2, l2): - self.assertEqual(el1, el2) - - # Empty collection - sc4 = ROOT.TList() - sc4.sort() - self.assertEqual(sc4.GetEntries(), 0) - - def test_index(self): - sc = self.create_tseqcollection() - - # Check all elements of collection - for i, elem in zip(range(self.num_elems), sc): - self.assertEqual(sc.index(elem), i) - - # Check element not in collection - o1 = ROOT.TObjString(str(self.num_elems)) - with self.assertRaises(ValueError): - sc.index(o1) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tstring_comparisonops.py b/bindings/pyroot/pythonizations/test/tstring.py similarity index 62% rename from bindings/pyroot/pythonizations/test/tstring_comparisonops.py rename to bindings/pyroot/pythonizations/test/tstring.py index 10ce2759fc1d5..1ea27fc642ef3 100644 --- a/bindings/pyroot/pythonizations/test/tstring_comparisonops.py +++ b/bindings/pyroot/pythonizations/test/tstring.py @@ -1,7 +1,40 @@ import unittest +import ROOT from ROOT import TString + +class TStringLen(unittest.TestCase): + """ + Test for the pythonization that provides the length of a + TString instance `s` via `len(s)`. + """ + + # Tests + def test_len(self): + s = 'test' + ts = ROOT.TString(s) + self.assertEqual(len(ts), len(s)) + + +class TStringStrRepr(unittest.TestCase): + """ + Test for the pythonizations that provide a string representation + for instances of TString (__str__, __repr__). + """ + + # Tests + def test_str(self): + s = 'test' + ts = ROOT.TString(s) + self.assertEqual(str(ts), s) + + def test_repr(self): + s = 'test' + ts = ROOT.TString(s) + self.assertEqual(repr(ts), repr(s)) + + class TStringComparisonOps(unittest.TestCase): """ Test for the comparison operators of TString: @@ -111,5 +144,47 @@ def test_list_sort(self): self.assertEqual(l1, l2) +class TStringConverter(unittest.TestCase): + """ + Tests for passing a Python string to a C++ function that expects a TString. + + This feature is not implemented by a PyROOT pythonization, but by a converter + that was added to Cppyy to create a TString out of a Python string. + """ + + test_str = "test" + + # Helpers + def check_type_conversion(self): + s = ROOT.TString(self.test_str) + + # Works with TString... + self.assertEqual(ROOT.myfun(s), self.test_str) + # ... and Python string + self.assertEqual(ROOT.myfun(self.test_str), self.test_str) + + # Tests + def test_by_value(self): + ROOT.gInterpreter.Declare(""" + const char* myfun(TString s) { return s.Data(); } + """) + + self.check_type_conversion() + + def test_by_reference(self): + ROOT.gInterpreter.Declare(""" + const char* myfun(TString &s) { return s.Data(); } + """) + + self.check_type_conversion() + + def test_by_const_reference(self): + ROOT.gInterpreter.Declare(""" + const char* myfun(const TString &s) { return s.Data(); } + """) + + self.check_type_conversion() + + if __name__ == '__main__': unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tstring_converter.py b/bindings/pyroot/pythonizations/test/tstring_converter.py deleted file mode 100644 index 97c9b666a913e..0000000000000 --- a/bindings/pyroot/pythonizations/test/tstring_converter.py +++ /dev/null @@ -1,49 +0,0 @@ -import unittest - -import ROOT - - -class TStringConverter(unittest.TestCase): - """ - Tests for passing a Python string to a C++ function that expects a TString. - - This feature is not implemented by a PyROOT pythonization, but by a converter - that was added to Cppyy to create a TString out of a Python string. - """ - - test_str = "test" - - # Helpers - def check_type_conversion(self): - s = ROOT.TString(self.test_str) - - # Works with TString... - self.assertEqual(ROOT.myfun(s), self.test_str) - # ... and Python string - self.assertEqual(ROOT.myfun(self.test_str), self.test_str) - - # Tests - def test_by_value(self): - ROOT.gInterpreter.Declare(""" - const char* myfun(TString s) { return s.Data(); } - """) - - self.check_type_conversion() - - def test_by_reference(self): - ROOT.gInterpreter.Declare(""" - const char* myfun(TString &s) { return s.Data(); } - """) - - self.check_type_conversion() - - def test_by_const_reference(self): - ROOT.gInterpreter.Declare(""" - const char* myfun(const TString &s) { return s.Data(); } - """) - - self.check_type_conversion() - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tstring_len.py b/bindings/pyroot/pythonizations/test/tstring_len.py deleted file mode 100644 index 710b199dc7a90..0000000000000 --- a/bindings/pyroot/pythonizations/test/tstring_len.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -import ROOT - - -class TStringLen(unittest.TestCase): - """ - Test for the pythonization that provides the length of a - TString instance `s` via `len(s)`. - """ - - # Tests - def test_len(self): - s = 'test' - ts = ROOT.TString(s) - self.assertEqual(len(ts), len(s)) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tstring_str_repr.py b/bindings/pyroot/pythonizations/test/tstring_str_repr.py deleted file mode 100644 index 68ae8c9985456..0000000000000 --- a/bindings/pyroot/pythonizations/test/tstring_str_repr.py +++ /dev/null @@ -1,25 +0,0 @@ -import unittest - -import ROOT - - -class TStringStrRepr(unittest.TestCase): - """ - Test for the pythonizations that provide a string representation - for instances of TString (__str__, __repr__). - """ - - # Tests - def test_str(self): - s = 'test' - ts = ROOT.TString(s) - self.assertEqual(str(ts), s) - - def test_repr(self): - s = 'test' - ts = ROOT.TString(s) - self.assertEqual(repr(ts), repr(s)) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/ttree.py b/bindings/pyroot/pythonizations/test/ttree.py new file mode 100644 index 0000000000000..9d00317dd1c83 --- /dev/null +++ b/bindings/pyroot/pythonizations/test/ttree.py @@ -0,0 +1,480 @@ +import unittest +from array import array + +import numpy as np + +import ROOT + + +class TTreeBranchAttr(unittest.TestCase): + """ + Test for the pythonization that allows to access top-level tree branches/leaves as attributes + (i.e. `mytree.mybranch`) + + Since this pythonization is common to TTree and its subclasses, TChain, TNtuple + and TNtupleD are also tested here. + """ + + filename = "treebranchattr.root" + treename = "mytree" + tuplename = "mytuple" + nentries = 2 + arraysize = 10 + more = 10 + + # Setup + @classmethod + def setUpClass(cls): + ROOT.gInterpreter.Declare('#include "TreeHelper.h"') + ROOT.CreateTTree(cls.filename, cls.treename, cls.nentries, cls.arraysize, cls.more, "RECREATE") + ROOT.CreateTNtuple(cls.filename, cls.tuplename, cls.nentries, cls.more, "UPDATE") + + # Helpers + def get_tree_and_chain(self): + f = ROOT.TFile(self.filename) + t = f.Get(self.treename) + # Prevent double deletion of the tree (Python and C++ TFile) + ROOT.SetOwnership(t, False) + + c = ROOT.TChain(self.treename) + c.Add(self.filename) + c.Add(self.filename) + + # Read first entry + for ds in t, c: + ds.GetEntry(0) + + return f, t, c + + def get_ntuples(self): + f = ROOT.TFile(self.filename) + + nt = f.Get(self.tuplename) + ROOT.SetOwnership(nt, False) + + ntd = f.Get(self.tuplename + "D") + ROOT.SetOwnership(ntd, False) + + # Read first entry + for ds in nt, ntd: + ds.GetEntry(0) + + return f, nt, ntd + + # Tests + def test_basic_type_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + self.assertEqual(ds.floatb, self.more) + + def test_array_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + a = ds.arrayb + + for j in range(self.arraysize): + self.assertEqual(a[j], j) + + def test_array_2D_branch(self): + f, t, c = self.get_tree_and_chain() + + # Synchronized with arraysizeInner in TreeHelper.h + arraysize_inner = 2 + + for ds in t, c: + a = ds.array2Db + + self.assertEqual(a.shape, (self.arraysize, arraysize_inner)) + + for j in range(self.arraysize): + for k in range(arraysize_inner): + self.assertEqual(a[j][k], j + k) + + def test_char_array_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + self.assertEqual(ds.chararrayb.as_string(), "one") + + ds.GetEntry(1) + + self.assertEqual(ds.chararrayb.as_string(), "onetwo") + + def test_vector_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + v = ds.vectorb + + for j in range(self.arraysize): + self.assertEqual(v[j], j) + + def test_struct_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + ms = ds.structb + + self.assertEqual(ms.myint1, self.more) + self.assertEqual(ms.myint2, 0) + + def test_struct_branch_leaflist(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + self.assertEqual(ds.myintll1, self.more) + self.assertEqual(ds.myintll2, 0) + + def test_alias_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + ds.SetAlias("myalias", "floatb") + + self.assertEqual(ds.myalias, ds.floatb) + + def test_ntuples(self): + f, nt, ntd = self.get_ntuples() + + for ds in nt, ntd: + self.assertEqual(ds.x, 0.0) + self.assertEqual(ds.y, self.more) + self.assertEqual(ds.z, 2 * self.more) + + def test_ttree_subclass_in_namespace(self): + t = ROOT.Foo.MyTree() + t.i + + +class TTreeIterable(unittest.TestCase): + """ + Test for the pythonization that makes TTree instances iterable in Python. + For example, this allows to do: + `for event in mytree:` + `...` + + Since this pythonization is common to TTree and its subclasses, TChain, TNtuple + and TNtupleD are also tested here. + """ + + filename = "treeiterable.root" + treename = "mytree" + tuplename = "mytuple" + nentries = 10 + arraysize = 10 + more = 10 + + # Setup + @classmethod + def setUpClass(cls): + ROOT.gInterpreter.Declare('#include "TreeHelper.h"') + ROOT.CreateTTree(cls.filename, cls.treename, cls.nentries, cls.arraysize, cls.more, "RECREATE") + ROOT.CreateTNtuple(cls.filename, cls.tuplename, cls.nentries, cls.more, "UPDATE") + + # Helpers + def get_tree_and_chain(self): + f = ROOT.TFile(self.filename) + t = f.Get(self.treename) + # Prevent double deletion of the tree (Python and C++ TFile) + ROOT.SetOwnership(t, False) + + c = ROOT.TChain(self.treename) + c.Add(self.filename) + c.Add(self.filename) + + return f, t, c + + def get_ntuples(self): + f = ROOT.TFile(self.filename) + + nt = f.Get(self.tuplename) + ROOT.SetOwnership(nt, False) + + ntd = f.Get(self.tuplename + "D") + ROOT.SetOwnership(ntd, False) + + return f, nt, ntd + + # Tests + def test_basic_type_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + n = array("f", [0.0]) + ds.SetBranchAddress("floatb", n) + + i = 0 + for _ in ds: + self.assertEqual(n[0], i + self.more) + i = (i + 1) % self.nentries + + def test_array_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + a = array("d", self.arraysize * [0.0]) + ds.SetBranchAddress("arrayb", a) + + i = 0 + for _ in ds: + for j in range(self.arraysize): + self.assertEqual(a[j], i + j) + i = (i + 1) % self.nentries + + def test_struct_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + ms = ROOT.MyStruct() + ds.SetBranchAddress("structleaflistb", ms) + + i = 0 + for _ in ds: + self.assertEqual(ms.myint1, i + self.more) + self.assertEqual(ms.myint2, i * self.more) + i = (i + 1) % self.nentries + + def test_ntuples(self): + f, nt, ntd = self.get_ntuples() + + colnames = ["x", "y", "z"] + cols = [array("f", [0.0]) for _ in colnames] + colsd = [array("d", [0.0]) for _ in colnames] + ncols = len(cols) + + for ds, cs in [(nt, cols), (ntd, colsd)]: + for i in range(ncols): + ds.SetBranchAddress(colnames[i], cs[i]) + + numentry = 0 + + for _ in ds: + for i in range(ncols): + self.assertEqual(cs[i][0], numentry + i * self.more) + numentry += 1 + + +class TTreeSetBranchAddress(unittest.TestCase): + """ + Test for the pythonization of TTree::SetBranchAddress, which allows to pass proxy + references as arguments from the Python side. Example: + `v = ROOT.std.vector('int')()` + `t.SetBranchAddress("my_vector_branch", v)` + + Since this pythonization is common to TTree and its subclasses, TChain, TNtuple + and TNtupleD are also tested here. + """ + + filename = "treesetbranchaddress.root" + treename = "mytree" + tuplename = "mytuple" + nentries = 2 + arraysize = 10 + more = 10 + + # Setup + @classmethod + def setUpClass(cls): + ROOT.gInterpreter.Declare('#include "TreeHelper.h"') + ROOT.CreateTTree(cls.filename, cls.treename, cls.nentries, cls.arraysize, cls.more, "RECREATE") + ROOT.CreateTNtuple(cls.filename, cls.tuplename, cls.nentries, cls.more, "UPDATE") + + # Helpers + def get_tree_and_chain(self): + f = ROOT.TFile(self.filename) + t = f.Get(self.treename) + # Prevent double deletion of the tree (Python and C++ TFile) + ROOT.SetOwnership(t, False) + + c = ROOT.TChain(self.treename) + c.Add(self.filename) + c.Add(self.filename) + + return f, t, c + + def get_ntuples(self): + f = ROOT.TFile(self.filename) + + nt = f.Get(self.tuplename) + ROOT.SetOwnership(nt, False) + + ntd = f.Get(self.tuplename + "D") + ROOT.SetOwnership(ntd, False) + + return f, nt, ntd + + # Tests + # Basic type, array and struct leaf list do not actually need the pythonization, + # but testing anyway for the sake of completeness + def test_basic_type_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + n = array("f", [0.0]) + ds.SetBranchAddress("floatb", n) + ds.GetEntry(0) + + self.assertEqual(n[0], self.more) + + def test_array_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + a = array("d", self.arraysize * [0.0]) + + status = ds.SetBranchAddress("arrayb", a) + self.assertEqual(status, 0) # should return status 0 for success + + ds.GetEntry(0) + + for j in range(self.arraysize): + self.assertEqual(a[j], j) + + # check if type mismatch is correctly detected + a_wrong_type = array("f", self.arraysize * [0.0]) + status = ds.SetBranchAddress("arrayb", a_wrong_type) + self.assertEqual(status, -2) # should return error code because of type mismatch + + def test_numpy_array_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + a = np.array(self.arraysize * [0.0]) # dtype='float64' + + status = ds.SetBranchAddress("arrayb", a) + self.assertEqual(status, 0) # should return status 0 for success + + ds.GetEntry(0) + + for j in range(self.arraysize): + self.assertEqual(a[j], j) + + # check if type mismatch is correctly detected + a_wrong_type = np.zeros(self.arraysize, dtype=np.float32) + status = ds.SetBranchAddress("arrayb", a_wrong_type) + self.assertEqual(status, -2) # should return error code because of type mismatch + + def test_struct_branch_leaflist(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + ms = ROOT.MyStruct() + ds.SetBranchAddress("structleaflistb", ms) + ds.GetEntry(0) + + self.assertEqual(ms.myint1, self.more) + self.assertEqual(ms.myint2, 0) + + # Vector and struct do benefit from the pythonization + def test_vector_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + v = ROOT.std.vector("double")() + ds.SetBranchAddress("vectorb", v) + ds.GetEntry(0) + + for j in range(self.arraysize): + self.assertEqual(v[j], j) + + def test_struct_branch(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + ms = ROOT.MyStruct() + ds.SetBranchAddress("structb", ms) + ds.GetEntry(0) + + self.assertEqual(ms.myint1, self.more) + self.assertEqual(ms.myint2, 0) + + def test_fallback_case(self): + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + n = array("f", [0.0]) + b = ds.GetBranch("floatb") + # Test an overload that uses the original SetBranchAddress proxy + ds.SetBranchAddress("floatb", n, b) + ds.GetEntry(0) + + self.assertEqual(n[0], self.more) + + def test_ntuples(self): + f, nt, ntd = self.get_ntuples() + + colnames = ["x", "y", "z"] + cols = [array("f", [0.0]) for _ in colnames] + colsd = [array("d", [0.0]) for _ in colnames] + ncols = len(cols) + + for ds, cs in [(nt, cols), (ntd, colsd)]: + for i in range(ncols): + ds.SetBranchAddress(colnames[i], cs[i]) + + ds.GetEntry(0) + + for i in range(ncols): + self.assertEqual(cs[i][0], i * self.more) + + def test_class_with_array_member(self): + # 6468 + f, t, c = self.get_tree_and_chain() + + for ds in t, c: + mc = ROOT.MyClass() + ds.SetBranchAddress("clarrmember", mc) + + ds.GetEntry(0) + self.assertEqual(mc.foo[0], 0.0) + self.assertEqual(mc.foo[1], 1.0) + + ds.GetEntry(1) + self.assertEqual(mc.foo[0], 1.0) + self.assertEqual(mc.foo[1], 2.0) + + def test_np_conversion(self): + # 18365 + a = np.zeros(3, np.uint16) + a[0] = 1 + a[1] = 2 + a[2] = 3 + c = np.zeros(3, np.int16) + c[0] = 4 + c[1] = 5 + c[2] = 6 + t = ROOT.TTree("t", "t") + t.Branch("b", a, "b[3]/s") + t.Branch("d", c, "d[3]/S") + t.Fill() + a[0] = 10 + a[1] = 20 + a[2] = 30 + c[0] = 40 + c[1] = 50 + c[2] = 60 + t.Fill() + # t.Print() + # t.Scan() + t.SetBranchAddress("b", a) + t.SetBranchAddress("d", c) + t.GetEntry(0) + self.assertEqual(a[0], 1) + self.assertEqual(a[1], 2) + self.assertEqual(a[2], 3) + self.assertEqual(c[0], 4) + self.assertEqual(c[1], 5) + self.assertEqual(c[2], 6) + t.GetEntry(1) + self.assertEqual(a[0], 10) + self.assertEqual(a[1], 20) + self.assertEqual(a[2], 30) + self.assertEqual(c[0], 40) + self.assertEqual(c[1], 50) + self.assertEqual(c[2], 60) + + +if __name__ == '__main__': + unittest.main() diff --git a/bindings/pyroot/pythonizations/test/ttree_branch_attr.py b/bindings/pyroot/pythonizations/test/ttree_branch_attr.py deleted file mode 100644 index dc8efcc088827..0000000000000 --- a/bindings/pyroot/pythonizations/test/ttree_branch_attr.py +++ /dev/null @@ -1,149 +0,0 @@ -import unittest - -import ROOT - - -class TTreeBranchAttr(unittest.TestCase): - """ - Test for the pythonization that allows to access top-level tree branches/leaves as attributes - (i.e. `mytree.mybranch`) - - Since this pythonization is common to TTree and its subclasses, TChain, TNtuple - and TNtupleD are also tested here. - """ - - filename = "treebranchattr.root" - treename = "mytree" - tuplename = "mytuple" - nentries = 2 - arraysize = 10 - more = 10 - - # Setup - @classmethod - def setUpClass(cls): - ROOT.gInterpreter.Declare('#include "TreeHelper.h"') - ROOT.CreateTTree(cls.filename, cls.treename, cls.nentries, cls.arraysize, cls.more, "RECREATE") - ROOT.CreateTNtuple(cls.filename, cls.tuplename, cls.nentries, cls.more, "UPDATE") - - # Helpers - def get_tree_and_chain(self): - f = ROOT.TFile(self.filename) - t = f.Get(self.treename) - # Prevent double deletion of the tree (Python and C++ TFile) - ROOT.SetOwnership(t, False) - - c = ROOT.TChain(self.treename) - c.Add(self.filename) - c.Add(self.filename) - - # Read first entry - for ds in t, c: - ds.GetEntry(0) - - return f, t, c - - def get_ntuples(self): - f = ROOT.TFile(self.filename) - - nt = f.Get(self.tuplename) - ROOT.SetOwnership(nt, False) - - ntd = f.Get(self.tuplename + "D") - ROOT.SetOwnership(ntd, False) - - # Read first entry - for ds in nt, ntd: - ds.GetEntry(0) - - return f, nt, ntd - - # Tests - def test_basic_type_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - self.assertEqual(ds.floatb, self.more) - - def test_array_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - a = ds.arrayb - - for j in range(self.arraysize): - self.assertEqual(a[j], j) - - def test_array_2D_branch(self): - f, t, c = self.get_tree_and_chain() - - # Synchronized with arraysizeInner in TreeHelper.h - arraysize_inner = 2 - - for ds in t, c: - a = ds.array2Db - - self.assertEqual(a.shape, (self.arraysize, arraysize_inner)) - - for j in range(self.arraysize): - for k in range(arraysize_inner): - self.assertEqual(a[j][k], j + k) - - def test_char_array_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - self.assertEqual(ds.chararrayb.as_string(), "one") - - ds.GetEntry(1) - - self.assertEqual(ds.chararrayb.as_string(), "onetwo") - - def test_vector_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - v = ds.vectorb - - for j in range(self.arraysize): - self.assertEqual(v[j], j) - - def test_struct_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - ms = ds.structb - - self.assertEqual(ms.myint1, self.more) - self.assertEqual(ms.myint2, 0) - - def test_struct_branch_leaflist(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - self.assertEqual(ds.myintll1, self.more) - self.assertEqual(ds.myintll2, 0) - - def test_alias_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - ds.SetAlias("myalias", "floatb") - - self.assertEqual(ds.myalias, ds.floatb) - - def test_ntuples(self): - f, nt, ntd = self.get_ntuples() - - for ds in nt, ntd: - self.assertEqual(ds.x, 0.0) - self.assertEqual(ds.y, self.more) - self.assertEqual(ds.z, 2 * self.more) - - def test_ttree_subclass_in_namespace(self): - t = ROOT.Foo.MyTree() - t.i - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/ttree_iterable.py b/bindings/pyroot/pythonizations/test/ttree_iterable.py deleted file mode 100644 index 9936af9b7982d..0000000000000 --- a/bindings/pyroot/pythonizations/test/ttree_iterable.py +++ /dev/null @@ -1,116 +0,0 @@ -import unittest -from array import array - -import ROOT - - -class TTreeIterable(unittest.TestCase): - """ - Test for the pythonization that makes TTree instances iterable in Python. - For example, this allows to do: - `for event in mytree:` - `...` - - Since this pythonization is common to TTree and its subclasses, TChain, TNtuple - and TNtupleD are also tested here. - """ - - filename = "treeiterable.root" - treename = "mytree" - tuplename = "mytuple" - nentries = 10 - arraysize = 10 - more = 10 - - # Setup - @classmethod - def setUpClass(cls): - ROOT.gInterpreter.Declare('#include "TreeHelper.h"') - ROOT.CreateTTree(cls.filename, cls.treename, cls.nentries, cls.arraysize, cls.more, "RECREATE") - ROOT.CreateTNtuple(cls.filename, cls.tuplename, cls.nentries, cls.more, "UPDATE") - - # Helpers - def get_tree_and_chain(self): - f = ROOT.TFile(self.filename) - t = f.Get(self.treename) - # Prevent double deletion of the tree (Python and C++ TFile) - ROOT.SetOwnership(t, False) - - c = ROOT.TChain(self.treename) - c.Add(self.filename) - c.Add(self.filename) - - return f, t, c - - def get_ntuples(self): - f = ROOT.TFile(self.filename) - - nt = f.Get(self.tuplename) - ROOT.SetOwnership(nt, False) - - ntd = f.Get(self.tuplename + "D") - ROOT.SetOwnership(ntd, False) - - return f, nt, ntd - - # Tests - def test_basic_type_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - n = array("f", [0.0]) - ds.SetBranchAddress("floatb", n) - - i = 0 - for _ in ds: - self.assertEqual(n[0], i + self.more) - i = (i + 1) % self.nentries - - def test_array_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - a = array("d", self.arraysize * [0.0]) - ds.SetBranchAddress("arrayb", a) - - i = 0 - for _ in ds: - for j in range(self.arraysize): - self.assertEqual(a[j], i + j) - i = (i + 1) % self.nentries - - def test_struct_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - ms = ROOT.MyStruct() - ds.SetBranchAddress("structleaflistb", ms) - - i = 0 - for _ in ds: - self.assertEqual(ms.myint1, i + self.more) - self.assertEqual(ms.myint2, i * self.more) - i = (i + 1) % self.nentries - - def test_ntuples(self): - f, nt, ntd = self.get_ntuples() - - colnames = ["x", "y", "z"] - cols = [array("f", [0.0]) for _ in colnames] - colsd = [array("d", [0.0]) for _ in colnames] - ncols = len(cols) - - for ds, cs in [(nt, cols), (ntd, colsd)]: - for i in range(ncols): - ds.SetBranchAddress(colnames[i], cs[i]) - - numentry = 0 - - for _ in ds: - for i in range(ncols): - self.assertEqual(cs[i][0], numentry + i * self.more) - numentry += 1 - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/ttree_setbranchaddress.py b/bindings/pyroot/pythonizations/test/ttree_setbranchaddress.py deleted file mode 100644 index 0c0127afc7ddd..0000000000000 --- a/bindings/pyroot/pythonizations/test/ttree_setbranchaddress.py +++ /dev/null @@ -1,229 +0,0 @@ -import unittest -from array import array - -import ROOT -import numpy as np - - -class TTreeSetBranchAddress(unittest.TestCase): - """ - Test for the pythonization of TTree::SetBranchAddress, which allows to pass proxy - references as arguments from the Python side. Example: - `v = ROOT.std.vector('int')()` - `t.SetBranchAddress("my_vector_branch", v)` - - Since this pythonization is common to TTree and its subclasses, TChain, TNtuple - and TNtupleD are also tested here. - """ - - filename = "treesetbranchaddress.root" - treename = "mytree" - tuplename = "mytuple" - nentries = 2 - arraysize = 10 - more = 10 - - # Setup - @classmethod - def setUpClass(cls): - ROOT.gInterpreter.Declare('#include "TreeHelper.h"') - ROOT.CreateTTree(cls.filename, cls.treename, cls.nentries, cls.arraysize, cls.more, "RECREATE") - ROOT.CreateTNtuple(cls.filename, cls.tuplename, cls.nentries, cls.more, "UPDATE") - - # Helpers - def get_tree_and_chain(self): - f = ROOT.TFile(self.filename) - t = f.Get(self.treename) - # Prevent double deletion of the tree (Python and C++ TFile) - ROOT.SetOwnership(t, False) - - c = ROOT.TChain(self.treename) - c.Add(self.filename) - c.Add(self.filename) - - return f, t, c - - def get_ntuples(self): - f = ROOT.TFile(self.filename) - - nt = f.Get(self.tuplename) - ROOT.SetOwnership(nt, False) - - ntd = f.Get(self.tuplename + "D") - ROOT.SetOwnership(ntd, False) - - return f, nt, ntd - - # Tests - # Basic type, array and struct leaf list do not actually need the pythonization, - # but testing anyway for the sake of completeness - def test_basic_type_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - n = array("f", [0.0]) - ds.SetBranchAddress("floatb", n) - ds.GetEntry(0) - - self.assertEqual(n[0], self.more) - - def test_array_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - a = array("d", self.arraysize * [0.0]) - - status = ds.SetBranchAddress("arrayb", a) - self.assertEqual(status, 0) # should return status 0 for success - - ds.GetEntry(0) - - for j in range(self.arraysize): - self.assertEqual(a[j], j) - - # check if type mismatch is correctly detected - a_wrong_type = array("f", self.arraysize * [0.0]) - status = ds.SetBranchAddress("arrayb", a_wrong_type) - self.assertEqual(status, -2) # should return error code because of type mismatch - - def test_numpy_array_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - a = np.array(self.arraysize * [0.0]) # dtype='float64' - - status = ds.SetBranchAddress("arrayb", a) - self.assertEqual(status, 0) # should return status 0 for success - - ds.GetEntry(0) - - for j in range(self.arraysize): - self.assertEqual(a[j], j) - - # check if type mismatch is correctly detected - a_wrong_type = np.zeros(self.arraysize, dtype=np.float32) - status = ds.SetBranchAddress("arrayb", a_wrong_type) - self.assertEqual(status, -2) # should return error code because of type mismatch - - def test_struct_branch_leaflist(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - ms = ROOT.MyStruct() - ds.SetBranchAddress("structleaflistb", ms) - ds.GetEntry(0) - - self.assertEqual(ms.myint1, self.more) - self.assertEqual(ms.myint2, 0) - - # Vector and struct do benefit from the pythonization - def test_vector_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - v = ROOT.std.vector("double")() - ds.SetBranchAddress("vectorb", v) - ds.GetEntry(0) - - for j in range(self.arraysize): - self.assertEqual(v[j], j) - - def test_struct_branch(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - ms = ROOT.MyStruct() - ds.SetBranchAddress("structb", ms) - ds.GetEntry(0) - - self.assertEqual(ms.myint1, self.more) - self.assertEqual(ms.myint2, 0) - - def test_fallback_case(self): - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - n = array("f", [0.0]) - b = ds.GetBranch("floatb") - # Test an overload that uses the original SetBranchAddress proxy - ds.SetBranchAddress("floatb", n, b) - ds.GetEntry(0) - - self.assertEqual(n[0], self.more) - - def test_ntuples(self): - f, nt, ntd = self.get_ntuples() - - colnames = ["x", "y", "z"] - cols = [array("f", [0.0]) for _ in colnames] - colsd = [array("d", [0.0]) for _ in colnames] - ncols = len(cols) - - for ds, cs in [(nt, cols), (ntd, colsd)]: - for i in range(ncols): - ds.SetBranchAddress(colnames[i], cs[i]) - - ds.GetEntry(0) - - for i in range(ncols): - self.assertEqual(cs[i][0], i * self.more) - - def test_class_with_array_member(self): - # 6468 - f, t, c = self.get_tree_and_chain() - - for ds in t, c: - mc = ROOT.MyClass() - ds.SetBranchAddress("clarrmember", mc) - - ds.GetEntry(0) - self.assertEqual(mc.foo[0], 0.0) - self.assertEqual(mc.foo[1], 1.0) - - ds.GetEntry(1) - self.assertEqual(mc.foo[0], 1.0) - self.assertEqual(mc.foo[1], 2.0) - - def test_np_conversion(self): - # 18365 - a = np.zeros(3, np.uint16) - a[0] = 1 - a[1] = 2 - a[2] = 3 - c = np.zeros(3, np.int16) - c[0] = 4 - c[1] = 5 - c[2] = 6 - t = ROOT.TTree("t", "t") - t.Branch("b", a, "b[3]/s") - t.Branch("d", c, "d[3]/S") - t.Fill() - a[0] = 10 - a[1] = 20 - a[2] = 30 - c[0] = 40 - c[1] = 50 - c[2] = 60 - t.Fill() - # t.Print() - # t.Scan() - t.SetBranchAddress("b", a) - t.SetBranchAddress("d", c) - t.GetEntry(0) - self.assertEqual(a[0], 1) - self.assertEqual(a[1], 2) - self.assertEqual(a[2], 3) - self.assertEqual(c[0], 4) - self.assertEqual(c[1], 5) - self.assertEqual(c[2], 6) - t.GetEntry(1) - self.assertEqual(a[0], 10) - self.assertEqual(a[1], 20) - self.assertEqual(a[2], 30) - self.assertEqual(c[0], 40) - self.assertEqual(c[1], 50) - self.assertEqual(c[2], 60) - - -if __name__ == "__main__": - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tvector3_getitem.py b/bindings/pyroot/pythonizations/test/tvector3.py similarity index 72% rename from bindings/pyroot/pythonizations/test/tvector3_getitem.py rename to bindings/pyroot/pythonizations/test/tvector3.py index cd506d2a1ce8a..2f3652949fe39 100644 --- a/bindings/pyroot/pythonizations/test/tvector3_getitem.py +++ b/bindings/pyroot/pythonizations/test/tvector3.py @@ -3,6 +3,18 @@ import ROOT +class TVector3Len(unittest.TestCase): + """ + Test for the pythonization that allows to get the size of a + TVector3 (always 3) by calling `len` on it. + """ + + # Tests + def test_len(self): + v = ROOT.TVector3(1., 2., 3.) + self.assertEqual(len(v), 3) + + class TVector3GetItem(unittest.TestCase): """ Test for the pythonization that allows to: (i) get an item of a diff --git a/bindings/pyroot/pythonizations/test/tvector3_len.py b/bindings/pyroot/pythonizations/test/tvector3_len.py deleted file mode 100644 index a6c30e2b1e6bc..0000000000000 --- a/bindings/pyroot/pythonizations/test/tvector3_len.py +++ /dev/null @@ -1,19 +0,0 @@ -import unittest - -import ROOT - - -class TVector3Len(unittest.TestCase): - """ - Test for the pythonization that allows to get the size of a - TVector3 (always 3) by calling `len` on it. - """ - - # Tests - def test_len(self): - v = ROOT.TVector3(1., 2., 3.) - self.assertEqual(len(v), 3) - - -if __name__ == '__main__': - unittest.main() diff --git a/bindings/pyroot/pythonizations/test/tvectort_getitem.py b/bindings/pyroot/pythonizations/test/tvectort.py similarity index 73% rename from bindings/pyroot/pythonizations/test/tvectort_getitem.py rename to bindings/pyroot/pythonizations/test/tvectort.py index c3938eb8733e1..7bcd3e7e1bd91 100644 --- a/bindings/pyroot/pythonizations/test/tvectort_getitem.py +++ b/bindings/pyroot/pythonizations/test/tvectort.py @@ -3,6 +3,20 @@ import ROOT +class TVectorTLen(unittest.TestCase): + """ + Test for the pythonization that allows to get the size of a + TVectorT by calling `len` on it. + """ + + num_elems = 3 + + # Tests + def test_len(self): + v = ROOT.TVectorT[float](self.num_elems) + self.assertEqual(len(v), self.num_elems) + + class TVectorTGetItem(unittest.TestCase): """ Test for the pythonization that allows to: (i) get an item of a diff --git a/bindings/pyroot/pythonizations/test/tvectort_len.py b/bindings/pyroot/pythonizations/test/tvectort_len.py deleted file mode 100644 index ad161983b1cdd..0000000000000 --- a/bindings/pyroot/pythonizations/test/tvectort_len.py +++ /dev/null @@ -1,21 +0,0 @@ -import unittest - -import ROOT - - -class TVectorTLen(unittest.TestCase): - """ - Test for the pythonization that allows to get the size of a - TVectorT by calling `len` on it. - """ - - num_elems = 3 - - # Tests - def test_len(self): - v = ROOT.TVectorT[float](self.num_elems) - self.assertEqual(len(v), self.num_elems) - - -if __name__ == '__main__': - unittest.main()