fosslight-scanner 2.1.8__tar.gz → 2.1.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/PKG-INFO +24 -2
  2. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/requirements.txt +2 -2
  3. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/setup.py +1 -1
  4. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/_help.py +3 -2
  5. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/_parse_setting.py +3 -2
  6. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/cli.py +11 -7
  7. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/common.py +11 -11
  8. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/fosslight_scanner.py +10 -7
  9. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner.egg-info/PKG-INFO +25 -3
  10. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner.egg-info/SOURCES.txt +8 -1
  11. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner.egg-info/requires.txt +2 -2
  12. fosslight_scanner-2.1.10/tests/test__get_input.py +44 -0
  13. fosslight_scanner-2.1.10/tests/test__help.py +22 -0
  14. fosslight_scanner-2.1.10/tests/test__parse_setting.py +35 -0
  15. fosslight_scanner-2.1.10/tests/test__run_compare.py +199 -0
  16. fosslight_scanner-2.1.10/tests/test_cli.py +72 -0
  17. fosslight_scanner-2.1.10/tests/test_common.py +192 -0
  18. fosslight_scanner-2.1.10/tests/test_fosslight_scanner.py +150 -0
  19. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/LICENSE +0 -0
  20. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/MANIFEST.in +0 -0
  21. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/README.md +0 -0
  22. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/setup.cfg +0 -0
  23. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/__init__.py +0 -0
  24. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/_get_input.py +0 -0
  25. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/_run_compare.py +0 -0
  26. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner/resources/bom_compare.html +0 -0
  27. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner.egg-info/dependency_links.txt +0 -0
  28. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner.egg-info/entry_points.txt +0 -0
  29. {fosslight_scanner-2.1.8 → fosslight_scanner-2.1.10}/src/fosslight_scanner.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: fosslight_scanner
3
- Version: 2.1.8
3
+ Version: 2.1.10
4
4
  Summary: FOSSLight Scanner
5
5
  Home-page: https://github.com/fosslight/fosslight_scanner
6
6
  Download-URL: https://github.com/fosslight/fosslight_scanner
@@ -14,6 +14,28 @@ Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Python: >=3.10,<3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
+ Requires-Dist: future
18
+ Requires-Dist: pandas
19
+ Requires-Dist: openpyxl
20
+ Requires-Dist: progress
21
+ Requires-Dist: pyyaml
22
+ Requires-Dist: beautifulsoup4
23
+ Requires-Dist: fosslight_util<3.0.0,>=2.1.12
24
+ Requires-Dist: fosslight_source<3.0.0,>=2.1.12
25
+ Requires-Dist: fosslight_dependency<5.0.0,>=4.1.3
26
+ Requires-Dist: fosslight_binary<6.0.0,>=5.1.9
27
+ Requires-Dist: fosslight_prechecker<5.0.0,>=4.0.0
28
+ Dynamic: author
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
32
+ Dynamic: download-url
33
+ Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: license-file
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
17
39
 
18
40
  <!--
19
41
  Copyright (c) 2021 LG Electronics
@@ -5,7 +5,7 @@ progress
5
5
  pyyaml
6
6
  beautifulsoup4
7
7
  fosslight_util>=2.1.12,<3.0.0
8
- fosslight_source>=2.1.4,<3.0.0
8
+ fosslight_source>=2.1.12,<3.0.0
9
9
  fosslight_dependency>=4.1.3,<5.0.0
10
- fosslight_binary>=5.1.2,<6.0.0
10
+ fosslight_binary>=5.1.9,<6.0.0
11
11
  fosslight_prechecker>=4.0.0,<5.0.0
@@ -15,7 +15,7 @@ with open('requirements.txt', 'r', 'utf-8') as f:
15
15
  if __name__ == "__main__":
16
16
  setup(
17
17
  name='fosslight_scanner',
18
- version='2.1.8',
18
+ version='2.1.10',
19
19
  package_dir={"": "src"},
20
20
  packages=find_packages(where='src'),
21
21
  description='FOSSLight Scanner',
@@ -40,13 +40,14 @@ _HELP_MESSAGE_SCANNER = """
40
40
  --no_correction\t Enter if you don't want to correct OSS information with sbom-info.yaml
41
41
  * Correction mode only supported xlsx format.
42
42
  --correct_fpath <path> Path to the sbom-info.yaml file
43
- --ui\t\t\t Generate UI mode result file
43
+ --ui\t\t Generate UI mode result file
44
+ --recursive_dep\t Recursively analyze dependencies
44
45
 
45
46
  Options for only 'all' or 'bin' mode
46
47
  -u <db_url>\t\t DB Connection(format :'postgresql://username:password@host:port/database_name')
47
48
 
48
49
  Options for only 'all' or 'dependency' mode
49
- -d <dependency_argument>\t Additional arguments for running dependency analysis"""
50
+ -d <dependency_arg>\t Additional arguments for running dependency analysis"""
50
51
 
51
52
 
52
53
  def print_help_msg():
@@ -25,12 +25,13 @@ def parse_setting_json(data):
25
25
  source_print_matched_text = data.get('source_print_matched_text', False)
26
26
  source_time_out = data.get('source_time_out', 120)
27
27
  binary_simple = data.get('binary_simple', False)
28
+ recursive_dep = data.get('recursive_dep', False)
28
29
  str_lists = [mode, path, exclude_path]
29
30
  strings = [
30
31
  dep_argument, output, format, db_url,
31
32
  correct_fpath, link, selected_source_scanner
32
33
  ]
33
- booleans = [timer, raw, no_correction, ui, source_write_json_file, source_print_matched_text, binary_simple]
34
+ booleans = [timer, raw, no_correction, ui, source_write_json_file, source_print_matched_text, binary_simple, recursive_dep]
34
35
 
35
36
  is_incorrect = False
36
37
 
@@ -65,4 +66,4 @@ def parse_setting_json(data):
65
66
  return mode, path, dep_argument, output, format, link, db_url, timer, \
66
67
  raw, core, no_correction, correct_fpath, ui, exclude_path, \
67
68
  selected_source_scanner, source_write_json_file, source_print_matched_text, source_time_out, \
68
- binary_simple
69
+ binary_simple, recursive_dep
@@ -15,7 +15,8 @@ from fosslight_util.help import print_package_version
15
15
 
16
16
 
17
17
  def set_args(mode, path, dep_argument, output, format, link, db_url, timer,
18
- raw, core, no_correction, correct_fpath, ui, setting, exclude_path):
18
+ raw, core, no_correction, correct_fpath, ui, setting, exclude_path,
19
+ recursive_dep):
19
20
 
20
21
  selected_source_scanner = "all"
21
22
  source_write_json_file = False
@@ -30,7 +31,7 @@ def set_args(mode, path, dep_argument, output, format, link, db_url, timer,
30
31
  s_mode, s_path, s_dep_argument, s_output, s_format, s_link, s_db_url, s_timer, s_raw, s_core, \
31
32
  s_no_correction, s_correct_fpath, s_ui, s_exclude_path, \
32
33
  s_selected_source_scanner, s_source_write_json_file, s_source_print_matched_text, \
33
- s_source_time_out, s_binary_simple = parse_setting_json(data)
34
+ s_source_time_out, s_binary_simple, s_recursive_dep = parse_setting_json(data)
34
35
 
35
36
  # direct cli arguments have higher priority than setting file
36
37
  mode = mode or s_mode
@@ -47,6 +48,7 @@ def set_args(mode, path, dep_argument, output, format, link, db_url, timer,
47
48
  correct_fpath = correct_fpath or s_correct_fpath
48
49
  ui = ui or s_ui
49
50
  exclude_path = exclude_path or s_exclude_path
51
+ recursive_dep = recursive_dep or s_recursive_dep
50
52
 
51
53
  # These options are only set from the setting file, not from CLI arguments
52
54
  selected_source_scanner = s_selected_source_scanner or selected_source_scanner
@@ -60,7 +62,7 @@ def set_args(mode, path, dep_argument, output, format, link, db_url, timer,
60
62
  return mode, path, dep_argument, output, format, link, db_url, timer, \
61
63
  raw, core, no_correction, correct_fpath, ui, exclude_path, \
62
64
  selected_source_scanner, source_write_json_file, source_print_matched_text, source_time_out, \
63
- binary_simple
65
+ binary_simple, recursive_dep
64
66
 
65
67
 
66
68
  def main():
@@ -79,7 +81,7 @@ def main():
79
81
  type=str, dest='format',nargs='*', default=[])
80
82
  parser.add_argument('--output', '-o', help='Output directory or file',
81
83
  type=str, dest='output', default="")
82
- parser.add_argument('--dependency', '-d', help='Dependency arguments',
84
+ parser.add_argument('--dependency', '-d', help='Dependency arguments (e.g. -d "-m pip" )',
83
85
  type=str, dest='dep_argument', default="")
84
86
  parser.add_argument('--url', '-u', help="DB Url",
85
87
  type=str, dest='db_url', default="")
@@ -105,6 +107,8 @@ def main():
105
107
  type=str, required=False, default='')
106
108
  parser.add_argument('--ui', help='Generate UI mode result file',
107
109
  action='store_true', required=False, default=False)
110
+ parser.add_argument('--recursive_dep', '-rd', help='Recursively analyze dependencies',
111
+ action='store_true', dest='recursive_dep', default=False)
108
112
 
109
113
  try:
110
114
  args = parser.parse_args()
@@ -118,16 +122,16 @@ def main():
118
122
  else:
119
123
  mode, path, dep_argument, output, format, link, db_url, timer, raw, core, no_correction, correct_fpath, \
120
124
  ui, exclude_path, selected_source_scanner, source_write_json_file, source_print_matched_text, \
121
- source_time_out, binary_simple, = set_args(
125
+ source_time_out, binary_simple, recursive_dep = set_args(
122
126
  args.mode, args.path, args.dep_argument, args.output,
123
127
  args.format, args.link, args.db_url, args.timer, args.raw,
124
128
  args.core, args.no_correction, args.correct_fpath, args.ui,
125
- args.setting, args.exclude_path)
129
+ args.setting, args.exclude_path, args.recursive_dep)
126
130
 
127
131
  run_main(mode, path, dep_argument, output, format, link, db_url, timer,
128
132
  raw, core, not no_correction, correct_fpath, ui, exclude_path,
129
133
  selected_source_scanner, source_write_json_file, source_print_matched_text,
130
- source_time_out, binary_simple)
134
+ source_time_out, binary_simple, recursive_dep)
131
135
 
132
136
 
133
137
  if __name__ == "__main__":
@@ -168,10 +168,12 @@ def correct_scanner_result(all_scan_item):
168
168
  try:
169
169
  remove_src_idx_list = []
170
170
  for idx_src, src_fileitem in enumerate(src_fileitems):
171
- src_fileitem.exclude = check_exclude_dir(src_fileitem.source_name_or_path, src_fileitem.exclude)
171
+ if check_package_dir(src_fileitem.source_name_or_path):
172
+ continue
172
173
  dup_flag = False
173
174
  for bin_fileitem in bin_fileitems:
174
- bin_fileitem.exclude = check_exclude_dir(bin_fileitem.source_name_or_path, bin_fileitem.exclude)
175
+ if check_package_dir(bin_fileitem.source_name_or_path):
176
+ continue
175
177
  if src_fileitem.source_name_or_path == bin_fileitem.source_name_or_path:
176
178
  dup_flag = True
177
179
  src_all_licenses_non_empty = all(oss_item.license for oss_item in src_fileitem.oss_items)
@@ -202,14 +204,12 @@ def correct_scanner_result(all_scan_item):
202
204
  return all_scan_item
203
205
 
204
206
 
205
- def check_exclude_dir(source_name_or_path, file_item_exclude):
206
- if file_item_exclude:
207
- return True
208
- _exclude_dirs = ["venv", "node_modules", "Pods", "Carthage"]
209
- exclude = False
207
+ def check_package_dir(source_name_or_path):
208
+ _package_dirs = ["venv", "node_modules", "Pods", "Carthage"]
209
+ is_pkg = False
210
210
 
211
- for exclude_dir in _exclude_dirs:
212
- if exclude_dir in source_name_or_path.split(os.path.sep):
213
- exclude = True
211
+ for package_dir in _package_dirs:
212
+ if package_dir in source_name_or_path.split(os.path.sep):
213
+ is_pkg = True
214
214
  break
215
- return exclude
215
+ return is_pkg
@@ -25,7 +25,6 @@ from fosslight_util.set_log import init_log
25
25
  from fosslight_util.timer_thread import TimerThread
26
26
  import fosslight_util.constant as constant
27
27
  from fosslight_util.output_format import check_output_formats_v2
28
- from fosslight_prechecker._precheck import run_lint as prechecker_lint
29
28
  from fosslight_util.cover import CoverItem
30
29
  from fosslight_util.oss_item import ScannerItem
31
30
  from fosslight_util.output_format import write_output_file
@@ -58,7 +57,8 @@ SCANNER_MODE = [
58
57
  ]
59
58
 
60
59
 
61
- def run_dependency(path_to_analyze, output_file_with_path, params="", path_to_exclude=[], formats=[]):
60
+ def run_dependency(path_to_analyze, output_file_with_path, params="", path_to_exclude=[], formats=[],
61
+ recursive_dep=False):
62
62
  result = []
63
63
 
64
64
  package_manager = ""
@@ -101,7 +101,9 @@ def run_dependency(path_to_analyze, output_file_with_path, params="", path_to_ex
101
101
  output_file_with_path,
102
102
  pip_activate_cmd, pip_deactivate_cmd,
103
103
  output_custom_dir, app_name,
104
- github_token, formats, True, path_to_exclude=path_to_exclude
104
+ github_token, formats, True, path_to_exclude=path_to_exclude,
105
+ graph_path="", graph_size=(600,600),
106
+ recursive=recursive_dep
105
107
  )
106
108
  if success:
107
109
  result = scan_item
@@ -132,7 +134,7 @@ def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
132
134
  default_oss_name="", default_oss_version="", url="",
133
135
  correct_mode=True, correct_fpath="", ui_mode=False, path_to_exclude=[],
134
136
  selected_source_scanner="all", source_write_json_file=False, source_print_matched_text=False,
135
- source_time_out=120, binary_simple=False, formats=[]):
137
+ source_time_out=120, binary_simple=False, formats=[], recursive_dep=False):
136
138
  final_excel_dir = output_path
137
139
  success = True
138
140
  all_cover_items = []
@@ -233,7 +235,8 @@ def run_scanner(src_path, dep_arguments, output_path, keep_raw_data=False,
233
235
 
234
236
  if run_dep:
235
237
  dep_scanitem = run_dependency(src_path, _output_dir,
236
- dep_arguments, path_to_exclude, formats)
238
+ dep_arguments, path_to_exclude, formats,
239
+ recursive_dep)
237
240
  all_scan_item.file_items.update(dep_scanitem.file_items)
238
241
  all_cover_items.append(dep_scanitem.cover)
239
242
  else:
@@ -360,7 +363,7 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
360
363
  db_url, hide_progressbar=False, keep_raw_data=False, num_cores=-1,
361
364
  correct_mode=True, correct_fpath="", ui_mode=False, path_to_exclude=[],
362
365
  selected_source_scanner="all", source_write_json_file=False, source_print_matched_text=False,
363
- source_time_out=120, binary_simple=False):
366
+ source_time_out=120, binary_simple=False, recursive_dep=False):
364
367
  global _executed_path, _start_time
365
368
 
366
369
  output_files = []
@@ -471,7 +474,7 @@ def run_main(mode_list, path_arg, dep_arguments, output_file_or_dir, file_format
471
474
  default_oss_name, default_oss_version, url_to_analyze,
472
475
  correct_mode, correct_fpath, ui_mode, path_to_exclude,
473
476
  selected_source_scanner, source_write_json_file, source_print_matched_text, source_time_out,
474
- binary_simple, formats)
477
+ binary_simple, formats, recursive_dep)
475
478
 
476
479
  if extract_folder:
477
480
  shutil.rmtree(extract_folder)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
2
- Name: fosslight-scanner
3
- Version: 2.1.8
1
+ Metadata-Version: 2.4
2
+ Name: fosslight_scanner
3
+ Version: 2.1.10
4
4
  Summary: FOSSLight Scanner
5
5
  Home-page: https://github.com/fosslight/fosslight_scanner
6
6
  Download-URL: https://github.com/fosslight/fosslight_scanner
@@ -14,6 +14,28 @@ Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Python: >=3.10,<3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
+ Requires-Dist: future
18
+ Requires-Dist: pandas
19
+ Requires-Dist: openpyxl
20
+ Requires-Dist: progress
21
+ Requires-Dist: pyyaml
22
+ Requires-Dist: beautifulsoup4
23
+ Requires-Dist: fosslight_util<3.0.0,>=2.1.12
24
+ Requires-Dist: fosslight_source<3.0.0,>=2.1.12
25
+ Requires-Dist: fosslight_dependency<5.0.0,>=4.1.3
26
+ Requires-Dist: fosslight_binary<6.0.0,>=5.1.9
27
+ Requires-Dist: fosslight_prechecker<5.0.0,>=4.0.0
28
+ Dynamic: author
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
32
+ Dynamic: download-url
33
+ Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: license-file
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
17
39
 
18
40
  <!--
19
41
  Copyright (c) 2021 LG Electronics
@@ -17,4 +17,11 @@ src/fosslight_scanner.egg-info/dependency_links.txt
17
17
  src/fosslight_scanner.egg-info/entry_points.txt
18
18
  src/fosslight_scanner.egg-info/requires.txt
19
19
  src/fosslight_scanner.egg-info/top_level.txt
20
- src/fosslight_scanner/resources/bom_compare.html
20
+ src/fosslight_scanner/resources/bom_compare.html
21
+ tests/test__get_input.py
22
+ tests/test__help.py
23
+ tests/test__parse_setting.py
24
+ tests/test__run_compare.py
25
+ tests/test_cli.py
26
+ tests/test_common.py
27
+ tests/test_fosslight_scanner.py
@@ -5,7 +5,7 @@ progress
5
5
  pyyaml
6
6
  beautifulsoup4
7
7
  fosslight_util<3.0.0,>=2.1.12
8
- fosslight_source<3.0.0,>=2.1.4
8
+ fosslight_source<3.0.0,>=2.1.12
9
9
  fosslight_dependency<5.0.0,>=4.1.3
10
- fosslight_binary<6.0.0,>=5.1.2
10
+ fosslight_binary<6.0.0,>=5.1.9
11
11
  fosslight_prechecker<5.0.0,>=4.0.0
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2020 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+
7
+ from fosslight_scanner._get_input import get_input, get_input_mode
8
+
9
+
10
+ def test_get_input(monkeypatch):
11
+ # given
12
+ ask_msg = "Please enter the path to analyze:"
13
+ default_value = "default"
14
+
15
+ # when
16
+ # Mock input to return an empty string
17
+ monkeypatch.setattr('builtins.input', lambda _: "")
18
+ result_no_input = get_input(ask_msg, default_value)
19
+
20
+ # Mock input to return "user_input"
21
+ monkeypatch.setattr('builtins.input', lambda _: "user_input")
22
+ result_with_input = get_input(ask_msg, "user_input")
23
+
24
+ # then
25
+ assert result_no_input == "default"
26
+ assert result_with_input == "user_input"
27
+
28
+
29
+ def test_get_input_mode(monkeypatch):
30
+ # given
31
+ executed_path = ""
32
+ mode_list = ["all", "dep"]
33
+
34
+ # Mock ask_to_run to return a predetermined input value
35
+ monkeypatch.setattr('fosslight_scanner._get_input.ask_to_run', lambda _: "1")
36
+
37
+ # Mock input to provide other necessary return values
38
+ monkeypatch.setattr('builtins.input', lambda _: "https://example.com")
39
+
40
+ # when
41
+ _, _, url_to_analyze = get_input_mode(executed_path, mode_list)
42
+
43
+ # then
44
+ assert url_to_analyze == "https://example.com"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2020 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+
7
+ import sys
8
+ from fosslight_scanner._help import print_help_msg, _HELP_MESSAGE_SCANNER
9
+
10
+
11
+ def test_print_help_msg(capsys, monkeypatch):
12
+ # given
13
+ # monkeypatch sys.exit to prevent the test from stopping
14
+ monkeypatch.setattr(sys, "exit", lambda: None)
15
+
16
+ # when
17
+ print_help_msg()
18
+
19
+ # then
20
+ captured = capsys.readouterr()
21
+ # Validate the help message output
22
+ assert _HELP_MESSAGE_SCANNER.strip() in captured.out
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2020 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ from fosslight_scanner._parse_setting import parse_setting_json
7
+
8
+
9
+ def test_parse_setting_json_valid_data():
10
+ data = {
11
+ 'mode': ['test'],
12
+ 'path': ['/some/path'],
13
+ 'dep_argument': 'arg',
14
+ 'output': 'output',
15
+ 'format': 'json',
16
+ 'link': 'http://example.com',
17
+ 'db_url': 'sqlite:///:memory:',
18
+ 'timer': True,
19
+ 'raw': True,
20
+ 'core': 4,
21
+ 'no_correction': True,
22
+ 'correct_fpath': '/correct/path',
23
+ 'ui': True,
24
+ 'exclude': ['/exclude/path'],
25
+ 'selected_source_scanner': 'scanner',
26
+ 'source_write_json_file': True,
27
+ 'source_print_matched_text': True,
28
+ 'source_time_out': 60,
29
+ 'binary_simple': True
30
+ }
31
+ result = parse_setting_json(data)
32
+ assert result == (
33
+ ['test'], ['/some/path'], 'arg', 'output', 'json', 'http://example.com', 'sqlite:///:memory:', True,
34
+ True, 4, True, '/correct/path', True, ['/exclude/path'], 'scanner', True, True, 60, True, False
35
+ )
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2020 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+
7
+ import pytest
8
+ from fosslight_scanner._run_compare import write_result_json_yaml, parse_result_for_table, get_sample_html, \
9
+ write_result_html, write_result_xlsx, write_compared_result, get_comparison_result_filename, \
10
+ count_compared_result, run_compare, \
11
+ ADD, DELETE, CHANGE, XLSX_EXT, HTML_EXT, YAML_EXT, JSON_EXT
12
+ import logging
13
+ import json
14
+ import yaml
15
+
16
+
17
+ @pytest.mark.parametrize("ext, expected_content", [
18
+ # Test for JSON and YAML extensions
19
+ (".json", {"key": "value"}),
20
+ (".yaml", {"key": "value"}),
21
+
22
+ # Test for TXT extension (failure)
23
+ (".txt", {"key": "value"}),
24
+ ])
25
+ def test_write_result_json_yaml(tmp_path, ext, expected_content):
26
+ # given
27
+ output_file = tmp_path / f"result{ext}"
28
+ compared_result = expected_content
29
+
30
+ # when
31
+ success = write_result_json_yaml(output_file, compared_result, ext)
32
+
33
+ # then
34
+ assert success is True, f"Failed to write file with extension {ext}"
35
+
36
+ # Verify content based on extension
37
+ if ext == ".json":
38
+ with open(output_file, 'r', encoding='utf-8') as f:
39
+ result_content = json.load(f)
40
+ assert result_content == compared_result, "The content of the JSON file does not match the expected content."
41
+
42
+ elif ext == ".yaml":
43
+ with open(output_file, 'r', encoding='utf-8') as f:
44
+ result_content = yaml.safe_load(f)
45
+ assert result_content == compared_result, "The content of the YAML file does not match the expected content."
46
+
47
+ elif ext == ".txt":
48
+ with open(output_file, 'r', encoding='utf-8') as f:
49
+ result_lines = f.readlines()
50
+ result_content = ''.join(result_lines)
51
+ assert result_content != compared_result, "The content of the TXT file does not match the expected string representation."
52
+
53
+
54
+ def test_parse_result_for_table():
55
+ # given
56
+ add_expected = [ADD, '', '', 'test(1.0)', 'MIT']
57
+ oi = {"name": "test", "version": "1.0", "license": ["MIT"]}
58
+
59
+ # when
60
+ add_result = parse_result_for_table(oi, ADD)
61
+
62
+ # then
63
+ assert add_result == add_expected
64
+
65
+
66
+ def test_get_sample_html():
67
+ # then
68
+ assert get_sample_html() != ''
69
+
70
+
71
+ @pytest.mark.parametrize("compared_result, expected_before, expected_after", [
72
+ # Case with empty add, delete, change
73
+ ({ADD: [], DELETE: [], CHANGE: []}, "before.yaml", "after.yaml"),
74
+
75
+ # Case with one entry in add and no deletes or changes
76
+ ({ADD: [{"name": "test", "version": "1.0", "license": ["MIT"]}], DELETE: [], CHANGE: []},
77
+ "before.yaml", "after.yaml")
78
+ ])
79
+ def test_write_result_html(tmp_path, compared_result, expected_before, expected_after):
80
+ # given
81
+ output_file = tmp_path / "result.html"
82
+
83
+ # when
84
+ success = write_result_html(output_file, compared_result, expected_before, expected_after)
85
+
86
+ # then
87
+ assert success is True, "Failed to write the HTML file."
88
+ assert output_file.exists(), "The HTML file was not created."
89
+ with open(output_file, 'r', encoding='utf-8') as f:
90
+ content = f.read()
91
+ assert content, "The HTML file is empty."
92
+
93
+
94
+ @pytest.mark.parametrize("compared_result", [
95
+ # Case with empty add, delete, change
96
+ {ADD: [], DELETE: [], CHANGE: []},
97
+
98
+ # Case with one entry in add and no deletes or changes
99
+ {ADD: [{"name": "test", "version": "1.0", "license": ["MIT"]}], DELETE: [], CHANGE: []}
100
+ ])
101
+ def test_write_result_xlsx(tmp_path, compared_result):
102
+ # given
103
+ output_file = tmp_path / "result.xlsx"
104
+
105
+ # when
106
+ success = write_result_xlsx(output_file, compared_result)
107
+
108
+ # then
109
+ assert success is True, "Failed to write the XLSX file."
110
+ assert output_file.exists(), "The XLSX file was not created."
111
+
112
+
113
+ @pytest.mark.parametrize("ext, expected_output", [
114
+ (XLSX_EXT, "xlsx"),
115
+ (HTML_EXT, "html"),
116
+ (JSON_EXT, "json"),
117
+ (YAML_EXT, "yaml"),
118
+ ])
119
+ def test_write_compared_result(tmp_path, ext, expected_output):
120
+ # given
121
+ output_file = tmp_path / "result"
122
+ compared_result = {ADD: [], DELETE: [], CHANGE: []}
123
+
124
+ # when
125
+ success, result_file = write_compared_result(output_file, compared_result, ext)
126
+
127
+ # then
128
+ assert success is True, f"Failed to write the compared result for extension {ext}"
129
+ if ext == XLSX_EXT:
130
+ assert str(result_file) == str(output_file), "The XLSX result file path does not match the expected output path."
131
+ elif ext == HTML_EXT:
132
+ expected_result_file = f"{str(output_file) + XLSX_EXT}, {str(output_file)}"
133
+ assert result_file == expected_result_file, "HTML file creation failed."
134
+ elif ext == JSON_EXT:
135
+ assert str(result_file) == str(output_file), "The JSON result file path does not match the expected output path."
136
+ else:
137
+ assert str(result_file) == str(output_file), "The YAML result file path does not match the expected output path."
138
+
139
+
140
+ @pytest.mark.parametrize("path, file_name, ext, time_suffix, expected_output", [
141
+ # Case when file name is provided
142
+ ("/path", "file", XLSX_EXT, "time", "/path/file.xlsx"),
143
+
144
+ # Case when file name is empty, with different extensions
145
+ ("/path", "", XLSX_EXT, "time", "/path/fosslight_compare_time.xlsx"),
146
+ ("/path", "", HTML_EXT, "time", "/path/fosslight_compare_time.html"),
147
+ ("/path", "", YAML_EXT, "time", "/path/fosslight_compare_time.yaml"),
148
+ ("/path", "", JSON_EXT, "time", "/path/fosslight_compare_time.json"),
149
+ ])
150
+ def test_get_comparison_result_filename(path, file_name, ext, time_suffix, expected_output):
151
+ # when
152
+ result = get_comparison_result_filename(path, file_name, ext, time_suffix)
153
+
154
+ # then
155
+ assert result == expected_output, f"Expected {expected_output} but got {result}"
156
+
157
+
158
+ @pytest.mark.parametrize("compared_result, expected_log", [
159
+ ({ADD: [], DELETE: [], CHANGE: []}, "all oss lists are the same."),
160
+ ({ADD: [{"name": "test"}], DELETE: [], CHANGE: []}, "total 1 oss updated (add: 1, delete: 0, change: 0)")
161
+ ])
162
+ def test_count_compared_result(compared_result, expected_log, caplog):
163
+ # when
164
+ with caplog.at_level(logging.INFO):
165
+ count_compared_result(compared_result)
166
+ # then
167
+ assert expected_log in caplog.text
168
+
169
+
170
+ def test_run_compare_different_extension(tmp_path):
171
+ # given
172
+ before_f = tmp_path / "before.yaml"
173
+ after_f = tmp_path / "after.xlsx"
174
+ output_path = tmp_path
175
+ output_file = "result"
176
+ file_ext = ".yaml"
177
+ _start_time = "time"
178
+ _output_dir = tmp_path
179
+
180
+ # Write example content to before_f and after_f
181
+ before_content = {
182
+ "oss_list": [
183
+ {"name": "test", "version": "1.0", "license": "MIT"}
184
+ ]
185
+ }
186
+
187
+ # Write these contents to the files
188
+ with open(before_f, "w") as bf:
189
+ yaml.dump(before_content, bf)
190
+
191
+ # Create an empty xlsx file for after_f
192
+ with open(after_f, "w") as af:
193
+ af.write("")
194
+
195
+ # when
196
+ comparison_result = run_compare(before_f, after_f, output_path, output_file, file_ext, _start_time, _output_dir)
197
+
198
+ # then
199
+ assert comparison_result is False
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2020 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ import pytest
7
+ import json
8
+ import sys
9
+ from fosslight_scanner.cli import main, set_args
10
+
11
+
12
+ def test_set_args(monkeypatch):
13
+ # Mocking os.path.isfile to return True
14
+ monkeypatch.setattr("os.path.isfile", lambda x: True)
15
+
16
+ # Mocking the open function to return a mock file object
17
+ mock_file_data = json.dumps({
18
+ "mode": ["test_mode"],
19
+ "path": ["test_path"],
20
+ "dep_argument": "test_dep_argument",
21
+ "output": "test_output",
22
+ "format": ["test_format"],
23
+ "link": "test_link",
24
+ "db_url": "test_db_url",
25
+ "timer": True,
26
+ "raw": True,
27
+ "core": 4,
28
+ "no_correction": True,
29
+ "correct_fpath": "test_correct_fpath",
30
+ "ui": True,
31
+ "exclude": ["test_exclude_path"],
32
+ "selected_source_scanner": "test_scanner",
33
+ "source_write_json_file": True,
34
+ "source_print_matched_text": True,
35
+ "source_time_out": 100,
36
+ "binary_simple": True
37
+ })
38
+
39
+ def mock_open(*args, **kwargs):
40
+ from io import StringIO
41
+ return StringIO(mock_file_data)
42
+
43
+ monkeypatch.setattr("builtins.open", mock_open)
44
+
45
+ # Call the function with some arguments
46
+ result = set_args(
47
+ mode=None, path=None, dep_argument=None, output=None, format=None, link=None, db_url=None, timer=None,
48
+ raw=None, core=-1, no_correction=None, correct_fpath=None, ui=None, setting="dummy_path", exclude_path=None,
49
+ recursive_dep=False
50
+ )
51
+
52
+ # Expected result
53
+ expected = (
54
+ ["test_mode"], ["test_path"], "test_dep_argument", "test_output", ["test_format"], "test_link", "test_db_url", True,
55
+ True, 4, True, "test_correct_fpath", True, ["test_exclude_path"], "test_scanner", True, True, 100, True, False
56
+ )
57
+
58
+ assert result == expected
59
+
60
+
61
+ def test_main_invalid_option(capsys):
62
+ # given
63
+ test_args = ["fosslight_scanner", "--invalid_option"]
64
+ sys.argv = test_args
65
+
66
+ # when
67
+ with pytest.raises(SystemExit): # 예상되는 SystemExit 처리
68
+ main()
69
+
70
+ # then
71
+ captured = capsys.readouterr()
72
+ assert "unrecognized arguments" in captured.err # 인식되지 않은 인자에 대한 에러 메시지 확인
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2020 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+
7
+ import os
8
+ import pytest
9
+ from fosslight_scanner.common import copy_file, run_analysis, call_analysis_api, check_package_dir, \
10
+ create_scancodejson, correct_scanner_result
11
+
12
+
13
+ def test_copy_file_success(tmp_path):
14
+ # given
15
+ source = tmp_path / "source.txt"
16
+ destination = tmp_path / "destination"
17
+ source.write_text("Test content")
18
+
19
+ # when
20
+ success, copied_file = copy_file(str(source), str(destination))
21
+
22
+ # then
23
+ assert success is True
24
+ assert os.path.exists(copied_file)
25
+
26
+
27
+ def test_copy_file_failure():
28
+ # given
29
+ source = "/nonexistent/path/source.txt"
30
+ destination = "/destination/path"
31
+
32
+ # when
33
+ success, _ = copy_file(source, destination)
34
+
35
+ # then
36
+ assert success is False
37
+
38
+
39
+ @pytest.mark.parametrize("path_to_run, expected_result", [
40
+ ("/test/path", "Mocked result"),
41
+ ("", "")
42
+ ])
43
+ def test_run_analysis(monkeypatch, path_to_run, expected_result):
44
+ # given
45
+ params = ["--param1", "value1"]
46
+ output = "/output/path"
47
+ exe_path = "/exe/path"
48
+
49
+ def mock_func():
50
+ return expected_result
51
+
52
+ # Mock os.chdir to prevent changing directories during test
53
+ monkeypatch.setattr(os, 'chdir', lambda x: None)
54
+ # Mock os.getcwd to return a default path
55
+ monkeypatch.setattr(os, 'getcwd', lambda: "/mocked/path")
56
+
57
+ # when
58
+ result = run_analysis(path_to_run, params, mock_func, "Test Run", output, exe_path)
59
+
60
+ # then
61
+ assert result == expected_result
62
+
63
+
64
+ def test_call_analysis_api_with_path():
65
+ # given
66
+ path_to_run = "/test/path"
67
+ str_run_start = "Test Run"
68
+ return_idx = -1
69
+
70
+ def mock_func():
71
+ return ["Result"]
72
+
73
+ # when
74
+ success, result = call_analysis_api(path_to_run, str_run_start, return_idx, mock_func)
75
+
76
+ # then
77
+ assert success is True
78
+ assert result == ["Result"]
79
+
80
+
81
+ def test_call_analysis_api_without_path():
82
+ # given
83
+ path_to_run = ""
84
+ str_run_start = "Test Run"
85
+ return_idx = -1
86
+
87
+ def mock_func():
88
+ return ["Result"]
89
+
90
+ # when
91
+ success, result = call_analysis_api(path_to_run, str_run_start, return_idx, mock_func)
92
+
93
+ # then
94
+ assert success is True
95
+ assert result == []
96
+
97
+
98
+ def test_create_scancodejson(monkeypatch):
99
+ # Given
100
+
101
+ # Mocking os.walk
102
+ def mock_os_walk(path):
103
+ return [("/mocked/absolute/path", ["dir1"], ["file1", "file2"])]
104
+
105
+ # Mocking write_scancodejson
106
+ def mock_write_scancodejson(dirname, basename, all_scan_item):
107
+ pass
108
+
109
+ # Mocking copy.deepcopy
110
+ def mock_deepcopy(obj):
111
+ return obj
112
+
113
+ # Mocking os.path.abspath
114
+ monkeypatch.setattr("os.path.abspath", lambda x: "/mocked/absolute/path")
115
+
116
+ # Mocking os.path.basename
117
+ monkeypatch.setattr("os.path.basename", lambda x: "mocked_parent")
118
+ monkeypatch.setattr("os.walk", mock_os_walk)
119
+ monkeypatch.setattr("fosslight_scanner.common.write_scancodejson", mock_write_scancodejson)
120
+ monkeypatch.setattr("copy.deepcopy", mock_deepcopy)
121
+
122
+ # Mocking FOSSLIGHT_DEPENDENCY and FOSSLIGHT_SOURCE
123
+ global FOSSLIGHT_DEPENDENCY, FOSSLIGHT_SOURCE
124
+ FOSSLIGHT_DEPENDENCY = "fosslight_dependency"
125
+ FOSSLIGHT_SOURCE = "fosslight_source"
126
+
127
+ # Mocking all_scan_item_origin
128
+ class AllScanItem:
129
+ def __init__(self):
130
+ self.file_items = {
131
+ FOSSLIGHT_DEPENDENCY: [],
132
+ FOSSLIGHT_SOURCE: []
133
+ }
134
+
135
+ all_scan_item_origin = AllScanItem()
136
+ ui_mode_report = "/mocked/ui_mode_report"
137
+ src_path = "/mocked/src_path"
138
+
139
+ # When
140
+ success, err_msg = create_scancodejson(all_scan_item_origin, ui_mode_report, src_path)
141
+
142
+ # Then
143
+ assert success is True
144
+ assert err_msg == ''
145
+
146
+
147
+ def test_correct_scanner_result(monkeypatch):
148
+ # Given
149
+ class MockOSSItem:
150
+ def __init__(self, license, exclude=False):
151
+ self.license = license
152
+ self.exclude = exclude
153
+
154
+ class MockFileItem:
155
+ def __init__(self, source_name_or_path, oss_items, exclude=False):
156
+ self.source_name_or_path = source_name_or_path
157
+ self.oss_items = oss_items
158
+ self.exclude = exclude
159
+
160
+ class MockAllScanItem:
161
+ def __init__(self, file_items):
162
+ self.file_items = file_items
163
+
164
+ src_oss_item = MockOSSItem(license="MIT")
165
+ bin_oss_item = MockOSSItem(license="")
166
+
167
+ src_file_item = MockFileItem("path/to/source", [src_oss_item])
168
+ bin_file_item = MockFileItem("path/to/source", [bin_oss_item])
169
+
170
+ all_scan_item = MockAllScanItem({
171
+ "FOSSLIGHT_SOURCE": [src_file_item],
172
+ "FOSSLIGHT_BINARY": [bin_file_item]
173
+ })
174
+
175
+ # When
176
+ result = correct_scanner_result(all_scan_item)
177
+ # Then
178
+ assert len(result.file_items["FOSSLIGHT_BINARY"][0].oss_items) == 1
179
+
180
+
181
+ @pytest.mark.parametrize("source_name_or_path, expected", [
182
+ ("project/venv/file.py", True),
183
+ ("project/node_modules/file.js", True),
184
+ ("project/Pods/file.m", True),
185
+ ("project/Carthage/file.swift", True),
186
+ ("project/src/file.py", False),
187
+ ("project/venv/file.py", True),
188
+ ])
189
+ def test_check_package_dir(source_name_or_path, expected):
190
+ result = check_package_dir(source_name_or_path)
191
+ # Then
192
+ assert result == expected
@@ -0,0 +1,150 @@
1
+ from fosslight_scanner.fosslight_scanner import run_scanner, download_source, init, run_main, run_dependency
2
+ from fosslight_util.oss_item import ScannerItem
3
+
4
+
5
+ def test_run_dependency(tmp_path):
6
+ # given
7
+ path_to_analyze = tmp_path / "test_project"
8
+ output_file_with_path = tmp_path / "output_file"
9
+ params = "-m 'npm' -a 'activate_cmd' -d 'deactivate_cmd' -c 'custom_dir' -n 'app_name' -t 'token_value'"
10
+ path_to_exclude = ["node_modules"]
11
+
12
+ # Create the directory to analyze
13
+ path_to_analyze.mkdir(parents=True, exist_ok=True)
14
+
15
+ # when
16
+ result = run_dependency(str(path_to_analyze), str(output_file_with_path), params, path_to_exclude)
17
+
18
+ # then
19
+ # Check that result is an instance of ScannerItem
20
+ assert isinstance(result, ScannerItem), "Result should be an instance of ScannerItem."
21
+
22
+ # Check that result is not None
23
+ assert result is not None, "Result should not be None."
24
+
25
+
26
+ def test_run_scanner(tmp_path):
27
+ # given
28
+ src_path = tmp_path / "test_src"
29
+ output_path = tmp_path / "output"
30
+ dep_arguments = ""
31
+ output_file = ['test_output']
32
+ output_extension = [".yaml"]
33
+
34
+ # Create necessary directories and files for the test
35
+ src_path.mkdir(parents=True, exist_ok=True)
36
+ output_path.mkdir(parents=True, exist_ok=True)
37
+
38
+ # Create a dummy source file in `src_path` to be scanned
39
+ test_file = src_path / "test_file.py"
40
+ test_file.write_text("# This is a test file\nprint('Hello, World!')")
41
+
42
+ # when
43
+ run_scanner(
44
+ src_path=str(src_path),
45
+ dep_arguments=dep_arguments,
46
+ output_path=str(output_path),
47
+ keep_raw_data=True,
48
+ run_src=True,
49
+ run_bin=False,
50
+ run_dep=False,
51
+ run_prechecker=False,
52
+ remove_src_data=False,
53
+ result_log={},
54
+ output_files=output_file,
55
+ output_extensions=output_extension,
56
+ num_cores=1,
57
+ path_to_exclude=[]
58
+ )
59
+
60
+ # then
61
+ # Check if `src_path` and `output_path` still exist
62
+ assert src_path.is_dir(), "Source path should still exist."
63
+ assert output_path.is_dir(), "Output path should still exist."
64
+
65
+
66
+ def test_download_source(tmp_path):
67
+ # given
68
+ link = "https://example.com/test_repo.git"
69
+ out_dir = tmp_path / "output"
70
+
71
+ # Create the necessary output directory
72
+ out_dir.mkdir(parents=True, exist_ok=True)
73
+
74
+ # when
75
+ success, temp_src_dir, oss_name, oss_version = download_source(str(link), str(out_dir))
76
+
77
+ # then
78
+ # Ensure the function completes successfully
79
+ assert isinstance(success, bool), "Function should return a boolean for success."
80
+
81
+ # If the function fails, ensure temp_src_dir is empty
82
+ if not success:
83
+ assert temp_src_dir == "", "temp_src_dir should be an empty string if download fails."
84
+ else:
85
+ # If the function succeeds, check temp_src_dir is a valid path
86
+ assert isinstance(temp_src_dir, str), "Temporary source directory should be a string."
87
+ assert str(temp_src_dir).startswith(str(out_dir)), "Temporary source directory should be within the output directory."
88
+
89
+ # Ensure oss_name and oss_version are strings
90
+ assert isinstance(oss_name, str), "OSS name should be a string."
91
+ assert isinstance(oss_version, str), "OSS version should be a string."
92
+
93
+
94
+ def test_init(tmp_path):
95
+ # given
96
+ output_path = tmp_path / "output"
97
+
98
+ # Initialize global variables if necessary
99
+ global _output_dir, _log_file
100
+ _output_dir = "test_output" # Set to a default or test-specific value
101
+ _log_file = "test_log" # Set the name of the log file
102
+
103
+ # when
104
+ dir_created, output_root_dir, result_log = init(str(output_path))
105
+
106
+ # then
107
+ # Ensure that the output directory was created
108
+ assert dir_created is True, "The output directory should be created."
109
+
110
+ # Check if the output_root_dir is correct
111
+ assert output_root_dir == str(output_path), "Output root directory should match the given path."
112
+
113
+ # Check that the result_log is not None and is a dictionary
114
+ assert result_log is not None, "Result log should not be None."
115
+ assert isinstance(result_log, dict), "Result log should be a dictionary."
116
+
117
+
118
+ def test_run_main(tmp_path):
119
+ # given
120
+ mode_list = ["source"]
121
+ path_arg = [str(tmp_path / "test_src")]
122
+ dep_arguments = []
123
+ output_file_or_dir = str(tmp_path / "output")
124
+ file_format = ['yaml']
125
+ url_to_analyze = ""
126
+ db_url = ""
127
+
128
+ # Create necessary directories and files for the test
129
+ (tmp_path / "test_src").mkdir(parents=True, exist_ok=True)
130
+
131
+ # when
132
+ result = run_main(
133
+ mode_list=mode_list,
134
+ path_arg=path_arg,
135
+ dep_arguments=dep_arguments,
136
+ output_file_or_dir=output_file_or_dir,
137
+ file_format=file_format,
138
+ url_to_analyze=url_to_analyze,
139
+ db_url=db_url,
140
+ hide_progressbar=True, # Disable progress bar for testing
141
+ keep_raw_data=True, # Keep raw data to avoid cleanup during test
142
+ num_cores=1,
143
+ correct_mode=True,
144
+ correct_fpath="",
145
+ ui_mode=False,
146
+ path_to_exclude=[]
147
+ )
148
+
149
+ # then
150
+ assert result is True