1"""Tests for apache_error_log_metrics."""
2
3import os
4import re
5import subprocess
6import tempfile
7import unittest
8
9import common
10
11import apache_error_log_metrics
12
13
14SCRIPT_PATH = os.path.abspath(
15    os.path.join(os.path.dirname(__file__),
16                 'apache_error_log_metrics.py'))
17
18
19class ApacheErrorTest(unittest.TestCase):
20    """Unittest for the apache error log regexp."""
21
22    def testNonMatchingLine(self):
23        """Test for log lines which don't match the expected format.."""
24        lines = [
25          '[] [] [] blank components',
26          '[] [:error] [] no "pid" section',
27          '[] [:error] [pid 1234] no timestamp',
28          '[hello world] [:] [pid 1234] no log level',
29          '[hello] [:error] [pid 42]     too far indented.'
30        ]
31        for line in lines:
32          self.assertEqual(
33              None, apache_error_log_metrics.ERROR_LOG_MATCHER.match(line))
34
35    def testMatchingLines(self):
36        """Test for lines that are expected to match the format."""
37        match = apache_error_log_metrics.ERROR_LOG_MATCHER.match(
38            "[foo] [:bar] [pid 123] WARNING")
39        self.assertEqual('bar', match.group('log_level'))
40        self.assertEqual(None, match.group('mod_wsgi'))
41
42        match = apache_error_log_metrics.ERROR_LOG_MATCHER.match(
43            "[foo] [mpm_event:bar] [pid 123] WARNING")
44        self.assertEqual('bar', match.group('log_level'))
45        self.assertEqual(None, match.group('mod_wsgi'))
46
47        match = apache_error_log_metrics.ERROR_LOG_MATCHER.match(
48            "[foo] [:bar] [pid 123] mod_wsgi (pid=123)")
49        self.assertEqual('bar', match.group('log_level'))
50        self.assertEqual('od_wsgi', match.group('mod_wsgi'))
51
52    def testExampleLog(self):
53        """Try on some example lines from a real apache error log."""
54        with open(os.path.join(os.path.dirname(__file__),
55                               'apache_error_log_example.txt')) as fh:
56          example_log = fh.readlines()
57        matcher_output = [apache_error_log_metrics.ERROR_LOG_MATCHER.match(line)
58                          for line in example_log]
59        matched = filter(bool, matcher_output)
60        self.assertEqual(5, len(matched))
61
62        self.assertEqual('error', matched[0].group('log_level'))
63        self.assertEqual(None, matched[0].group('mod_wsgi'))
64
65        self.assertEqual('warn', matched[1].group('log_level'))
66        self.assertEqual('od_wsgi', matched[1].group('mod_wsgi'))
67
68        self.assertEqual('error', matched[2].group('log_level'))
69        self.assertEqual(None, matched[2].group('mod_wsgi'))
70
71        self.assertEqual('error', matched[3].group('log_level'))
72        self.assertEqual(None, matched[3].group('mod_wsgi'))
73
74        self.assertEqual('error', matched[4].group('log_level'))
75        self.assertEqual(None, matched[4].group('mod_wsgi'))
76
77
78    def _ShellOutToScript(self, lines):
79        """Shells out to the script.
80
81        @param lines: Lines to feed to stdin."""
82        with tempfile.NamedTemporaryFile() as temp_file:
83            p = subprocess.Popen([SCRIPT_PATH,
84                                  '--debug-metrics-file', temp_file.name],
85                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
86            p.communicate('\n'.join(lines))
87
88            with open(temp_file.name) as fh:
89                return fh.read()
90
91
92    def testApacheErrorLogScriptWithNonMatchingLine(self):
93        """Try shelling out the the script with --debug-file.
94
95        Sending it a non-matching line should result in no output from
96        ERROR_LOG_METRIC.
97        """
98        contents = self._ShellOutToScript(['an example log line'])
99
100        # We have to use re.search here with a word border character ('\b')
101        # because the ERROR_LOG_LINE_METRIC contains ERROR_LOG_METRIC as a
102        # substring.
103        self.assertTrue(re.search(
104            apache_error_log_metrics.ERROR_LOG_LINE_METRIC[1:] + r'\b',
105            contents))
106        self.assertFalse(re.search(
107            apache_error_log_metrics.ERROR_LOG_METRIC[1:] + r'\b',
108            contents))
109
110    def testApachErrorLogScriptWithMatchingLine(self):
111        """Try shelling out the the script with a matching line.
112
113        Sending it a line which matches the first-line regex should result in
114        output from ERROR_LOG_METRIC.
115        """
116        contents = self._ShellOutToScript(['[foo] [:bar] [pid 123] WARNING'])
117
118        self.assertTrue(re.search(
119            apache_error_log_metrics.ERROR_LOG_LINE_METRIC[1:] + r'\b',
120            contents))
121        self.assertTrue(re.search(
122            apache_error_log_metrics.ERROR_LOG_METRIC[1:] + r'\b',
123            contents))
124
125    def testApachErrorLogScriptWithSpecialLines(self):
126        """Sending lines with specific messages."""
127        contents = self._ShellOutToScript(['[foo] [:bar] [pid 123] WARNING Segmentation fault'])
128
129        self.assertTrue(re.search(
130            apache_error_log_metrics.SEGFAULT_METRIC[1:] + r'\b',
131            contents))
132
133
134if __name__ == '__main__':
135    unittest.main()
136