blob: cd2406dfc8fd2799ea4f4d1bad3119648ebd5573 [file] [log] [blame]
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import unittest
from mozfile.mozfile import NamedTemporaryFile
from mozbuild.compilation.warnings import CompilerWarning
from mozbuild.compilation.warnings import WarningsCollector
from mozbuild.compilation.warnings import WarningsDatabase
from mozunit import main
CLANG_TESTS = [
('foobar.cpp:123:10: warning: you messed up [-Wfoo]',
'foobar.cpp', 123, 10, 'you messed up', '-Wfoo'),
("c_locale_dummy.c:457:1: warning: (near initialization for "
"'full_wmonthname[0]') [-Wpointer-sign]",
'c_locale_dummy.c', 457, 1,
"(near initialization for 'full_wmonthname[0]')", '-Wpointer-sign')
]
MSVC_TESTS = [
("C:/mozilla-central/test/foo.cpp(793) : warning C4244: 'return' : "
"conversion from 'double' to 'uint32_t', possible loss of data",
'C:/mozilla-central/test/foo.cpp', 793, 'C4244',
"'return' : conversion from 'double' to 'uint32_t', possible loss of "
'data')
]
CURRENT_LINE = 1
def get_warning():
global CURRENT_LINE
w = CompilerWarning()
w['filename'] = '/foo/bar/baz.cpp'
w['line'] = CURRENT_LINE
w['column'] = 12
w['message'] = 'This is irrelevant'
CURRENT_LINE += 1
return w
class TestCompilerWarning(unittest.TestCase):
def test_equivalence(self):
w1 = CompilerWarning()
w2 = CompilerWarning()
s = set()
# Empty warnings should be equal.
self.assertEqual(w1, w2)
s.add(w1)
s.add(w2)
self.assertEqual(len(s), 1)
w1['filename'] = '/foo.c'
w2['filename'] = '/bar.c'
self.assertNotEqual(w1, w2)
s = set()
s.add(w1)
s.add(w2)
self.assertEqual(len(s), 2)
w1['filename'] = '/foo.c'
w1['line'] = 5
w2['line'] = 5
w2['filename'] = '/foo.c'
w1['column'] = 3
w2['column'] = 3
self.assertEqual(w1, w2)
def test_comparison(self):
w1 = CompilerWarning()
w2 = CompilerWarning()
w1['filename'] = '/aaa.c'
w1['line'] = 5
w1['column'] = 5
w2['filename'] = '/bbb.c'
w2['line'] = 5
w2['column'] = 5
self.assertLess(w1, w2)
self.assertGreater(w2, w1)
self.assertGreaterEqual(w2, w1)
w2['filename'] = '/aaa.c'
w2['line'] = 4
w2['column'] = 6
self.assertLess(w2, w1)
self.assertGreater(w1, w2)
self.assertGreaterEqual(w1, w2)
w2['filename'] = '/aaa.c'
w2['line'] = 5
w2['column'] = 10
self.assertLess(w1, w2)
self.assertGreater(w2, w1)
self.assertGreaterEqual(w2, w1)
w2['filename'] = '/aaa.c'
w2['line'] = 5
w2['column'] = 5
self.assertLessEqual(w1, w2)
self.assertLessEqual(w2, w1)
self.assertGreaterEqual(w2, w1)
self.assertGreaterEqual(w1, w2)
class TestWarningsParsing(unittest.TestCase):
def test_clang_parsing(self):
for source, filename, line, column, message, flag in CLANG_TESTS:
collector = WarningsCollector(resolve_files=False)
warning = collector.process_line(source)
self.assertIsNotNone(warning)
self.assertEqual(warning['filename'], filename)
self.assertEqual(warning['line'], line)
self.assertEqual(warning['column'], column)
self.assertEqual(warning['message'], message)
self.assertEqual(warning['flag'], flag)
def test_msvc_parsing(self):
for source, filename, line, flag, message in MSVC_TESTS:
collector = WarningsCollector(resolve_files=False)
warning = collector.process_line(source)
self.assertIsNotNone(warning)
self.assertEqual(warning['filename'], os.path.normpath(filename))
self.assertEqual(warning['line'], line)
self.assertEqual(warning['flag'], flag)
self.assertEqual(warning['message'], message)
class TestWarningsDatabase(unittest.TestCase):
def test_basic(self):
db = WarningsDatabase()
self.assertEqual(len(db), 0)
for i in range(10):
db.insert(get_warning(), compute_hash=False)
self.assertEqual(len(db), 10)
warnings = list(db)
self.assertEqual(len(warnings), 10)
def test_hashing(self):
"""Ensure that hashing files on insert works."""
db = WarningsDatabase()
temp = NamedTemporaryFile(mode='wt')
temp.write('x' * 100)
temp.flush()
w = CompilerWarning()
w['filename'] = temp.name
w['line'] = 1
w['column'] = 4
w['message'] = 'foo bar'
# Should not throw.
db.insert(w)
w['filename'] = 'DOES_NOT_EXIST'
with self.assertRaises(Exception):
db.insert(w)
def test_pruning(self):
"""Ensure old warnings are removed from database appropriately."""
db = WarningsDatabase()
source_files = []
for i in range(1, 21):
temp = NamedTemporaryFile(mode='wt')
temp.write('x' * (100 * i))
temp.flush()
# Keep reference so it doesn't get GC'd and deleted.
source_files.append(temp)
w = CompilerWarning()
w['filename'] = temp.name
w['line'] = 1
w['column'] = i * 10
w['message'] = 'irrelevant'
db.insert(w)
self.assertEqual(len(db), 20)
# If we change a source file, inserting a new warning should nuke the
# old one.
source_files[0].write('extra')
source_files[0].flush()
w = CompilerWarning()
w['filename'] = source_files[0].name
w['line'] = 1
w['column'] = 50
w['message'] = 'replaced'
db.insert(w)
self.assertEqual(len(db), 20)
warnings = list(db.warnings_for_file(source_files[0].name))
self.assertEqual(len(warnings), 1)
self.assertEqual(warnings[0]['column'], w['column'])
# If we delete the source file, calling prune should cause the warnings
# to go away.
old_filename = source_files[0].name
del source_files[0]
self.assertFalse(os.path.exists(old_filename))
db.prune()
self.assertEqual(len(db), 19)
if __name__ == '__main__':
main()