| # 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() |