Skip to content

Commit

Permalink
Fix and regression tests for JSON output format.
Browse files Browse the repository at this point in the history
Fixes #5.
  • Loading branch information
arm-chrjan01 committed Oct 29, 2020
1 parent 4fb7925 commit c21567d
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
1.4.1 2020-10-29

* Fix for https://github.com/arm-hpc/porting-advisor/issues/5.

1.4 2020-07-03

* Add `--output-format` command line argument to specify the output format.
Expand Down
103 changes: 103 additions & 0 deletions integrationtests/test_json_report_from_command_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""
Copyright 2020 Arm Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
"""

import advisor.main
from advisor.issue_type_config import IssueTypeConfig
import json
import os
import tempfile
import unittest

class TestJsonReportFromCommandLine(unittest.TestCase):
def test_json_report_from_command_line(self):
with tempfile.NamedTemporaryFile() as output,\
tempfile.TemporaryDirectory() as srcdir:
self._populate_source_directory(srcdir)
argv = ['--output', output.name,
'--output-format', 'json',
srcdir]
advisor.main.main(argv)

with open(output.name) as jsonf:
json_top = json.load(jsonf)

self.assertIn('errors', json_top)
self.assertEqual(len(json_top['errors']), 0)
self.assertIn('issues', json_top)
self.assertEqual(len(json_top['issues']), 2)
self.assertIn('remarks', json_top)
self.assertEqual(len(json_top['remarks']), 1)
self.assertIn('issue_types', json_top)
self.assertEqual(json_top['issue_types'], IssueTypeConfig.DEFAULT_FILTER)
self.assertIn('target_os', json_top)
self.assertIn(json_top['target_os'], ['linux', 'windows'])
self.assertIn('root_directory', json_top)
self.assertEqual(json_top['root_directory'], srcdir)
self.assertIn('source_dirs', json_top)
self.assertEqual(len(json_top['source_dirs']), 1)
self.assertEqual(json_top['source_dirs'][0], srcdir)
self.assertIn('source_files', json_top)
self.assertEqual(len(json_top['source_files']), 3)
seen_test_negative = False
seen_test_neutral = False
seen_config_guess = False
for fname in json_top['source_files']:
if 'test_negative.c' in fname:
seen_test_negative = True
elif 'test_neutral.c' in fname:
seen_test_neutral = True
elif 'config.guess' in fname:
seen_config_guess = True
else:
self.fail('Unexpected source file name in JSON output')
self.assertTrue(seen_test_negative)
self.assertTrue(seen_test_neutral)
self.assertTrue(seen_config_guess)
seen_issue1 = False
seen_issue2 = False
for issue in json_top['issues']:
if 'test_negative.c' in issue:
self.assertIn('InlineAsm', issue)
seen_issue1 = True
elif 'test_neutral.c' in issue:
self.assertIn('PragmaSimd', issue)
seen_issue2 = True
else:
self.fail('Unexpected issue in JSON output')
self.assertTrue(seen_issue1)
self.assertTrue(seen_issue2)
seen_config_guess = False
for remark in json_top['remarks']:
if 'config.guess' in remark:
seen_config_guess = True
self.assertTrue(seen_config_guess)

def _populate_source_directory(self, srcdir):
def write_file_contents(srcdir, fname, contents):
with open(os.path.join(srcdir, fname), 'w') as f:
f.write(contents)

write_file_contents(srcdir, 'test_negative.c', '''
__asm__("mov r0, r1")
''')
write_file_contents(srcdir, 'test_neutral.c', '''
#pragma simd foo
''')
write_file_contents(srcdir, 'config.guess', '''
'aarch64:Linux'
''')
4 changes: 2 additions & 2 deletions src/advisor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Copyright 2017-2019 Arm Ltd.
Copyright 2017-2020 Arm Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,7 @@
"""

__project__ = 'porting-advisor'
__version__ = '1.4'
__version__ = '1.4.1'
__summary__ = 'Produces an aarch64 porting readiness report.'
__webpage__ = 'http://www.gitlab.com/arm-hpc/porting-advisor'

Expand Down
2 changes: 2 additions & 0 deletions src/advisor/issue_type_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def __init__(self, config_string=None):
config_string = IssueTypeConfig.DEFAULT_FILTER
self._include_by_default = True

self.config_string = config_string

issue_types = config_string.split(',')
self.klasses = []
for issue_type in issue_types:
Expand Down
9 changes: 8 additions & 1 deletion src/advisor/json_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@
class JsonReport(Report):
"""Generates a JSON report."""

def __init__(self, root_directory, target_os='linux', issue_type_config=None):
"""Generates a JSON report.
issue_type_config (IssueTypeConfig): issue type filter configuration.
"""
super().__init__(root_directory, target_os)
self.issue_types = issue_type_config.config_string if issue_type_config else None

def write_items(self, output_file, items):
self.issue_types = issue_types
# munge 'self' fields so it can be serialized
self.source_dirs = list(self.source_dirs)
self.issues = [i.__class__.__name__ + ': ' + str(i) for i in self.issues]
Expand Down
2 changes: 1 addition & 1 deletion src/advisor/report_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def createReport(self, root_directory, target_os='linux', issue_type_config=None
elif output_format == ReportOutputFormat.CSV_ISSUE_TYPE_COUNT_BY_FILE:
report = CsvIssueTypeCountByFileReport(root_directory, target_os=target_os, issue_type_config=issue_type_config)
elif output_format == ReportOutputFormat.JSON:
report = JsonReport(root_directory, target_os=target_os)
report = JsonReport(root_directory, target_os=target_os, issue_type_config=issue_type_config)
else:
raise ValueError(output_format)
return report
2 changes: 1 addition & 1 deletion src/advisor/source_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def finalize_report(self, report):
for fname in self.other_arch_intrinsic_inline_asm_files:
port_file = find_port_file(
fname, report.source_files, report.source_dirs)
if not self.filter_ported_code or (port_file and port_file not in self.aarch64_intrinsic_inline_asm_files):
if not self.filter_ported_code or (not port_file or port_file not in self.aarch64_intrinsic_inline_asm_files):
report.add_issue(self.other_arch_intrinsic_inline_asm_files[fname])
else:
report.ported_inline_asm += 1
Expand Down
110 changes: 110 additions & 0 deletions unittest/test_json_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""
Copyright 2020 Arm Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
"""

from advisor.config_guess_scanner import ConfigGuessScanner
from advisor.issue_type_config import IssueTypeConfig
from advisor.json_report import JsonReport
from advisor.report_item import ReportItem
from advisor.source_scanner import SourceScanner
import io
import json
import os
import tempfile
import unittest


class TestJsonReport(unittest.TestCase):
def test_output(self):
config_guess_scanner = ConfigGuessScanner()
source_scanner = SourceScanner()

issue_type_config = IssueTypeConfig()
report = JsonReport('/root', issue_type_config=issue_type_config)
report.add_source_file('/root/src/test_negative.c')
io_object = io.StringIO('__asm__("mov r0, r1")')
source_scanner.scan_file_object(
'test_negative.c', io_object, report)
report.add_source_file('/root/src/test_neutral.c')
io_object = io.StringIO('#pragma simd foo')
source_scanner.scan_file_object(
'test_neutral.c', io_object, report)
report.add_source_file('/root/src/config.guess')
io_object = io.StringIO('aarch64:Linux')
config_guess_scanner.scan_file_object(
'config.guess', io_object, report)
self.assertEqual(len(report.issues), 2)
self.assertEqual(len(report.remarks), 1)

with tempfile.NamedTemporaryFile(mode='w', delete=False) as ofp:
report.write(ofp)
fname = ofp.name
ofp.close()

with open(fname) as ifp:
json_top = json.load(ifp)

self.assertIn('errors', json_top)
self.assertEqual(len(json_top['errors']), 0)
self.assertIn('issues', json_top)
self.assertEqual(len(json_top['issues']), 2)
self.assertIn('remarks', json_top)
self.assertEqual(len(json_top['remarks']), 1)
self.assertIn('issue_types', json_top)
self.assertEqual(json_top['issue_types'], IssueTypeConfig.DEFAULT_FILTER)
self.assertIn('target_os', json_top)
self.assertIn(json_top['target_os'], ['linux', 'windows'])
self.assertIn('root_directory', json_top)
self.assertEqual(json_top['root_directory'], '/root')
self.assertIn('source_dirs', json_top)
self.assertEqual(len(json_top['source_dirs']), 1)
self.assertEqual(json_top['source_dirs'][0], '/root/src')
self.assertIn('source_files', json_top)
self.assertEqual(len(json_top['source_files']), 3)
seen_test_negative = False
seen_test_neutral = False
seen_config_guess = False
for fname in json_top['source_files']:
if 'test_negative.c' in fname:
seen_test_negative = True
elif 'test_neutral.c' in fname:
seen_test_neutral = True
elif 'config.guess' in fname:
seen_config_guess = True
else:
self.fail('Unexpected source file name in JSON output')
self.assertTrue(seen_test_negative)
self.assertTrue(seen_test_neutral)
self.assertTrue(seen_config_guess)
seen_issue1 = False
seen_issue2 = False
for issue in json_top['issues']:
if 'test_negative.c' in issue:
self.assertIn('InlineAsm', issue)
seen_issue1 = True
elif 'test_neutral.c' in issue:
self.assertIn('PragmaSimd', issue)
seen_issue2 = True
else:
self.fail('Unexpected issue in JSON output')
self.assertTrue(seen_issue1)
self.assertTrue(seen_issue2)
seen_config_guess = False
for remark in json_top['remarks']:
if 'config.guess' in remark:
seen_config_guess = True
self.assertTrue(seen_config_guess)
2 changes: 1 addition & 1 deletion unittest/test_source_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def test_no_equivalent_inline_asm_single_file(self):
source_scanner.scan_file_object(
'test.c', io_object, report)
source_scanner.finalize_report(report)
self.assertEqual(len(report.issues), 1)
self.assertEqual(len(report.issues), 2)

def test_equivalent_inline_asm_function_outline(self):
source_scanner = SourceScanner()
Expand Down

0 comments on commit c21567d

Please sign in to comment.