Skip to content

Commit

Permalink
[client] Store support configuration file
Browse files Browse the repository at this point in the history
  • Loading branch information
csordasmarton committed Mar 23, 2020
1 parent d76d7b1 commit 8986ed7
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 17 deletions.
17 changes: 15 additions & 2 deletions docs/web/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ a database.
to the database.

```
usage: CodeChecker store [-h] [-t {plist}] [-n NAME] [--tag TAG] [-f]
[--url PRODUCT_URL]
usage: CodeChecker store [-h] [-t {plist}] [-n NAME] [--tag TAG]
[--trim-path-prefix [TRIM_PATH_PREFIX [TRIM_PATH_PREFIX ...]]]
[--config CONFIG_FILE] [-f] [--url PRODUCT_URL]
[--verbose {info,debug,debug_analyzer}]
[file/folder [file/folder ...]]
Expand Down Expand Up @@ -192,6 +193,18 @@ optional arguments:
removing "/a/b/" prefix will store files like c/x.cpp
and c/y.cpp. If multiple prefix is given, the longest
match will be removed.
--config CONFIG_FILE Allow the configuration from an explicit JSON based
configuration file. The values configured in the
config file will overwrite the values set in the
command line. The format of configuration file is:
{
"enabled": true,
"store": [
"--name=run_name",
"--tag=my_tag"
"--url=http://codechecker.my/MyProduct"
]
}. (default: None)
-f, --force Delete analysis results stored in the database for the
current analysis run's name and store only the results
reported in the 'input' files. (By default,
Expand Down
53 changes: 41 additions & 12 deletions web/client/codechecker_client/cmd/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@

from codechecker_client import client as libclient

from codechecker_common import logger
from codechecker_common import util
from codechecker_common import plist_parser
from codechecker_common import arg, logger, plist_parser, util
from codechecker_common.output_formatters import twodim_to_str
from codechecker_common.source_code_comment_handler import \
SourceCodeCommentHandler
Expand Down Expand Up @@ -72,16 +70,18 @@ def get_argparser_ctor_args():

return {
'prog': 'CodeChecker store',
'formatter_class': argparse.ArgumentDefaultsHelpFormatter,
'formatter_class': arg.RawDescriptionDefaultHelpFormatter,

# Description is shown when the command's help is queried directly
'description': "Store the results from one or more 'codechecker-"
"analyze' result files in a database.",
'description': """
Store the results from one or more 'codechecker-analyze' result files in a
database.""",

# Epilogue is shown after the arguments when the help is queried
# directly.
'epilog': "The results can be viewed by connecting to such a server "
"in a Web browser or via 'CodeChecker cmd'.",
'epilog': """
The results can be viewed by connecting to such a server in a Web browser or
via 'CodeChecker cmd'.""",

# Help is shown when the "parent" CodeChecker command lists the
# individual subcommands.
Expand Down Expand Up @@ -143,6 +143,23 @@ def add_arguments_to_parser(parser):
"If multiple prefix is given, the longest match "
"will be removed.")

parser.add_argument('--config',
dest='config_file',
required=False,
help="R|Allow the configuration from an explicit JSON "
"based configuration file. The values configured "
"in the config file will overwrite the values "
"set in the command line. The format of "
"configuration file is:\n"
"{\n"
" \"enabled\": true,\n"
" \"store\": [\n"
" \"--name=run_name\",\n"
" \"--tag=my_tag\"\n"
" \"--url=http://codechecker.my/MyProduct\"\n"
" ]\n"
"}.")

parser.add_argument('-f', '--force',
dest="force",
default=argparse.SUPPRESS,
Expand All @@ -158,9 +175,10 @@ def add_arguments_to_parser(parser):

server_args = parser.add_argument_group(
"server arguments",
"Specifies a 'CodeChecker server' instance which will be used to "
"store the results. This server must be running and listening, and "
"the given product must exist prior to the 'store' command being ran.")
"""
Specifies a 'CodeChecker server' instance which will be used to store the
results. This server must be running and listening, and the given product
must exist prior to the 'store' command being ran.""")

server_args.add_argument('--url',
type=str,
Expand All @@ -173,7 +191,18 @@ def add_arguments_to_parser(parser):
"'[http[s]://]host:port/Endpoint'.")

logger.add_verbose_arguments(parser)
parser.set_defaults(func=main)
parser.set_defaults(func=main,
func_process_config_file=process_config_file)


def process_config_file(args):
"""
Handler to get config file options.
"""
if args.config_file and os.path.exists(args.config_file):
cfg = util.load_json_or_empty(args.config_file, default={})
if cfg.get("enabled"):
return cfg.get('store', [])


def __get_run_name(input_list):
Expand Down
25 changes: 22 additions & 3 deletions web/tests/functional/cli_config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@

import os
import shutil
import sys

from libtest import codechecker
from libtest import env
from libtest import project


# Test workspace should be initialized in this module.
Expand All @@ -33,11 +36,23 @@ def setup_package():
codechecker_cfg = {
'workspace': TEST_WORKSPACE,
'check_env': env.test_env(TEST_WORKSPACE),
'viewer_host': 'localhost',
'viewer_product': 'db_cleanup'
'reportdir': os.path.join(TEST_WORKSPACE, 'reports')
}

env.export_test_cfg(TEST_WORKSPACE, {'codechecker_cfg': codechecker_cfg})
# Start or connect to the running CodeChecker server and get connection
# details.
print("This test uses a CodeChecker server... connecting...")
server_access = codechecker.start_or_get_server()
server_access['viewer_product'] = 'config'
codechecker.add_test_package_product(server_access, TEST_WORKSPACE)

# Extend the checker configuration with the server access.
codechecker_cfg.update(server_access)

test_config = {
'codechecker_cfg': codechecker_cfg}

env.export_test_cfg(TEST_WORKSPACE, test_config)


def teardown_package():
Expand All @@ -47,5 +62,9 @@ def teardown_package():
# and print out the path.
global TEST_WORKSPACE

check_env = env.import_test_cfg(TEST_WORKSPACE)[
'codechecker_cfg']['check_env']
codechecker.remove_test_package_product(TEST_WORKSPACE, check_env)

print("Removing: " + TEST_WORKSPACE)
shutil.rmtree(TEST_WORKSPACE)
92 changes: 92 additions & 0 deletions web/tests/functional/cli_config/test_store_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#
# -----------------------------------------------------------------------------
# The CodeChecker Infrastructure
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
# -----------------------------------------------------------------------------

"""
Test store configuration file.
"""


import json
import os
import subprocess
import unittest

from libtest import env


class TestStoreConfig(unittest.TestCase):
_ccClient = None

def setUp(self):

# TEST_WORKSPACE is automatically set by test package __init__.py .
self.test_workspace = os.environ['TEST_WORKSPACE']
self.codechecker_cfg = env.import_codechecker_cfg(self.test_workspace)

test_class = self.__class__.__name__
print('Running ' + test_class + ' tests in ' + self.test_workspace)

# Get the CodeChecker cmd if needed for the tests.
self._codechecker_cmd = env.codechecker_cmd()

self.config_file = os.path.join(self.test_workspace,
"codechecker.json")

# Create an empty report directory which will be used to check store
# command.
if not os.path.exists(self.codechecker_cfg['reportdir']):
os.mkdir(self.codechecker_cfg['reportdir'])

def test_valid_config(self):
""" Store with a valid configuration file. """
with open(self.config_file, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'enabled': True,
'store': [
'--name=' + 'store_config',
'--url=' + env.parts_to_url(self.codechecker_cfg),
self.codechecker_cfg['reportdir']]}, config_f)

store_cmd = [env.codechecker_cmd(), 'store', '--config',
self.config_file]

subprocess.check_output(
store_cmd, encoding="utf-8", errors="ignore")

def test_invalid_config(self):
""" Store with an invalid configuration file. """
with open(self.config_file, 'w+',
encoding="utf-8", errors="ignore") as config_f:
json.dump({
'enabled': True,
'store': ['--dummy-option']}, config_f)

store_cmd = [env.codechecker_cmd(), 'store',
'--name', 'store_config',
'--config', self.config_file,
'--url', env.parts_to_url(self.codechecker_cfg),
self.codechecker_cfg['reportdir']]

with self.assertRaises(subprocess.CalledProcessError):
subprocess.check_output(
store_cmd, encoding="utf-8", errors="ignore")

def test_empty_config(self):
""" Store with an empty configuration file. """
with open(self.config_file, 'w+',
encoding="utf-8", errors="ignore") as config_f:
config_f.write("")

store_cmd = [env.codechecker_cmd(), 'store',
'--name', 'store_config',
'--config', self.config_file,
'--url', env.parts_to_url(self.codechecker_cfg),
self.codechecker_cfg['reportdir']]

subprocess.check_output(
store_cmd, encoding="utf-8", errors="ignore")

0 comments on commit 8986ed7

Please sign in to comment.