Commit 6935e1da64fd25d5132644b060d228402bd612ce

Authored by Jürgen Knödlseder
1 parent 9a18f090

Fix invalid pointer problem in GLog copies (#1823)

ChangeLog
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 * Version 1.2.0 released 3 * Version 1.2.0 released
4 ========================= 4 =========================
5 5
  6 + Fix invalid pointer problem in GLog copies (#1823)
6 Fix GSkyMap::publish() bug for empty sky maps (#1835) 7 Fix GSkyMap::publish() bug for empty sky maps (#1835)
7 Fix loss of application name and version (#1835) 8 Fix loss of application name and version (#1835)
8 Add methods for logging in applications (#1828) 9 Add methods for logging in applications (#1828)
@@ -62,6 +62,13 @@ The application name and version and now conserved in a GApplication::clear() @@ -62,6 +62,13 @@ The application name and version and now conserved in a GApplication::clear()
62 method call and the application is properly initialised like in the call of 62 method call and the application is properly initialised like in the call of
63 a constructor (#1835). 63 a constructor (#1835).
64 64
  65 +The GLog::copy_members() method simply copied the file pointer, hence after
  66 +a copy of a log instance there were two instances holding the same pointer
  67 +to the file, and as soon as one instance closes the log file the file
  68 +pointer in the second instance became invalid. This has been fixed by
  69 +duplicating the file descriptor and opening the log file in the copied
  70 +instance using the duplicated descriptor. This solves the problems (#1823).
  71 +
65 72
66 5. Base module 73 5. Base module
67 -------------- 74 --------------
src/app/GLog.cpp
@@ -36,6 +36,7 @@ @@ -36,6 +36,7 @@
36 #include <iostream> 36 #include <iostream>
37 #include <sstream> 37 #include <sstream>
38 #include <fstream> 38 #include <fstream>
  39 +#include <unistd.h> // dup
39 #include "GLog.hpp" 40 #include "GLog.hpp"
40 #include "GTools.hpp" 41 #include "GTools.hpp"
41 42
@@ -552,7 +553,7 @@ void GLog::init_members(void) @@ -552,7 +553,7 @@ void GLog::init_members(void)
552 /***********************************************************************//** 553 /***********************************************************************//**
553 * @brief Copy class members 554 * @brief Copy class members
554 * 555 *
555 - * @param[in] log Object from which members which should be copied. 556 + * @param[in] log Logger.
556 ***************************************************************************/ 557 ***************************************************************************/
557 void GLog::copy_members(const GLog& log) 558 void GLog::copy_members(const GLog& log)
558 { 559 {
@@ -563,12 +564,20 @@ void GLog::copy_members(const GLog&amp; log) @@ -563,12 +564,20 @@ void GLog::copy_members(const GLog&amp; log)
563 m_stderr = log.m_stderr; 564 m_stderr = log.m_stderr;
564 m_use_date = log.m_use_date; 565 m_use_date = log.m_use_date;
565 m_linestart = log.m_linestart; 566 m_linestart = log.m_linestart;
566 - m_file = log.m_file;  
567 m_filename = log.m_filename; 567 m_filename = log.m_filename;
568 m_name = log.m_name; 568 m_name = log.m_name;
569 m_buffer = log.m_buffer; 569 m_buffer = log.m_buffer;
570 m_chatter = log.m_chatter; 570 m_chatter = log.m_chatter;
571 571
  572 + // Copy file pointer by duplicating the file descriptor and opening
  573 + // the duplicated file descriptor
  574 + if (log.m_file != NULL) {
  575 + m_file = fdopen(dup(fileno(log.m_file)), "a");
  576 + }
  577 + else {
  578 + m_file = NULL;
  579 + }
  580 +
572 // Return 581 // Return
573 return; 582 return;
574 } 583 }
@@ -820,10 +829,3 @@ void GLog::append(std::string arg) @@ -820,10 +829,3 @@ void GLog::append(std::string arg)
820 // Return 829 // Return
821 return; 830 return;
822 } 831 }
823 -  
824 -  
825 -/*==========================================================================  
826 - = =  
827 - = Friends =  
828 - = =  
829 - ==========================================================================*/  
test/test_GApplication.py
1 # ========================================================================== 1 # ==========================================================================
2 -# This module performs unit tests for the GammaLib application module. 2 +# This module performs unit tests for the GammaLib application module
3 # 3 #
4 -# Copyright (C) 2012-2015 Juergen Knoedlseder 4 +# Copyright (C) 2012-2016 Juergen Knoedlseder
5 # 5 #
6 # This program is free software: you can redistribute it and/or modify 6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by 7 # it under the terms of the GNU General Public License as published by
@@ -25,150 +25,184 @@ import gammalib @@ -25,150 +25,184 @@ import gammalib
25 # ========================================== # 25 # ========================================== #
26 class Test(gammalib.GPythonTestSuite): 26 class Test(gammalib.GPythonTestSuite):
27 """ 27 """
28 - Test class for GammaLib application module. 28 + Test class for GammaLib application module
29 """ 29 """
30 # Constructor 30 # Constructor
31 def __init__(self): 31 def __init__(self):
32 """ 32 """
33 - Constructor. 33 + Constructor
34 """ 34 """
35 # Call base class constructor 35 # Call base class constructor
36 gammalib.GPythonTestSuite.__init__(self) 36 gammalib.GPythonTestSuite.__init__(self)
37 37
38 # Set members 38 # Set members
39 - self.logfile = "test_logger.log" 39 + self._logfile = 'test_logger.log'
40 40
41 # Return 41 # Return
42 return 42 return
43 43
44 - # Set test functions 44 +
  45 + # Private methods
  46 + def _check_log_file(self, ref, calls):
  47 + """
  48 + Check log file versus a reference
  49 +
  50 + Parameters
  51 + ----------
  52 + ref : list of str
  53 + Reference lines
  54 + calls : list of str
  55 + Methods that are tested
  56 + """
  57 + # Open log file, read all line and close file
  58 + file = open(self._logfile, 'r')
  59 + lines = file.readlines()
  60 + file.close()
  61 +
  62 + # Check all lines versus reference. There is a special kluge in case
  63 + # that the reference is "true\n" as in some Python/SWIG version this
  64 + # is displayed by "1\n". Both variants are tested to avoid version
  65 + # dependent test results.
  66 + for i, line in enumerate(lines):
  67 + if ref[i] == 'true\n':
  68 + self.test_assert(line == ref[i] or line == '1\n',
  69 + 'Check '+calls[i]+': '+line)
  70 + else:
  71 + self.test_value(line, ref[i], 'Check '+calls[i]+': '+line)
  72 +
  73 + # Return
  74 + return
  75 +
  76 +
  77 + # Public methods
45 def set(self): 78 def set(self):
46 """ 79 """
47 - Set all test functions. 80 + Set all test functions
48 """ 81 """
49 # Set test name 82 # Set test name
50 - self.name("app") 83 + self.name('app')
51 84
52 # Append tests 85 # Append tests
53 - self.append(self.test_log, "Test GLog")  
54 - self.append(self.test_pars, "Test GApplicationPars") 86 + self.append(self.test_log, 'Test GLog')
  87 + self.append(self.test_pars, 'Test GApplicationPars')
55 88
56 # Return 89 # Return
57 return 90 return
58 91
59 - # Test GLog  
60 def test_log(self): 92 def test_log(self):
61 """ 93 """
62 - Test GLog. 94 + Test GLog
63 """ 95 """
64 # Allocate logger 96 # Allocate logger
65 - log = gammalib.GLog(self.logfile, True) 97 + log = gammalib.GLog(self._logfile, True)
66 98
67 # Test print methods 99 # Test print methods
68 - log("Test __call__(std::string)")  
69 - log("\n") 100 + log('Test __call__(std::string)\n')
70 log(True) 101 log(True)
71 - log("\n") 102 + log('\n')
72 log(41) 103 log(41)
73 - log("\n") 104 + log('\n')
74 log(3.1415) 105 log(3.1415)
75 - log("\n")  
76 - log.parformat("This is a parameter")  
77 - log("\n")  
78 - log.toupper("upper case")  
79 - log("\n")  
80 - log.tolower("LOWER CASE")  
81 - log("\n")  
82 - log.fill("Higgs", 3)  
83 - log("\n")  
84 - log.left("Left", 10)  
85 - log("\n")  
86 - log.right("Right", 10)  
87 - log("\n")  
88 - log.centre("Centre", 10)  
89 - log("\n")  
90 - log.header0("Header 0")  
91 - log("\n")  
92 - log.header1("Header 1")  
93 - log.header2("Header 2")  
94 - log.header3("Header 3") 106 + log('\n')
  107 + log.parformat('This is a parameter')
  108 + log('\n')
  109 + log.toupper('upper case')
  110 + log('\n')
  111 + log.tolower('LOWER CASE')
  112 + log('\n')
  113 + log.fill('Higgs', 3)
  114 + log('\n')
  115 + log.left('Left', 10)
  116 + log('\n')
  117 + log.right('Right', 10)
  118 + log('\n')
  119 + log.centre('Centre', 10)
  120 + log('\n')
  121 + log.header0('Header 0')
  122 + log('\n')
  123 + log.header1('Header 1')
  124 + log.header2('Header 2')
  125 + log.header3('Header 3')
95 126
96 # Close logger 127 # Close logger
97 log.close() 128 log.close()
98 129
99 # Set reference 130 # Set reference
100 ref = [] 131 ref = []
101 - ref.append("Test __call__(std::string)\n")  
102 - #ref.append("1\n") # Old swig version  
103 - ref.append("true\n") # New swig version (3.x.y)  
104 - ref.append("41\n")  
105 - ref.append("3.1415\n")  
106 - ref.append(" This is a parameter .......: \n")  
107 - ref.append("UPPER CASE\n")  
108 - ref.append("lower case\n")  
109 - ref.append("HiggsHiggsHiggs\n")  
110 - ref.append("Left \n")  
111 - ref.append(" Right\n")  
112 - ref.append(" Centre \n")  
113 - ref.append("\n")  
114 - ref.append("+==========+\n")  
115 - ref.append("| Header 1 |\n")  
116 - ref.append("+==========+\n")  
117 - ref.append("+----------+\n")  
118 - ref.append("| Header 2 |\n")  
119 - ref.append("+----------+\n")  
120 - ref.append("=== Header 3 ===\n") 132 + ref.append('Test __call__(std::string)\n')
  133 + ref.append('true\n') # New swig version (3.x.y)
  134 + ref.append('41\n')
  135 + ref.append('3.1415\n')
  136 + ref.append(' This is a parameter .......: \n')
  137 + ref.append('UPPER CASE\n')
  138 + ref.append('lower case\n')
  139 + ref.append('HiggsHiggsHiggs\n')
  140 + ref.append('Left \n')
  141 + ref.append(' Right\n')
  142 + ref.append(' Centre \n')
  143 + ref.append('\n')
  144 + ref.append('+==========+\n')
  145 + ref.append('| Header 1 |\n')
  146 + ref.append('+==========+\n')
  147 + ref.append('+----------+\n')
  148 + ref.append('| Header 2 |\n')
  149 + ref.append('+----------+\n')
  150 + ref.append('=== Header 3 ===\n')
121 151
122 # Set calls for error messages 152 # Set calls for error messages
123 calls = [] 153 calls = []
124 - calls.append("__call__(std::string)")  
125 - calls.append("__call__(bool)")  
126 - calls.append("__call__(int)")  
127 - calls.append("__call__(double)")  
128 - calls.append("parformat()")  
129 - calls.append("toupper()")  
130 - calls.append("tolower()")  
131 - calls.append("fill()")  
132 - calls.append("left()")  
133 - calls.append("right()")  
134 - calls.append("centre()")  
135 - calls.append("header0()")  
136 - calls.append("header1()")  
137 - calls.append("header1()")  
138 - calls.append("header1()")  
139 - calls.append("header2()")  
140 - calls.append("header2()")  
141 - calls.append("header2()")  
142 - calls.append("header3()")  
143 -  
144 - # Check file  
145 - file = open(self.logfile, "r")  
146 - lines = file.readlines()  
147 - file.close()  
148 - for i, line in enumerate(lines):  
149 - if ref[i] == "true\n":  
150 - self.test_assert(line == ref[i] or line == "1\n",  
151 - "Test "+calls[i]+": "+line)  
152 - else:  
153 - self.test_assert(line == ref[i], "Test "+calls[i]+": "+line) 154 + calls.append('__call__(std::string)')
  155 + calls.append('__call__(bool)')
  156 + calls.append('__call__(int)')
  157 + calls.append('__call__(double)')
  158 + calls.append('parformat()')
  159 + calls.append('toupper()')
  160 + calls.append('tolower()')
  161 + calls.append('fill()')
  162 + calls.append('left()')
  163 + calls.append('right()')
  164 + calls.append('centre()')
  165 + calls.append('header0()')
  166 + calls.append('header1()')
  167 + calls.append('header1()')
  168 + calls.append('header1()')
  169 + calls.append('header2()')
  170 + calls.append('header2()')
  171 + calls.append('header2()')
  172 + calls.append('header3()')
  173 +
  174 + # Check log file
  175 + self._check_log_file(ref, calls)
  176 +
  177 + # Create copy of logger
  178 + cpy_log = log.copy()
  179 +
  180 + # Print one more line into copy
  181 + cpy_log.header3('Header 3bis')
  182 +
  183 + # Append reference and call for error message
  184 + ref.append('=== Header 3bis ===\n')
  185 + calls.append('header3()')
  186 +
  187 + # Check log file again
  188 + self._check_log_file(ref, calls)
154 189
155 # Return 190 # Return
156 return 191 return
157 192
158 - # Test GApplicationPars  
159 def test_pars(self): 193 def test_pars(self):
160 """ 194 """
161 - Test GApplicationPars. 195 + Test GApplicationPars
162 """ 196 """
163 # Test GApplicationPars constructor with bad filename. 197 # Test GApplicationPars constructor with bad filename.
164 - self.test_try("Test GApplicationPars constructor with bad filename") 198 + self.test_try('Test GApplicationPars constructor with bad filename')
165 try: 199 try:
166 - pars = gammalib.GApplicationPars("testme.par")  
167 - self.test_try_failure("Exception not thrown.") 200 + pars = gammalib.GApplicationPars('testme.par')
  201 + self.test_try_failure('Exception not thrown')
168 except: 202 except:
169 self.test_try_success() 203 self.test_try_success()
170 else: 204 else:
171 - self.test_try_failure("This should never happen.") 205 + self.test_try_failure('This should never happen')
172 206
173 # Return 207 # Return
174 return 208 return