Skip to content

Commit

Permalink
[web] Introduce a default other component
Browse files Browse the repository at this point in the history
Introduce a default auto-generated other component that contains files
that are uncovered by the rest of the components.
  • Loading branch information
csordasmarton committed Nov 2, 2020
1 parent ddea1cc commit baf13ac
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 44 deletions.
105 changes: 76 additions & 29 deletions web/server/codechecker_server/api/report_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@

LOG = get_logger('server')

GEN_OTHER_COMPONENT_NAME = "Other (auto-generated)"

class CommentKindValue(object):
USER = 0
Expand Down Expand Up @@ -317,32 +318,8 @@ def process_report_filter(session, run_ids, report_filter, cmp_data=None):
AND.append(or_(*OR))

if report_filter.componentNames:
OR = []

for component_name in report_filter.componentNames:
skip, include = get_component_values(session, component_name)

if skip and include:
include_q = select([File.id]) \
.where(or_(*[
File.filepath.like(conv(fp)) for fp in include])) \
.distinct()

skip_q = select([File.id]) \
.where(or_(*[
File.filepath.like(conv(fp)) for fp in skip])) \
.distinct()

OR.append(or_(File.id.in_(
include_q.except_(skip_q))))
elif include:
include_q = [File.filepath.like(conv(fp)) for fp in include]
OR.append(or_(*include_q))
elif skip:
skip_q = [not_(File.filepath.like(conv(fp))) for fp in skip]
OR.append(and_(*skip_q))

AND.append(or_(*OR))
AND.append(process_source_component_filter(
session, report_filter.componentNames))

if report_filter.bugPathLength is not None:
min_path_length = report_filter.bugPathLength.min
Expand All @@ -357,6 +334,67 @@ def process_report_filter(session, run_ids, report_filter, cmp_data=None):
return filter_expr


def process_source_component_filter(session, component_names):
""" Process source component filter.
The virtual auto-generated Other component will be handled separately and
the query part will be added to the filter.
"""
OR = []

for component_name in component_names:
if component_name == GEN_OTHER_COMPONENT_NAME:
OR.append(or_(*get_other_source_component_file_query(session)))
continue

OR.append(or_(*get_source_component_file_query(session,
component_name)))

return or_(*OR)


def get_source_component_file_query(session, component_name):
""" Get filter query for a single source component. """
skip, include = get_component_values(session, component_name)

if skip and include:
include_q = select([File.id]) \
.where(or_(*[
File.filepath.like(conv(fp)) for fp in include])) \
.distinct()

skip_q = select([File.id]) \
.where(or_(*[
File.filepath.like(conv(fp)) for fp in skip])) \
.distinct()

return [File.id.in_(include_q.except_(skip_q))]
elif include:
return [File.filepath.like(conv(fp)) for fp in include]
elif skip:
return [not_(File.filepath.like(conv(fp))) for fp in skip]


def get_other_source_component_file_query(session):
""" Get filter query for the auto-generated Others component. """
OR = []

component_names = session.query(SourceComponent.name).all()

# If there are no user defined source components we don't have to filter.
if not component_names:
return []

for (component_name, ) in component_names:
OR.extend(get_source_component_file_query(session, component_name))

q = select([File.id]) \
.where(or_(*OR)) \
.distinct()

return [File.id.notin_(q)]


def get_open_reports_date_filter_query(tbl=Report, date=RunHistory.time):
""" Get open reports date filter. """
return and_(tbl.detected_at <= date,
Expand Down Expand Up @@ -2379,9 +2417,18 @@ def getSourceComponents(self, component_filter):

q = q.order_by(SourceComponent.name)

return list([SourceComponentData(c.name,
c.value.decode('utf-8'),
c.description) for c in q])
components = [SourceComponentData(c.name, c.value.decode('utf-8'),
c.description) for c in q]

component_other = \
SourceComponentData(GEN_OTHER_COMPONENT_NAME, None,
"Special auto-generated source component "
"which contains files that are uncovered "
"by the rest of the components.")

components.append(component_other)

return components

@exc_to_thrift_reqfail
@timeit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default {
return {
id : component.name,
title: component.name,
value: component.value
value: component.value || component.description
};
}));
this.loading = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ export default {
this.loading = true;
ccService.getClient().getSourceComponents(null,
handleThriftError(components => {
this.components = components;
this.components = components.filter(c =>
!c.name.includes("auto-generated"));
this.loading = false;
}));
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
<v-list-item
v-for="v in value.split('\n')"
:key="v"
:class="[ v[0] === '+' ? 'include' : 'exclude' ]"
:class="[
v[0] === '+' ? 'include' : v[0] === '-' ? 'exclude': 'other'
]"
dense
>
{{ v }}
Expand Down Expand Up @@ -49,6 +51,10 @@ export default {
&.exclude {
color: red !important;
}
&.other {
color: black !important;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export default {
return {
component : component.name,
value : component.value,
value : component.value || component.description,
reports : initDiffField(res[0]),
unreviewed : initDiffField(res[1]),
confirmed : initDiffField(res[2]),
Expand All @@ -166,7 +166,7 @@ export default {
this.statistics = this.components.map(component => ({
component : component.name,
value : component.value,
value : component.value || component.description,
reports : initDiffField(undefined),
unreviewed : initDiffField(undefined),
confirmed : initDiffField(undefined),
Expand Down
135 changes: 125 additions & 10 deletions web/tests/functional/component/test_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

from libtest import env

GEN_OTHER_COMPONENT_NAME = "Other (auto-generated)"


class TestComponent(unittest.TestCase):

Expand Down Expand Up @@ -129,19 +131,61 @@ def __remove_source_component(self, name):
ret = self._cc_client.removeSourceComponent(name)
self.assertTrue(ret)

def __get_user_defined_source_components(self):
""" Get user defined source components. """
components = self._cc_client.getSourceComponents(None)
self.assertNotEqual(len(components), 0)

return [c for c in components
if GEN_OTHER_COMPONENT_NAME not in c.name]

def __test_other_component(self, components, excluded_from_other,
included_in_other=None):
"""
Test that the results filtered by the given components and the Other
components covers all the reports and the results filtered by the
Other component doesn't contain reports which are covered by rest of
the component.
"""
all_results = self._cc_client.getRunResults(None, 500, 0, None, None,
None, False)

r_filter = ReportFilter(componentNames=[c['name'] for c in components])
component_results = self._cc_client.getRunResults(None, 500, 0, None,
r_filter, None,
False)
self.assertNotEqual(len(component_results), 0)

r_filter = ReportFilter(componentNames=[GEN_OTHER_COMPONENT_NAME])
other_results = self._cc_client.getRunResults(None, 500, 0, None,
r_filter, None, False)
self.assertNotEqual(len(other_results), 0)

self.assertEqual(len(all_results),
len(component_results) + len(other_results))

for f_path in excluded_from_other:
self.assertEqual(len([r for r in other_results if
r.checkedFile.endswith(f_path)]), 0)

if included_in_other:
for f_path in included_in_other:
self.assertNotEqual(len([r for r in other_results if
r.checkedFile.endswith(f_path)]), 0)

def test_component_management(self):
"""
Test management of the components.
"""
test_component = self.components[0]

# There are no source components available.
components = self._cc_client.getSourceComponents(None)
# There are no user defined source components available.
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

self.__add_new_component(test_component)

components = self._cc_client.getSourceComponents(None)
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 1)
self.assertEqual(components[0].name, test_component['name'])
self.assertEqual(components[0].value, test_component['value'])
Expand All @@ -150,8 +194,8 @@ def test_component_management(self):

self.__remove_source_component(test_component['name'])

# There are no source components available.
components = self._cc_client.getSourceComponents(None)
# There are no user defined source components available.
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

def test_filter_report_by_component(self):
Expand All @@ -160,7 +204,7 @@ def test_filter_report_by_component(self):
"""
test_component = self.components[0]

components = self._cc_client.getSourceComponents(None)
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

self.__add_new_component(test_component)
Expand Down Expand Up @@ -203,7 +247,7 @@ def test_filter_report_by_complex_component(self):
"""
test_component = self.components[2]

components = self._cc_client.getSourceComponents(None)
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

self.__add_new_component(test_component)
Expand Down Expand Up @@ -253,13 +297,13 @@ def test_filter_report_by_multiple_components(self):
test_component1 = self.components[3]
test_component2 = self.components[4]

components = self._cc_client.getSourceComponents(None)
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

self.__add_new_component(test_component1)
self.__add_new_component(test_component2)

components = self._cc_client.getSourceComponents(None)
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 2)

r_filter = ReportFilter(componentNames=[test_component1['name'],
Expand Down Expand Up @@ -303,7 +347,7 @@ def test_filter_report_by_excluding_all_results_component(self):
"""
test_component = self.components[5]

components = self._cc_client.getSourceComponents(None)
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

self.__add_new_component(test_component)
Expand Down Expand Up @@ -332,3 +376,74 @@ def test_component_name_with_whitespaces(self):

self.__add_new_component(test_component)
self.__remove_source_component(test_component['name'])

def test_no_user_defined_component(self):
"""
When there is no user defined component in the database, the
auto-generated Other component will not filter the results.
"""
components = self.__get_user_defined_source_components()
self.assertEqual(len(components), 0)

all_results = self._cc_client.getRunResults(None, 500, 0, None, None,
None, False)
self.assertIsNotNone(all_results)

r_filter = ReportFilter(componentNames=[GEN_OTHER_COMPONENT_NAME])
other_results = self._cc_client.getRunResults(None, 500, 0, None,
r_filter, None, False)

self.assertEqual(len(all_results), len(other_results))

def test_other_with_single_user_defined_component(self):
"""
Test that the Other component will not contain reports which are
covered by a single component.
"""
component = {
'name': 'UserDefined',
'value': '\n'.join(['+*/divide_zero.cpp',
'+*/new_delete.cpp'])}

self.__add_new_component(component)

excluded_from_other = ['divide_zero.cpp', 'new_delete.cpp']
self.__test_other_component([component], excluded_from_other)
self.__remove_source_component(component['name'])

def test_other_with_multiple_user_defined_component(self):
"""
Test that the Other component will not contain reports which are
covered by multiple components.
"""
components = [
{
'name': 'UserDefined1',
'value': '+*/divide_zero.cpp'
},
{
'name': 'UserDefined2',
'value': '+*/new_delete.cpp'
},
{
'name': 'UserDefined3',
'value': '\n'.join([
'+*/null_dereference.cpp',
'-*/call_and_message.cpp',
])
}
]

for c in components:
self.__add_new_component(c)

excluded_from_other = ['divide_zero.cpp', 'new_delete.cpp',
'null_dereference.cpp']

included_in_other = [ 'call_and_message.cpp' ]

self.__test_other_component(components, excluded_from_other,
included_in_other)

for c in components:
self.__remove_source_component(c['name'])

0 comments on commit baf13ac

Please sign in to comment.