diff --git a/Trigger/TrigValidation/TrigValTools/CMakeLists.txt b/Trigger/TrigValidation/TrigValTools/CMakeLists.txt
index f49eff884a76c1a30ddff94a14d819dbabc236da..45e23e0bfbd0c560ad5567cbfff911662776a93b 100644
--- a/Trigger/TrigValidation/TrigValTools/CMakeLists.txt
+++ b/Trigger/TrigValidation/TrigValTools/CMakeLists.txt
@@ -1,44 +1,36 @@
-################################################################################
-# Package: TrigValTools
-################################################################################
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 
 # Declare the package name:
 atlas_subdir( TrigValTools )
 
 # External dependencies:
-find_package( ROOT COMPONENTS Hist Graf Gpad RIO Core Tree MathCore pthread Graf3d Html Postscript Gui GX11TTF GX11 )
-
-include_directories(src)
+find_package( ROOT COMPONENTS Hist Graf Gpad RIO Core MathCore Postscript )
 
 # Component(s) in the package:
 atlas_add_library( TrigValTools
                    src/*.cxx
                    PUBLIC_HEADERS TrigValTools
-                   PRIVATE_INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
-                   PRIVATE_LINK_LIBRARIES ${ROOT_LIBRARIES} )
+                   INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                   LINK_LIBRARIES ${ROOT_LIBRARIES} )
 
 atlas_add_dictionary( TrigValToolsDict
                       TrigValTools/TrigValToolsDict.h
                       TrigValTools/selection.xml
-                      INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
-                      LINK_LIBRARIES ${ROOT_LIBRARIES} TrigValTools )
+                      LINK_LIBRARIES TrigValTools )
 
 # Install files from the package:
-atlas_install_python_modules( python/*.py python/TrigValSteering bin/chainDump.py )
+atlas_install_python_modules( python/*.py python/TrigValSteering bin/chainDump.py POST_BUILD_CMD ${ATLAS_FLAKE8} )
+atlas_install_scripts( bin/*.py test/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} )
+atlas_install_scripts( bin/*.pl bin/*.sh )
 atlas_install_data( share/*.json share/*.conf )
-atlas_install_runtime( bin/chainDump.py )
-atlas_install_scripts( bin/*.py bin/*.pl bin/*.sh test/*.py )
-atlas_install_generic( html/root2html/*.html
-                       DESTINATION share/TrigValTools/root2html
-                       EXECUTABLE )
 
 # Unit tests:
-atlas_add_test( TrigValSteering_flake8
-                SCRIPT flake8 --select=ATL,F,E7,E9,W6 --enable-extension=ATL900,ATL901 ${CMAKE_CURRENT_SOURCE_DIR}/python/TrigValSteering
-                POST_EXEC_SCRIPT nopost.sh )
-
 atlas_add_test( TrigValSteering_unit_test
                 SCRIPT test_unit_trigvalsteering.py
                 PROPERTIES TIMEOUT 300
                 POST_EXEC_SCRIPT nopost.sh )
 
+atlas_add_test( rootcomp
+                SCRIPT test/test_rootcomp.sh ${CMAKE_CURRENT_SOURCE_DIR}/test/test_rootcomp.C
+                PROPERTIES TIMEOUT 120
+                POST_EXEC_SCRIPT nopost.sh )
diff --git a/Trigger/TrigValidation/TrigValTools/TrigValTools/TRoot2Html.h b/Trigger/TrigValidation/TrigValTools/TrigValTools/TRoot2Html.h
deleted file mode 100644
index 10c5dd2b9caacf7f7d4760d2f2a955dd55e6462a..0000000000000000000000000000000000000000
--- a/Trigger/TrigValidation/TrigValTools/TrigValTools/TRoot2Html.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
-  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
-*/
-
-#ifndef TRIGVALTOOLS_TROOT2HTML_H
-#define TRIGVALTOOLS_TROOT2HTML_H
-
-/**
- * @file   TRoot2Html.h
- * @brief  TRoot2Html class
- * @author Frank Winklmeier
- *
- * $Id: TRoot2Html.h 702373 2015-10-22 13:55:56Z fwinkl $
- */
-
-#include <fstream>
-#include <vector>
-
-#include "TMultiFileLooper.h"
-#include "TDirectory.h"
-#include "TString.h"
-#include "TH1.h"
-
-class TPRegexp;
-
-/**
- * @class  TRoot2Html
- * @brief  Create a (static) web page from ROOT histograms
- * @author Frank Winklmeier
- *
- * Create images from histograms in root files and assemble them in
- * a web page with a tree like naviation structure.
- */
-class TRoot2Html : public TMultiFileLooper {
- public:
-
-  /// C'tor
-  TRoot2Html();
-  /// D'tor
-  virtual ~TRoot2Html() {}
-  /// Copy C'tor (needed for Reflex dictionary generation)
-  TRoot2Html(const TRoot2Html& other);
-
-  /// \name Processing hooks
-  //@{
-  virtual void beginJob();
-  virtual void endJob();
-  virtual void beforeFile();
-  virtual void afterFile();
-  virtual void beforeDir();
-  virtual void afterDir();
-  virtual void processKey(TDirectory& dir, TKey& key);
-  //@}
-  
-  /// Output directory for HTML pages
-  void setOutputDir(const char* dir) {m_outDir = dir;}
-
-  /// Set image size
-  void setImageSize(Int_t width, Int_t height) {m_imgWidth = width; m_imgHeight = height;}
-
-  /// Add draw options for images (selected by regular expression on name)
-  void addDrawOptions(const char* regexp, const char* options);
-
-  /// Add draw options for images (selected by regular expression on class name)
-  void addClassDrawOptions(const char* regexp, const char* options);
-    
-  /// Show full file name in tree
-  void showFullFileName(Bool_t show = kTRUE) {m_showFullFileName = show;}
-  
- private:
-  TString m_fileList;
-  TString m_outDir;
-  Int_t m_imgHeight, m_imgWidth;
-  std::ofstream m_xml;
-  Int_t m_nodeId;
-  
-  Bool_t m_showFullFileName;
-  std::vector< std::pair<TPRegexp*,TString> > m_drawOptions;
-  std::vector< std::pair<TPRegexp*,TString> > m_classDrawOptions;
-    
-  TString hist2Png(TDirectory& dir, const TString& name);
-  TString getDrawOptions(const TH1& h);
-    
-  TString xmlTreeItem(const char* text);
-  TString xmlTreeItemClose();
-  TString xmlUserData(const char* name, const char* data);
-
-  // Not copyable due to the ofstream.
-  TRoot2Html& operator= (const TRoot2Html&);
-};
-
-#endif
diff --git a/Trigger/TrigValidation/TrigValTools/TrigValTools/TrigValToolsDict.h b/Trigger/TrigValidation/TrigValTools/TrigValTools/TrigValToolsDict.h
index 28064c1311102e7c58d25f36df8dce2660585121..f95c08a5c7e74a28d2da1c60e45d2da41f7c43e0 100644
--- a/Trigger/TrigValidation/TrigValTools/TrigValTools/TrigValToolsDict.h
+++ b/Trigger/TrigValidation/TrigValTools/TrigValTools/TrigValToolsDict.h
@@ -1,10 +1,9 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 */
 
 #include "TrigValTools/TRootCompare.h"
 #include "TrigValTools/TFileLooper.h"
 #include "TrigValTools/TMultiFileLooper.h"
-#include "TrigValTools/TRoot2Html.h"
 
 #include "TPRegexp.h"
diff --git a/Trigger/TrigValidation/TrigValTools/TrigValTools/selection.xml b/Trigger/TrigValidation/TrigValTools/TrigValTools/selection.xml
index 512c99894514910c44d5c663988914aa57fce28b..2102ba54f93d5003f5d202303c9794f2e432e34f 100644
--- a/Trigger/TrigValidation/TrigValTools/TrigValTools/selection.xml
+++ b/Trigger/TrigValidation/TrigValTools/TrigValTools/selection.xml
@@ -2,5 +2,4 @@
   <class name="TRootCompare" />
   <class name="TFileLooper" />
   <class name="TMultiFileLooper" />
-  <class name="TRoot2Html" />
-</lcgdict>
\ No newline at end of file
+</lcgdict>
diff --git a/Trigger/TrigValidation/TrigValTools/bin/COOLRates.py b/Trigger/TrigValidation/TrigValTools/bin/COOLRates.py
index 1f1d37e5cc4678992b25099fdd4f62bd62794005..a5ec94e6668103e830bb98afe424c601b85b1ddd 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/COOLRates.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/COOLRates.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 
 #====================================================================================================================
 
@@ -13,8 +13,6 @@ __doc__     = "Fast Rate plotter from COOL database"
 from PyCool import cool
 from multiprocessing import Queue, cpu_count, Process
 from time import sleep
-import random
-import copy
 import argparse,sys,logging,time,os
 from math import log10
 import signal
@@ -45,7 +43,7 @@ if __name__=='__main__':
 	parser.add_argument('-m','--processes',dest='PROCESSES',default=2,type=int,help='Sets number of processes for multiprocessing retrieval of COOL data')	
 	args = parser.parse_args()
 
-from ROOT import gDirectory, TDatime, TProfile, TH1F, TCanvas, TLegend, TGaxis, gROOT, gStyle, SetOwnership, TColor, TLatex
+from ROOT import TDatime, TProfile, TCanvas, TLegend, TGaxis, gROOT, SetOwnership, TColor, TLatex
 
 #====================================================================================================================
 
@@ -145,7 +143,7 @@ class COOLQueryWorker():
 					break
 				resultBundle = self.processQuery(queryBundle)
 				queueOut.put(resultBundle)
-		except Exception,exception:
+		except Exception as exception:
 			logger.critical(exception)
 		
 		self.close()
@@ -265,7 +263,7 @@ class QueryBundle():
 		self.IoVEnd = IoVEnd
 		for request in payloadRequests:
 			if not len(request)==3:
-				print payloadRequests
+				print(payloadRequests)
 				sys.exit(0)
 		self.payloadRequests = payloadRequests
 		self.payloadRequirements = payloadRequirements
@@ -315,7 +313,7 @@ def rateNameInfo(runLbRanges,mySignal,numProc=1):
 			except Empty:
 				sleep(.001)
 				continue
-			if run == True:
+			if run is True:
 				break
 			runLbStart=run<<32
 			runLbEnd=runLbStart+1
@@ -400,7 +398,7 @@ def rateNameInfo(runLbRanges,mySignal,numProc=1):
 			except Empty:
 				time.sleep(.001)
 				continue
-			if result == True:
+			if result is True:
 				finished+=1
 				if finished==numProc: break
 				continue
@@ -561,7 +559,7 @@ def timeRangeToRunLbRange(timeRange):
 	try:
 		timeStart = time.strptime(timeStart,'%Y-%m-%d:%H:%M:%S')
 		timeEnd = time.strptime(timeEnd,'%Y-%m-%d:%H:%M:%S')
-	except:
+	except Exception:
 		logger.critical('Time range "{0}" does not match YYYY-MM-DD:HH:mm:ss;YYYY-MM-DD:HH:mm:ss'.format(timeRange))
 		sys.exit(0)
 
@@ -600,7 +598,6 @@ def nanoTimeRangesToRunLbRanges(nanoTimeRanges):
 #====================================================================================================================
 
 def fillNumberToRunLbRange(fillNumbers):
-	runLbRanges = []
 	minFill = min(fillNumbers)-1
 	worker = COOLQueryWorker()
 	result = {}
diff --git a/Trigger/TrigValidation/TrigValTools/bin/check_log.py b/Trigger/TrigValidation/TrigValTools/bin/check_log.py
index 9c4271afa98dc28bf0587aa95b2dde0d2b2af467..75e21bf84d8bbbe2ced1d34782396805173a55e5 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/check_log.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/check_log.py
@@ -15,24 +15,24 @@ If no config file is provided, all errors will be shown.'
 
 # Error keywords
 errorRegex = [
-    '^ERROR ', ' ERROR ', ' FATAL ', 'CRITICAL ', 'ABORT_CHAIN',
-    '^Exception\:',
-    '^Caught signal',
-    '^Core dump',
-    'inconsistent use of tabs and spaces in indentation',
-    'glibc detected',
-    'tcmalloc\: allocation failed',
-    'athenaHLT.py\: error',
-    'HLTMPPU.*Child Issue',
-    'HLTMPPU.*Configuration Issue',
-    'There was a crash',
-    'illegal instruction',
-    'failure loading library',
-    'Cannot allocate memory',
-    'in state: CONTROLREADY$',
-    '^\s*missing data: ',
-    '^\s*can be produced by alg(s): ',
-    'pure virtual method called'
+    r'^ERROR ', ' ERROR ', ' FATAL ', 'CRITICAL ', 'ABORT_CHAIN',
+    r'^Exception\:',
+    r'^Caught signal',
+    r'^Core dump',
+    r'inconsistent use of tabs and spaces in indentation',
+    r'glibc detected',
+    r'tcmalloc\: allocation failed',
+    r'athenaHLT.py\: error',
+    r'HLTMPPU.*Child Issue',
+    r'HLTMPPU.*Configuration Issue',
+    r'There was a crash',
+    r'illegal instruction',
+    r'failure loading library',
+    r'Cannot allocate memory',
+    r'in state: CONTROLREADY$',
+    r'^\s*missing data: ',
+    r'^\s*can be produced by alg(s): ',
+    r'pure virtual method called'
 ]
 
 # Add list of all builtin Python errors
@@ -42,11 +42,11 @@ errorRegex.extend(builtinErrors)
 
 # Traceback keywords
 traceback = [
-    'Traceback',
-    'Shortened traceback',
-    'stack trace',
-    '^Algorithm stack',
-    '^#\d+\s*0x\w+ in '
+    r'Traceback',
+    r'Shortened traceback',
+    r'stack trace',
+    r'^Algorithm stack',
+    r'^#\d+\s*0x\w+ in '
 ]
 errorRegex.extend(traceback)
 
@@ -113,7 +113,7 @@ def parseConfig():
                       line = line[1:-1]
                   ignorePattern.append(line)
         noConfig = False
-    except:
+    except Exception:
       print('No config file, all warnings/errors will be printed')
       noConfig = True
 
@@ -124,9 +124,9 @@ def scanLogfile():
     tPattern = re.compile('|'.join(traceback))
     global msgLevels
     global logFileAddress
-    if args.warnings == True:
+    if args.warnings is True:
         pattern = warningRegex
-    if args.errors == True:
+    if args.errors is True:
         pattern = errorRegex
     msgLevels = re.compile('|'.join(pattern))
     igLevels = re.compile('|'.join(ignorePattern))
@@ -136,7 +136,7 @@ def scanLogfile():
         tracing = False
         for line in logFile:
             #Tracing only makes sense for errors
-            if args.errors == True and re.search(tPattern,line):
+            if args.errors is True and re.search(tPattern,line):
                 tracing = True
             elif line =='\n':
                 tracing = False
diff --git a/Trigger/TrigValidation/TrigValTools/bin/histSizes.py b/Trigger/TrigValidation/TrigValTools/bin/histSizes.py
index 4ea3c2d3d37fa9c8d83adc8886b21f857c10828f..10d30e3bd526ec1106f74948beb427da70d68eed 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/histSizes.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/histSizes.py
@@ -13,11 +13,11 @@ def filledBins(h):
     return sum([1 for i in range(N) if h.At(i)!=0])
 
 def hasLabels(h):
-    return (h.GetXaxis().GetLabels()!=None)
+    return (h.GetXaxis().GetLabels() is not None)
 
 def missingLabels(h):
     l = h.GetXaxis().GetLabels()    
-    return (l!=None and h.GetXaxis().GetNbins()!=l.GetSize())
+    return (l is not None and h.GetXaxis().GetNbins()!=l.GetSize())
             
 def addDirList(dir,path,hists):
     list=dir.GetListOfKeys()
@@ -29,9 +29,9 @@ def addDirList(dir,path,hists):
         else:
             h = key.ReadObj()
             if not h.InheritsFrom('TH1'): continue
-            if opts.labeled==True and not hasLabels(h): continue
-            if opts.misslabel==True and not missingLabels(h): continue
-            if opts.empty==True and h.GetEntries()>0: continue
+            if opts.labeled is True and not hasLabels(h): continue
+            if opts.misslabel is True and not missingLabels(h): continue
+            if opts.empty is True and h.GetEntries()>0: continue
             b = filledBins(h) if opts.filled else h.GetSize()
             hists[path+name]=(cname,b)
         
@@ -58,8 +58,7 @@ def byName(hists, nameFunc):
     return algs
 
 def byAlg(hists):
-    f = lambda h : h.split('/',2)[1]
-    return byName(hists, f)
+    return byName(hists, lambda h : h.split('/',2)[1])
 
 
 def main():    
@@ -108,11 +107,11 @@ def main():
 
    if not opts.byAlg: opts.byName = True
 
-   if opts.byName==True:
+   if opts.byName is True:
        for h,v in sorted(hists.items(), key=sortKey):
            print('%-80s %10s %10s' % (h,v[0],v[1]))
 
-   if opts.byAlg==True:
+   if opts.byAlg is True:
        algs = byAlg(hists)
        for h,v in sorted(algs.items(), key=sortKey):
            print('%-80s %10s %10s' % (h,v[0],v[1]))
diff --git a/Trigger/TrigValidation/TrigValTools/bin/regtest.py b/Trigger/TrigValidation/TrigValTools/bin/regtest.py
index e13ea5aceca581ed7e2bd6c9f9c001a916a48703..84d6aa0d859f4391daf49b617d15a23a67c3b32b 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/regtest.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/regtest.py
@@ -1,6 +1,8 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+from __future__ import print_function
 
 import re
 import argparse
@@ -18,7 +20,7 @@ def progErrorExit(message, exitcode):
         failkey
     except NameError:
         failkey = 'FAILURE'
-    print 'regtest.py:', failkey, message  
+    print('regtest.py: %s %s' % (failkey, message))
     sys.exit(exitcode)
         
 def commandLine():
@@ -66,12 +68,12 @@ def commandLine():
                         )
     global args
     args = parser.parse_args()
-    if help == True: 
+    if help is True:
         usage()
         progErrorExit('usage', -1)
 
 def usage():
-    print'''
+    print('''
  Usage: regtest.py [options] 
 
   Testing tool for comparing marked lines in a log file against a reference
@@ -95,7 +97,7 @@ def usage():
   Technical info:
 
   Lines which match the regular expression
-  '''
+  ''')
 
 def regtest():
     with open(args.inputfile,'r') as inpfile:
@@ -105,29 +107,29 @@ def regtest():
                 matchline += 1
    
     if matchline == 0:
-        print '=== Alert!', failkey, 'no lines matching', linematch, 'in LOG'
+        print('=== Alert!', failkey, 'no lines matching', linematch, 'in LOG')
         result = True
         exit()
 
-    if debug == True:
-       print 'regtest.py,: debug: diff -b', args.inputfile, args.reffile
+    if debug is True:
+       print('regtest.py,: debug: diff -b', args.inputfile, args.reffile)
   
     command = 'diff -b ' +  args.inputfile + ' ' +  args.reffile
     rc = os.system(command)
-    if rc == False:
-       print '=== Output is the same as reference.'
+    if rc is False:
+       print('=== Output is the same as reference.')
 #       result = False
        result = 0
     else:
-        print '''=== Alert!''', failkey, '''input file (<) differs from reference (>)
-    If this change is understood, to update the reference file please type:
-    cp ''', args.inputfile, args.reffile
-#        result = True
+        print('''=== Alert!''', failkey, '''input file (<) differs from reference (>)
+        If this change is understood, to update the reference file please type:
+        cp ''', args.inputfile, args.reffile)
+        #        result = True
         result = 1
 
 
-    if debug == True:
-        print 'regtest.py  debug: returning result', result   
+    if debug is True:
+        print('regtest.py  debug: returning result', result)
     return result
 
 if __name__ == '__main__':
diff --git a/Trigger/TrigValidation/TrigValTools/bin/root2html.py b/Trigger/TrigValidation/TrigValTools/bin/root2html.py
index 2e7fbaeeb7833515866861b8f51bcd68aaf20ccb..0fbd03e0d8c0ecaed5f6aff27f862d43af3b90a7 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/root2html.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/root2html.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 # @file:    root2html.py
 # @purpose: Generate the index.htm for a list of root files
 # @author:  Will Buttinger
@@ -27,8 +27,6 @@ html_template = """
 
 
 import sys
-import os
-from AthenaCommon.Utils.unixtools import FindFile
 
 def main():
          
@@ -48,7 +46,7 @@ def main():
    o_html.writelines(html_template%d)
    o_html.flush()
    o_html.close()
-   print "Wrote index.htm file"
+   print("Wrote index.htm file")
 
    return 0
 
diff --git a/Trigger/TrigValidation/TrigValTools/bin/rootcomp.py b/Trigger/TrigValidation/TrigValTools/bin/rootcomp.py
index 56e0221bfa870f1df7fac36a47300560ef563be4..a2a5a081c90e51e6dc5f546f27d43bb8dd87f51e 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/rootcomp.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/rootcomp.py
@@ -9,44 +9,6 @@ from __future__ import print_function
 import sys
 import os
 import os.path
-from TrigValTools.TrigRootUtils import lsroot
-
-
-def diffFiles(ref,file,opts):
-   """Compare the keys in both files"""
-
-   import ROOT   
-   tref = ROOT.TFile(ref)
-   tfile = ROOT.TFile(file)
-   if tref.IsZombie() or tfile.IsZombie(): return
-   
-   lsref = lsroot(tref.GetDirectory(opts.refBaseDir))
-   lsfile = lsroot(tfile.GetDirectory(opts.fileBaseDir))
-   tref.Close()
-   tfile.Close()
-   del tref
-   del tfile
-
-   diff = list(set(lsref).difference(set(lsfile)))
-
-   import re
-   selection = lambda x: True
-
-   # Only show diffs that are not on the skip list ...
-   if len(opts.skip)>0:
-      selection = lambda x : not reduce(lambda a,b:a|b,[re.search(pat,x)!=None for pat in opts.skip])
-   # ... or pass the selection
-   elif len(opts.select)>0:
-      selection = lambda x : reduce(lambda a,b:a|b,[re.search(pat,x)!=None for pat in opts.select])
-      
-   refonly = filter(selection,filter(lambda s:s[0]=='-',diff))
-
-   if len(refonly)>0:
-      print("\nHistograms only found in reference:")
-      for s in refonly: print(s)
-
-   return
-
 
 def main():
       
@@ -184,12 +146,12 @@ def main():
 
 
    # Now import ROOT
-   import cppyy
+   import cppyy  # noqa: F401
    try:
       from PerfMonAna import PyRootLib
       ROOT = PyRootLib.importRoot( batch=True )
    except ImportError:
-      import ROOT
+      import ROOT   # noqa: F401
 
    sys.stdout.flush()
    sys.stderr.flush()
@@ -247,9 +209,6 @@ def main():
       print("GZipping postscript file -> %s.ps.gz" % opts.outFile)
       os.system("gzip -f %s.ps" % (opts.outFile))
 
-   # List histograms that are only found in reference
-   #diffFiles(args[0],args[1],opts)  # this is quite slow, disable it
-
    if rc != 0:
       result = 255
    elif valid.totalHist()>0:
diff --git a/Trigger/TrigValidation/TrigValTools/bin/trigDumpTimers.py b/Trigger/TrigValidation/TrigValTools/bin/trigDumpTimers.py
index 7affe68bfe7641e1ad007c3550515ec793bd7ccb..6f1ba6fee5f22446fe6e7fe9e56322b05a9144bb 100755
--- a/Trigger/TrigValidation/TrigValTools/bin/trigDumpTimers.py
+++ b/Trigger/TrigValidation/TrigValTools/bin/trigDumpTimers.py
@@ -1,14 +1,12 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-# @file:    dumpTimes.py
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# @file:    trigDumpTimers.py
 # @purpose: Script to dump the timing histograms from expert-monitoring.root/TIMERS
 # @author:  Stewart Martin-Haugh
 
-import argparse
 import ROOT
 from optparse import OptionParser
-import os
 import re
 from TrigValTools.TrigRootUtils import lsroot
 
@@ -31,7 +29,7 @@ def get_matches(pattern, exclude, myFile):
   if exclude:
     regex_exclude = re.compile(".*" + exclude + ".*")
   for name in names:
-    if not "TIME" in name:
+    if "TIME" not in name:
       continue
     if not regex.match(name):
       continue
@@ -44,18 +42,18 @@ def get_matches(pattern, exclude, myFile):
 def main():
   parser = OptionParser()
   parser.add_option("-p", "--pattern", dest="pattern", type = "string", default = None,
-                                                                      help="Pattern to match histogram to")
+                    help="Pattern to match histogram to")
   parser.add_option("-x", "--exclude", dest="exclude", type = "string", default = None,
-                                                                      help="Pattern to exclude histogram from matching")
+                    help="Pattern to exclude histogram from matching")
   (options, args) = parser.parse_args()        
-  print(options, args)
+
   for arg in args:
     print(arg)
     myFile = ROOT.TFile(arg)
     pattern = ".*"
     if (options.pattern):
       pattern = options.pattern
-    get_matches(options.pattern, options.exclude, myFile)
+    get_matches(pattern, options.exclude, myFile)
 
     
 if __name__ == "__main__":
diff --git a/Trigger/TrigValidation/TrigValTools/bin/trigslimval.py b/Trigger/TrigValidation/TrigValTools/bin/trigslimval.py
deleted file mode 100755
index a022377a035dcdd5f9fe9a4788f6b26d888aa7e8..0000000000000000000000000000000000000000
--- a/Trigger/TrigValidation/TrigValTools/bin/trigslimval.py
+++ /dev/null
@@ -1,540 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
-#
-# @file   TrigSlimVal.py
-# @brief  Checks the output of TrigSlimValidation run logs
-# @author Ben Smith <bcsmith@fas.harvard.edu>
-
-import os
-import sys
-from sets import Set
-
-# Global variables
-variables = Set()
-failures = list()
-testsRun = 0
-
-# variable class
-class Variable:
-  name = ""
-  string = ""
-
-  def __init__(self, name, string):
-    self.name = name
-    self.string = string
-  def __repr__(self):
-    return 'Variable \'' + self.name + '\' from string \'' + self.string + '\''
-
-# comparison class
-class Comparison:
-  name = "Comparison"
-  string = ""
-
-  def __init__(self, name, string):
-    self.name = name
-    self.string = string
-  def __repr__(self):
-    return 'Comparison \'' + self.name + '\' with definition \'' + self.string + '\''
-
-  def passes(self, test, event):
-    # replace words with variable values
-    words = self.string.split(' ')
-    comparison = '='
-    currside = 0
-    leftVal = 0
-    rightVal = 0
-    operation = '+'
-    allVals = list()
-    for word in words:
-      # check if its an operation
-      if len(word) == 1 and not word.isdigit():
-        if word == '+':
-          operation = '+'
-          allVals.append('+')
-        elif word == '-':
-          operation = '-'
-          allVals.append('-')
-        elif word == '=':
-          operation = '+'
-          comparison = '='
-          allVals.append('=')
-          currside = 1
-        elif word == '<':
-          operation = '+'
-          comparison = '<'
-          allVals.append('<')
-          currside = 1
-        elif word == '>':
-          operation = '+'
-          comparison = '>'
-          allVals.append('>')
-          currside = 1
-        else:
-          return 'Error: cannot parse word ' + word + ' from comparison ' + self.string
-        continue
-      # replace the word with variable value
-      val = ''
-      if word.startswith('BASE.'):
-        val = test.getBaseVal(word.replace('BASE.', ''), event)
-      elif word.startswith('MOD.'):
-        val = test.getModifiedVal(word.replace('MOD.', ''), event)
-      elif word.isdigit():
-        val = int(word)
-      if isinstance(val, basestring) and val.count('not defined in') > 0:
-        val = 0
-      if not isinstance(val, int):
-        return 'ERROR: Cannot parse word ' + word + ' from comparison ' + self.string
-      
-      allVals.append('%d' % val)
-      if operation == '-':
-        val = val * -1
-      if currside == 0:
-        leftVal += val
-      else:
-        rightVal += val
-    
-    if leftVal == 0 and rightVal == 0:
-      return 'PASSED'
-    if comparison == '=' and leftVal == rightVal:
-      return 'PASSED'
-    if comparison == '<' and leftVal < rightVal:
-      return 'PASSED'
-    if comparison == '>' and leftVal > rightVal:
-      return 'PASSED'
-      
-    message = 'FAILED comparison \'' + self.name +'\': ' + self.string
-    message += ' ( '
-    for x in allVals:
-      message += x + ' '
-    message += ')'
-    message += ' Event: %d' % event
-    return message
-
-# test class
-class Test:
-  name = "TestClass"
-  alterableVariables = Set()
-  comparisons = Set()
-  baseValDictArray = list()
-  modifiedValDictArray = list()
-
-  def __init__(self, name):
-    self.name = name
-  def __repr__(self):
-    str = 'Test ' + self.name
-    str += '\n\tAlterableVariables: ' + self.alterableVariables.__repr__()
-    str += '\n\tComparisons: ' + self.comparisons.__repr__()
-    str += '\n\tBase value dictionary array: ' + self.baseValDictArray.__repr__()
-    str += '\n\tModified value dictionary array: ' + self.modifiedValDictArray.__repr__()
-    return str
-
-  def getBaseVal(self, name, event):
-    if not len(self.baseValDictArray) > event:
-      return 'No entries in the base value dictionary for event %d' % event
-    baseValDict = self.baseValDictArray[event];
-    v = getVariable(name)
-    if not isinstance(v, Variable):
-      return v
-    if not v in baseValDict:
-      return 'Variable ' + name + ' not defined in the base value dictionary'
-    return baseValDict[v]
-
-  def getModifiedVal(self, name, event):
-    if not len(self.modifiedValDictArray) > event:
-      return 'No entries in the modified value dictionary for event %d' % event
-    modifiedValDict = self.modifiedValDictArray[event];
-    v = getVariable(name)
-    if not isinstance(v, Variable):
-      return v
-    if not v in modifiedValDict:
-      return 'Variable ' + name + ' not defined in the modified value dictionary'
-    return modifiedValDict[v]
-
-  def runTests(self):
-    global testsRun
-    global failures
-    
-    testsRun = testsRun + 1
-    # loop through events and check each one
-    if not len(self.baseValDictArray) == len(self.modifiedValDictArray):
-      return 'Initialization error: base and modified value dictionary arrays have different sizes'
-    i = 0
-    for i in range(0, len(self.baseValDictArray)):
-      # check that the non-alterable variables have not changed
-      for v in variables.difference(map(getVariable, self.alterableVariables)):
-        if isinstance(self.getBaseVal(v.name, i),basestring):
-          continue
-        c = Comparison('UnalterableVariable: ' + v.name, 'BASE.' + v.name + ' = ' + 'MOD.' + v.name)
-        val = c.passes(self, i)
-        if not val == 'PASSED':
-          failures.append(self.name + ': ' + val)
-          return val
-      # check that the comparisons pass
-      for c in self.comparisons:
-        val = c.passes(self, i)
-        if not val == 'PASSED':
-          failures.append(self.name + ': ' + val)
-          return val
-    # if we made it this far, then we passed!
-    return 'PASSED'
-
-# variable functions
-def getVariable(name):
-  for v in variables:
-    if v.name == name:
-      return v
-  return 'Variable ' + name + ' not found'
-
-def addVariable(name, string):
-  if isinstance(getVariable(name), Variable):
-    return 'Could not add variable ' + name + '... Variable already exists'
-  v = Variable(name, string)
-  variables.add(v)
-  return v
-
-# parsing functions
-def matchLine(line, valDict):
-  # ignore blank lines
-  if len(line) == 0:
-    return ''
-  # check if any of the variable strings appear in the line
-  for v in variables:
-    if line.count(v.string) > 0:
-      if v in valDict: 
-        return 'Wanted to assign line ' + line + ' to var ' + v.name + ' but ' + v.name + ' is already assigned!'
-      # split the line by the colon
-      words = line.split(':')
-      if not len(words) == 2:
-        message = 'Wanted to assign line ' + line + ' to var ' + v.name
-        message = message + ' but line has wrong num (' + words.length() + ') of words'
-        return messsage
-      val = int(words[1].replace(' ', '').replace('\n', ''))
-      valDict[v] = val
-      return ''
-  return 'Unable to match line ' + line
-  
-def parseEvent(log, valDict):
-  for line in log:
-    errorVal = matchLine(line, valDict)
-    if not errorVal == '': 
-      return errorVal 
-  return '' 
-
-def parseLog(logFile, valDictArray):
-  if not os.path.exists(logFile):
-    return 'File ' + logFile + ' does not exist'
-  file = open(logFile, 'r')
-
-  # split the log file by event by looking for the lines
-  startBlock = 'START of TrigSlimValidation DUMP'
-  endBlock = 'END of TrigSlimValidation DUMP'
-  log = list()
-  inBlock = False
-  eventNum = 1
-
-  for line in file.readlines():
-    # if we see a start block, prepare to read an event
-    if line.count(startBlock) > 0:
-      if inBlock:
-        return 'Wanted to start event %d, but event %d never closed' % (eventNum, eventNum - 1)
-      inBlock = True
-      log[:] = []
-      continue
-    if line.count(endBlock) > 0:
-    # if we see an end block, close event and parse it
-      if not inBlock:
-        return 'Wanted to end event %d, but event was never opened!' % (eventNum)
-      valDict = dict()
-      errorVal = parseEvent(log, valDict)
-      valDictArray.append(valDict)
-      if not errorVal == '':
-        return errorVal
-      inBlock = False
-      eventNum = eventNum + 1
-      continue
-    # if we're in an event block, add the log line
-    # otherwise, just ignore the line
-    if inBlock:
-      log.append(line)
-  file.close()
-
-  return ''
-
-def setupTest(test, baseLog, modifiedLog):
-  test.baseValDictArray = list()
-  errorVal = parseLog(baseLog, test.baseValDictArray)
-  if not errorVal == '':
-    print '\nParsing the base log ' +  baseLog + ' for test',
-    print test.name + ' failed with the following error:'
-    print errorVal
-    print 'This is unrecoverable... exiting!\n'
-    exit()
-  test.modifiedValDictArray = list()
-  errorVal = parseLog(modifiedLog, test.modifiedValDictArray)
-  if not errorVal == '':
-    print '\nParsing the modified log ' +  modifiedLog + ' for test',
-    print test.name + ' failed with the following error:'
-    print errorVal
-    print 'This is unrecoverable... exiting!\n'
-    exit()
-
-
-############## TrigSlimValidation specific code #################
-def buildObjectList(log, key):
-  s = Set()
-
-  if not os.path.exists(log):
-    return 'File ' + log + ' does not exist'
-  file = open(log, 'r')
-  for line in file.readlines():
-    if line.count(key) > 0:
-      for word in line.split(' '):
-        if word.count(':') > 0:
-          s.add(word.replace(':', ''))
-          break
-  return s
-
-def buildStreamRemovedTest(stream, baseLog, modLog, features):
-  RSTest = Test('RS' + stream)
-  setupTest(RSTest, baseLog, modLog)
-  RSTest.alterableVariables = Set(['elements', 'intermediates', 'stream'+stream, 'links', 'features', 'featureless', 'ghosts', 'seeds', 'seededBy', 'sameRoI','longest', 'unassociated'])
-  RSTest.alterableVariables.add('unassociatedgroup')
-  # All features can change, as entire streams are removed
-  for f in features:
-    RSTest.alterableVariables.add('feature'+f)
-  RSTest.comparisons = Set([Comparison('StreamRemoved', 'MOD.stream' + stream + ' < BASE.stream' + stream)])
-  return RSTest
-
-def buildFeatureRemovedTest(feature, baseLog, modLog):
-  RFTest = Test('RF'+feature)
-  setupTest(RFTest, baseLog, modLog)
-  RFTest.alterableVariables = Set(['features','featureless','links','feature'+feature])
-  RFTest.comparisons = Set([Comparison('DistinctFeatures', 'BASE.features = MOD.features + 1'),
-                        Comparison('FeatureLinks', 'BASE.links < 1 + MOD.links + BASE.feature'+feature),
-                        Comparison('FeatureRemoved', 'MOD.feature'+feature+' = 0')])
-  return RFTest
-  
-def buildSqueezeTest(baseLog, modLog, streams, features):
-  SqueezeTest = Test('Squeeze')
-  setupTest(SqueezeTest, baseLog, modLog)
-  SqueezeTest.alterableVariables = Set(['intermediates', 'seeds', 'seededBy', 'sameRoI', 'elements', 'unassociated', 'longest', 'shortest', 'links','unassociatedgroup'])
-  for s in streams:
-    SqueezeTest.alterableVariables.add('stream'+s)
-  SqueezeTest.comparisons = Set([Comparison('IntermeidateElements','MOD.intermediates = 0'),
-                             Comparison('Elements', 'MOD.elements + BASE.intermediates = BASE.elements'),
-                             Comparison('LongestChain', 'MOD.longest = 3'),
-                             Comparison('ShortestChain', 'MOD.shortest < 4')])
-  # When you squeeze, you can actually increase the number of occurrences of a given feature
-  # if you remove a node with multiple children.  Thus, we need to check that the number
-  # of occurrences of each feature is the same or larger
-  for f in features:
-    SqueezeTest.alterableVariables.add('feature'+f)
-    SqueezeTest.comparisons.add(Comparison('FeatureIncrease'+f, 'MOD.feature'+f+' + 1 > BASE.feature' + f)) 
-  return SqueezeTest
-
-def buildNoSlimTest(baseLog, modLog):
-  # No Slim Test
-  # Nothing changes, so this is super easy!
-  NoSlimTest = Test("NoSlimTest")
-  setupTest(NoSlimTest, baseLog, m)
-  return NoSlimTest
-
-def buildCombinedTest(test1, test2):
-  test = Test(test1.name + test2.name)
-  test.alterableVariables = test1.alterableVariables.union(test2.alterableVariables)
-  test.comparisons = test1.comparisons.union(test2.comparisons)
-  test.baseValDictArray = test1.baseValDictArray
-  test.modifiedValDictArray = test1.modifiedValDictArray
-  # There are some special rules for combining RF and squeeze tests
-  # Basically, squeeze tests require that the number of occurrences of a feature
-  # stays the same or increases.  We need to remove this required when we combine
-  # with an RF test, but only for the feature that was removed
-  toRemove = Set()
-  if test1.name.count('RF') > 0:
-    feature = test1.name.replace('RF', '')
-    for c in test.comparisons:
-      if c.name.startswith('FeatureIncrease') and c.name.count(feature) > 0:
-        toRemove.add(c)
-  if test2.name.count('RF') > 0:
-    feature = test2.name.replace('RF', '')
-    for c in test.comparisons:
-      if c.name.startswith('FeatureIncrease') and c.name.count(feature) > 0:
-        toRemove.add(c)
-  test.comparisons = test.comparisons.difference(toRemove)
-  return test
-  
-# Begin main
-
-def main():
-
-  # read command line options
-  from optparse import OptionParser
-  parser = OptionParser(usage = "usage: %prog [options]",
-                       description = "Compares output of TrigSlimValAlg on slimmed and unslimmed AODs to validate TrigNavigationSlimming.")
-  parser.add_option("-b", "--base", action = "store", dest = "baseLog", default = "TrigSlimValidation_Base.log", help = "Base log file")
-  parser.add_option("-m", "--modified", action = "append", dest = "modifiedLog", default = list(), help = "Modified log file")
-  parser.add_option("-t", "--test", action = "append", dest = "testType", default = list(), help = "Test to run")
-  parser.add_option("-f", "--feature", action = "append", dest = "removedFeature", default = list(), help = "Removed feature (if needed by test)")
-  parser.add_option("-s", "--stream", action = "append", dest = "removedStream", default = list(), help = "Removed stream (if needed by test)")
-
-  (opts, args) = parser.parse_args()
-
-  if len(args) > 0:
-    print
-    print 'Unable to parse option: ' + args[0]
-    print 'This is unrecoverable... exiting!\n'
-    print
-    exit()
-
-  # Populate all variables
-  baseLog = opts.baseLog
-  modifiedLogs = opts.modifiedLog
-  testTypes = opts.testType
-  removedFeatures = opts.removedFeature
-  removedStreams = opts.removedStream
-  if len(modifiedLogs) == 0 and len(testTypes) == 0:
-    print
-    print 'You must specify either a modified log (with -m) or a test type (with -t)'
-    print
-    exit()
-  if len(modifiedLogs) == 0:
-    for t in testTypes:
-      modifiedLog = 'TrigSlimValidation_' + t + '.log'
-      print
-      print 'Assuming modified log file at ' + modifiedLog + ' based upon test type ' + t
-      print
-      modifiedLogs.append(modifiedLog)
-  if len(testTypes) == 0:
-    for l in modifiedLogs:
-      frontRemoved = l.split('_')
-      if not len(frontRemoved) == 2:
-        print
-        print 'Unable to determine implicit test type from modified log', 
-        print l + '... you should supply it explicity with -t'
-        print
-        exit()
-      backRemoved = frontRemoved[1].split('.')
-      if not len(backRemoved) == 2:
-        print
-        print 'Unable to determine implicit test type from modified log', 
-        print l + '... you should supply it explicity with -t'
-        print
-        exit()
-      t = backRemoved[0]
-      print
-      print 'Assuming test type ' + t + ' based upon modified log file ' + l
-      print
-      testTypes.append(t)
-
-  # Build feature, stream, and group lists
-  features = buildObjectList(baseLog, 'Occurrences of feature')
-  if not isinstance(features, Set):
-    print
-    print 'Unable to build feature list due to the following error:'
-    print features
-    print 'This is unrecoverable... exiting!\n'
-    exit()
-  streams = buildObjectList(baseLog, 'Elements associated with stream')
-  if not isinstance(streams, Set):
-    print
-    print 'Unable to build stream list due to the following error:'
-    print streams
-    print 'This is unrecoverable... exiting!\n'
-    exit()
-  groups = buildObjectList(baseLog, 'Elements associated with group')
-  if not isinstance(groups, Set):
-    print
-    print 'Unable to build group list due to the following error:'
-    print groups
-    print 'This is unrecoverable... exiting!\n'
-    exit()
-
-  # Define needed variables
-  addVariable('elements', 'Number of trigger elements:')
-  addVariable('features', 'Number of distinct features:')
-  addVariable('intermediates', 'Number of intermediate trigger elements:')
-  addVariable('featureless', 'Number of featureless trigger elements:')
-  addVariable('ghosts', 'Number of ghost trigger elements:')
-  addVariable('RoIs', 'Number of RoIs:')
-  addVariable('longest', 'Longest chain:')
-  addVariable('shortest', 'Shortest chain:')
-  addVariable('links', 'Number of feature links:')
-  addVariable('seeds', 'Number of seeds relations:')
-  addVariable('seededBy', 'Number of seeded by relations:')
-  addVariable('sameRoI', 'Number of same RoI relations:')
-
-  # add feature, stream, and group variables
-  for f in features:
-    addVariable('feature'+f, 'Occurrences of feature ' + f + ':')
-  for s in streams:
-    addVariable('stream'+s, 'Elements associated with stream ' + s + ':')
-  for g in groups:
-    addVariable('group'+g, 'Elements associated with group ' + g + ':')
-  addVariable('unassociated', 'Elements unassociated with any stream:')
-  addVariable('unassociatedgroup', 'Elements unassociated with any group:')
-
-  # Define some useful tests
-  AllTests = list()
-
-  for t,m in zip(testTypes,modifiedLogs):
-    if t == 'NoSlim':
-      AllTests.append(buildNoSlimTest(baseLog, m))
-    if t == 'RF':
-      if len(removedFeatures) == 0:
-        print
-        print "Could not find feature to remove for test " + t + ' with modified log file ' + m
-        print
-        exit()
-      AllTests.append(buildFeatureRemovedTest(removedFeatures.pop(0), baseLog, m))
-    if t == 'RS':
-      if len(removedStreams) == 0:
-        print
-        print "Could not find stream to remove for test " + t + ' with modified log file ' + m
-        print
-        exit()
-      AllTests.append(buildStreamRemovedTest(removedStreams.pop(0), baseLog, m, features))
-    if t == 'Squeeze':
-      AllTests.append(buildSqueezeTest(baseLog, m, streams, features)) 
-    if t == 'SqueezeRF':
-      if len(removedFeatures) == 0:
-        print
-        print "Could not find feature to remove for test " + t + ' with modified log file ' + m
-        print
-        exit()
-      SqueezeTest = buildSqueezeTest(baseLog, m, streams, features)
-      RFTest = buildFeatureRemovedTest(removedFeatures.pop(0), baseLog, m)
-      AllTests.append(buildCombinedTest(SqueezeTest, RFTest))
-
-  # Check the tests
-  print
-  print 'Running tests...'
-  print
-
-  for t in AllTests:
-    print t.name + ': ' + t.runTests()
-
-  print
-  print 'Tests complete!'
-  print
-
-  # Print the final report
-  print
-  print 'Final report:'
-  print 'Tests passed: %d/%d' % (testsRun - len(failures), testsRun)
-  print 'Tests failed: %d/%d' % (len(failures), testsRun)
-  print
-
-  if len(failures) > 0:
-    print
-    print "trigslimval.py ERROR: Failures found"
-    print
-    print 'Failures:'
-    for f in failures:
-      print f
-    print
-    print
-
-if __name__ == "__main__":
-   sys.exit(main())
-
diff --git a/Trigger/TrigValidation/TrigValTools/html/root2html/index.html b/Trigger/TrigValidation/TrigValTools/html/root2html/index.html
deleted file mode 100644
index faa2a8df09bd598e5283efb9338d3a7af250e975..0000000000000000000000000000000000000000
--- a/Trigger/TrigValidation/TrigValTools/html/root2html/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<!-- Main html file for root2html --!>
-
-<html>
-  <head>
-    <title>HLT monitoring histograms</title>
-  </head>
-  <frameset cols="320,*" border="0">
-    <frame src="tree.html" name="tree"></frame>
-    <frame src="" name="img"></frame>
-  </frameset>
-</html>
diff --git a/Trigger/TrigValidation/TrigValTools/html/root2html/tree.html b/Trigger/TrigValidation/TrigValTools/html/root2html/tree.html
deleted file mode 100644
index fc7a2ea9109347529edb1f890a399db05793e115..0000000000000000000000000000000000000000
--- a/Trigger/TrigValidation/TrigValTools/html/root2html/tree.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<!-- Tree navigation for root2html -->
-  
-<html>
-  <body>
-    <link rel="STYLESHEET" type="text/css" href="http://atlas-project-trigger-release-validation.web.cern.ch/atlas-project-trigger-release-validation/www/dhtmlxTree/css/dhtmlXTree.css">
-      <script src="http://atlas-project-trigger-release-validation.web.cern.ch/atlas-project-trigger-release-validation/www/dhtmlxTree/js/dhtmlXCommon.js"></script>
-      <script src="http://atlas-project-trigger-release-validation.web.cern.ch/atlas-project-trigger-release-validation/www/dhtmlxTree/js/dhtmlXTree.js"></script>
-      
-      <input type="button" name="draw" value="Show selected" onClick="drawSelected()"></input>
-      <input type="button" name="clear" value="ClearAll" onClick="clearAll()"></input>
-      <p><font size="-2">
-          To show histograms, click on single leaf or select multiple and click on "Show selected".
-          (Please be patient while the tree loads...)
-      </font></p>
-      <div id="naviTree" style="width:300"></div>
-      <div id="footer"><font size="-2">Created by root2html<br>(C) Frank Winklmeier<br>powered by <a href="http://www.scbr.com/docs/products/dhtmlxTree/" target="new">dhtmlXTree</a></font></div>
-      <script language="JavaScript">
-        tree = new dhtmlXTreeObject('naviTree',"100%","80%",0);
-        tree.setImagePath("http://atlas-project-trigger-release-validation.web.cern.ch/atlas-project-trigger-release-validation/www/dhtmlxTree/imgs/");
-        tree.enableCheckBoxes(true);
-        tree.enableThreeStateCheckboxes(true);
-        tree.loadXML("tree.xml");
-        tree.setOnClickHandler(doOnClick);
-
-        // onClick event handler
-        // Load image path on click
-        function doOnClick(nodeId) {
-          var imgPath = tree.getUserData(nodeId,"img");
-          if (imgPath != undefined) parent["img"].location.href = imgPath;
-        }
-
-        // show all selected plots
-        function drawSelected() {
-          var doc = parent["img"].document;
-          doc.open();
-          doc.write("<html><body>");
-
-          // get list of all selected elements
-          var ids = tree.getAllChecked().split(',');
-          // loop over all elements and write <img> tag into frame
-          for (var i in ids) {
-            var imgPath = tree.getUserData(ids[i],"img");
-            if (imgPath != undefined) {
-              doc.write('<img src="');
-              doc.write(imgPath);
-              doc.write('"');
-            }
-          }
-          doc.write("</body></html>");
-          doc.close()              
-        }
-
-        // clear all selected items
-        function clearAll() {
-          for (var i in tree.getAllChecked().split(',')) {
-            tree.setCheck(i,false);
-          }
-        }
-      </script>
-
-  </body>
-</html>
diff --git a/Trigger/TrigValidation/TrigValTools/src/TRoot2Html.cxx b/Trigger/TrigValidation/TrigValTools/src/TRoot2Html.cxx
deleted file mode 100644
index 93d24229bec67448e8868a90acab6977175e5fc6..0000000000000000000000000000000000000000
--- a/Trigger/TrigValidation/TrigValTools/src/TRoot2Html.cxx
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
-  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
-*/
-
-/**
- * @file   TRoot2Html.cxx
- * @brief  TRoot2Html implementation
- * @author Frank Winklmeier
- *
- * $Id: TRoot2Html.cxx,v 1.1 2008-07-28 11:02:00 fwinkl Exp $
- */
-
-#include "TrigValTools/TRoot2Html.h"
-
-#include <iostream>
-#include <sstream>
-#include "TError.h"
-#include "TClass.h"
-#include "TH1.h"
-#include "TFile.h"
-#include "TCanvas.h"
-#include "TSystem.h"
-#include "TKey.h"
-#include "TPRegexp.h"
-
-using namespace std;
-
-TRoot2Html::TRoot2Html() :
-  m_nodeId(1),
-  m_showFullFileName(kFALSE)
-{
-  setOutputDir("./");
-  setImageSize(400,400);
-}
-
-TRoot2Html::TRoot2Html(const TRoot2Html& other):
-  TMultiFileLooper(other),
-  m_imgHeight(other.m_imgHeight),
-  m_imgWidth(other.m_imgWidth),
-  m_nodeId(other.m_nodeId),
-  m_showFullFileName(other.m_showFullFileName)
-{
-}
-
-void TRoot2Html::beginJob()
-{  
-   m_xml.open(m_outDir+"/tree.xml");
-  if (!m_xml) {
-    cout << "Cannot write to directory " << m_outDir << endl;
-    return;
-  }
-  
-  m_xml << "<?_xml version='1.0' encoding='iso-8859-1'?>" << endl;
-  m_xml << "<!-- This file was auto generated by root2html -->" << endl;
-  m_xml << "<!-- Use it with the dhtmlxTree component (http://www.scbr.com/docs/products/dhtmlxTree) -->" << endl;
-  m_xml << "<tree id=\"0\">" << endl;
-  m_nodeId = 1;
-}
-
-void TRoot2Html::endJob()
-{
-  m_xml << "</tree>" << endl;
-  m_xml.close();
-}
-
-
-void TRoot2Html::beforeFile()
-{
-  TString treeNodeName;
-  if (m_showFullFileName) treeNodeName = file()->GetName();
-  else treeNodeName = gSystem->BaseName(file()->GetName());
-  
-  m_xml << "<!-- Start of " << treeNodeName << " -->" << endl;
-  m_xml << xmlTreeItem(treeNodeName).Data() << endl;
-  
-  // Create output directory
-  gSystem->mkdir(m_outDir+"/img", true);
-}
-
-void TRoot2Html::afterFile()
-{
-  m_xml << xmlTreeItemClose().Data() << endl;
-}
-
-
-void TRoot2Html::beforeDir()
-{
-  TString s(getPathFromDir(*gDirectory));
-  TString imgDir = TString(m_outDir) + "/img/" + s;
-  gSystem->mkdir(imgDir, true);
-
-  m_xml << xmlTreeItem(gDirectory->GetName()).Data() << endl;
-}
-
-void TRoot2Html::afterDir()
-{
-  m_xml << xmlTreeItemClose().Data() << endl;
-}
-
-
-void TRoot2Html::processKey(TDirectory& dir, TKey& key)
-{
-  dir.cd();
-  
-  TObject* obj = key.ReadObj();
-  if (obj->IsA()->InheritsFrom("TH1")) {
-    m_xml << xmlTreeItem(key.GetName()).Data() << endl;
-    TString imgPath = hist2Png(*gDirectory, key.GetName());
-    if (imgPath!="") {
-      m_xml << xmlUserData("img",imgPath).Data() << endl;
-    }
-    m_xml << xmlTreeItemClose().Data() << endl;
-  }
-}
-
-
-// Save histogram 'name' from 'dir' in 'm_outDir/img'
-// Return "" on error otherwise image path relative to m_outDir
-TString TRoot2Html::hist2Png(TDirectory& dir, const TString& name)
-{  
-  TH1* h = (TH1*)dir.Get(name);
-  if (h==0) {
-    cout << "hist2Png: Cannot load histogram " << name << endl;
-    return "";
-  }
-  
-  TCanvas c("c","c",m_imgWidth,m_imgHeight);
-  TString options(getDrawOptions(*h));
-  if (m_verbose) cout << "Drawing histogram " << h->GetName()
-                      << " (" << h->ClassName() << ") with options '"
-                      << options << "'" << endl;
-  h->Draw(options);
-  TString s(getPathFromDir(dir));
-  TString pngName = "img/" + s + "/" + name + ".png";
-
-  // Suppress the info message when saving file
-  Int_t oldIgnoreLevel = gErrorIgnoreLevel;
-  if (!m_verbose) gErrorIgnoreLevel = kWarning;
-  c.SaveAs(m_outDir+"/"+pngName);
-
-  gErrorIgnoreLevel = oldIgnoreLevel;
-  
-  return pngName;
-}
-
-
-// Set draw options for all histograms matching re
-void TRoot2Html::addDrawOptions(const char* regexp, const char* options)
-{
-  if (regexp && options) {
-    TPRegexp* re = new TPRegexp(regexp);
-    if (re) m_drawOptions.push_back(std::pair<TPRegexp*,TString>(re,options));
-  }
-}
-
-void TRoot2Html::addClassDrawOptions(const char* regexp, const char* options)
-{
-  if (regexp && options) {
-    TPRegexp* re = new TPRegexp(regexp);
-    if (re) m_classDrawOptions.push_back(std::pair<TPRegexp*,TString>(re,options));
-  }
-}
-
-
-// return draw options for specified histogram
-TString TRoot2Html::getDrawOptions(const TH1& h)
-{
-  TString options("");
-  
-  // First check if we have class wide draw options for this histogram
-  vector< pair<TPRegexp*,TString> >::iterator iter;
-  for (iter=m_classDrawOptions.begin(); iter!=m_classDrawOptions.end(); iter++) {
-    if (iter->first->Match(h.ClassName())>0) {
-      options = iter->second;
-      break;
-    }
-  }
-  
-  // Check if any regexp matches the histogram name
-  for (iter=m_drawOptions.begin(); iter!=m_drawOptions.end(); iter++) {
-    if (iter->first->Match(h.GetName())>0) {
-      options = iter->second;
-      break;
-    }
-  }
-  
-  return options;
-}
-
-// Tree node with text and id
-TString TRoot2Html::xmlTreeItem(const char* text)
-{
-  TString s;
-  s.Form("<item text=\"%s\" id=\"%d\">",text,m_nodeId);
-  m_nodeId++;
-  return s;
-}
-
-// Tree node close
-TString TRoot2Html::xmlTreeItemClose()
-{
-  return "</item>";
-}
-
-// User data for tree node
-TString TRoot2Html::xmlUserData(const char* name, const char* data)
-{
-  TString s;
-  s.Form("<userdata name=\"%s\">%s</userdata>",name,data);
-  return s;
-}
diff --git a/Trigger/TrigValidation/TrigValTools/test/test_rootcomp.C b/Trigger/TrigValidation/TrigValTools/test/test_rootcomp.C
new file mode 100644
index 0000000000000000000000000000000000000000..bd3266216a70822353c0e9da92fc1afcad215daf
--- /dev/null
+++ b/Trigger/TrigValidation/TrigValTools/test/test_rootcomp.C
@@ -0,0 +1,36 @@
+// Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+// Create two ROOT files with histograms used in test_rootcomp.sh
+{
+  // Histogram for both files:
+  TH1D h1("h1", "", 10, 0, 10);
+  h1.Fill(5);
+  {
+    TFile f("hist1.root", "RECREATE");
+    h1.Write();
+
+    TH1D hr("hrandom", "", 10, 0, 10);
+    hr.FillRandom("gaus", 1000);
+
+    TH1D hl("hlabel", "", 5, 0, 5);
+    hl.Fill("a", 1);
+    hl.Fill("b", 2);
+
+    TH1D h2("hextra", "", 10, 0, 10);
+
+    f.Write();
+  }
+
+  {
+    TFile f("hist2.root", "RECREATE");
+    h1.Write();
+
+    TH1D hr("hrandom", "", 10, 0, 10);
+    hr.FillRandom("gaus", 1000);
+
+    TH1D hl("hlabel", "", 4, 0, 4);   // one less (empty) bin than above
+    hl.Fill("b", 2);
+    hl.Fill("a", 1);
+
+    f.Write();
+  }
+}
diff --git a/Trigger/TrigValidation/TrigValTools/test/test_rootcomp.sh b/Trigger/TrigValidation/TrigValTools/test/test_rootcomp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9ee20934588ad0d322c8fc5687147f30f2865456
--- /dev/null
+++ b/Trigger/TrigValidation/TrigValTools/test/test_rootcomp.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+#
+# Unit test for rootcomp.py
+
+if [ -z "$1" ]; then
+    echo "Usage: $0 root.C"
+    exit 1
+fi
+
+# Helpers:
+assert_pass() {
+    eval $@ || exit 1
+}
+
+assert_fail() {
+    eval $@ && exit 1
+}
+
+# Create histogram files:
+assert_pass root -l -b -n -q $1
+
+# Check default options:
+assert_pass rootcomp.py hist1.root hist1.root
+assert_pass test -f rootcomp.root
+assert_pass test -f rootcomp.ps
+
+# Default options for the following specific tests:
+opts="--noSkipList --noPS --noRoot"
+
+# Check regular histogram:
+assert_pass rootcomp.py hist1.root hist2.root $opts --select h1
+
+# Check label sorting:
+assert_pass rootcomp.py hist1.root hist2.root $opts --skip hrandom --sortLabels
+
+# Check failure for random histogram:
+assert_fail rootcomp.py hist1.root hist2.root $opts --sortLabels
+
+# Missing references:
+assert_fail rootcomp.py hist2.root hist1.root $opts --skip hrandom --sortLabels
+assert_pass rootcomp.py hist2.root hist1.root $opts --skip hrandom --sortLabels --ignoreMissingRef
+
+# If we get here all tests succeeded:
+exit 0
diff --git a/Trigger/TrigValidation/TrigValTools/test/test_unit_trigvalsteering.py b/Trigger/TrigValidation/TrigValTools/test/test_unit_trigvalsteering.py
index 8d8958e2b9c6d162ba73dba9d634f7139cefbc86..b77f263e9cfaa970f25fcb606a385a5c3d1c6015 100755
--- a/Trigger/TrigValidation/TrigValTools/test/test_unit_trigvalsteering.py
+++ b/Trigger/TrigValidation/TrigValTools/test/test_unit_trigvalsteering.py
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
-
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+#
 # This is not an ART test. This is a unit test of the framework used for
 # steering Trigger ART tests.