zackliqcom commited on
Commit
31dadc6
·
verified ·
1 Parent(s): 523c899

Upload folder using huggingface_hub

Browse files
conftest.py CHANGED
@@ -2,19 +2,18 @@
2
  # Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.
3
  # SPDX-License-Identifier: BSD-3-Clause
4
  # ---------------------------------------------------------------------
5
- """Shared pytest fixtures for QDC on-device test runners."""
6
 
7
  import os
8
 
9
  import pytest
10
- from appium import webdriver
11
 
12
- from utils import options, write_qdc_log
13
 
14
 
15
  @pytest.fixture(scope="session", autouse=True)
16
  def driver():
17
- return webdriver.Remote(command_executor="http://127.0.0.1:4723/wd/hub", options=options)
18
 
19
 
20
  def pytest_sessionfinish(session, exitstatus):
 
2
  # Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.
3
  # SPDX-License-Identifier: BSD-3-Clause
4
  # ---------------------------------------------------------------------
5
+ """Shared pytest fixtures for QDC on-device test runners (Linux IoT)."""
6
 
7
  import os
8
 
9
  import pytest
 
10
 
11
+ from utils import write_qdc_log
12
 
13
 
14
  @pytest.fixture(scope="session", autouse=True)
15
  def driver():
16
+ return None
17
 
18
 
19
  def pytest_sessionfinish(session, exitstatus):
requirements.txt CHANGED
@@ -1,22 +1,6 @@
1
- Appium-Python-Client==5.2.4
2
- attrs==25.4.0
3
- certifi==2025.10.5
4
  exceptiongroup==1.3.0
5
- h11==0.16.0
6
- idna==3.11
7
  iniconfig==2.1.0
8
- outcome==1.3.0.post0
9
  packaging==25.0
10
  pluggy==1.6.0
11
- PySocks==1.7.1
12
  pytest==8.4.2
13
- selenium==4.36.0
14
- sniffio==1.3.1
15
- sortedcontainers==2.4.0
16
  tomli==2.3.0
17
- trio==0.31.0
18
- trio-websocket==0.12.2
19
- typing_extensions==4.15.0
20
- urllib3==2.5.0
21
- websocket-client==1.9.0
22
- wsproto==1.2.0
 
 
 
 
1
  exceptiongroup==1.3.0
 
 
2
  iniconfig==2.1.0
 
3
  packaging==25.0
4
  pluggy==1.6.0
 
5
  pytest==8.4.2
 
 
 
6
  tomli==2.3.0
 
 
 
 
 
 
run_backend_ops_posix.py CHANGED
@@ -5,8 +5,7 @@
5
  """
6
  On-device test-backend-ops runner for llama.cpp (HTP0 backend).
7
 
8
- Executed by QDC's Appium test framework on the QDC runner.
9
- The runner has ADB access to the allocated device.
10
  """
11
 
12
  import os
@@ -14,7 +13,7 @@ import sys
14
 
15
  import pytest
16
 
17
- from utils import BIN_PATH, CMD_PREFIX, push_bundle_if_needed, run_adb_command, write_qdc_log
18
 
19
 
20
  @pytest.fixture(scope="session", autouse=True)
@@ -29,10 +28,7 @@ def test_backend_ops_htp0(type_a):
29
  cmd += r' -p "^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$"'
30
  else:
31
  cmd += f" -p type_a={type_a}"
32
- result = run_adb_command(
33
- cmd,
34
- check=False,
35
- )
36
  write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "")
37
  assert result.returncode == 0, f"test-backend-ops type_a={type_a} failed (exit {result.returncode})"
38
 
 
5
  """
6
  On-device test-backend-ops runner for llama.cpp (HTP0 backend).
7
 
8
+ Runs directly on a Linux IoT device via QDC.
 
9
  """
10
 
11
  import os
 
13
 
14
  import pytest
15
 
16
+ from utils import BIN_PATH, CMD_PREFIX, push_bundle_if_needed, run_shell_command, write_qdc_log
17
 
18
 
19
  @pytest.fixture(scope="session", autouse=True)
 
28
  cmd += r' -p "^(?=.*type_a=q4_0)(?!.*type_b=f32,m=576,n=512,k=576).*$"'
29
  else:
30
  cmd += f" -p type_a={type_a}"
31
+ result = run_shell_command(cmd, check=False)
 
 
 
32
  write_qdc_log(f"backend_ops_{type_a}.log", result.stdout or "")
33
  assert result.returncode == 0, f"test-backend-ops type_a={type_a} failed (exit {result.returncode})"
34
 
run_bench_tests_posix.py CHANGED
@@ -5,22 +5,20 @@
5
  """
6
  On-device bench and completion test runner for llama.cpp (CPU, GPU, NPU backends).
7
 
8
- Executed by QDC's Appium test framework on the QDC runner.
9
- The runner has ADB access to the allocated device.
10
 
11
  Placeholders replaced at artifact creation time by run_qdc_jobs.py:
12
  <<MODEL_URL>> Direct URL to the GGUF model file (downloaded on-device via curl)
13
  """
14
 
15
  import os
16
- import subprocess
17
  import sys
18
 
19
  import pytest
20
 
21
- from utils import BIN_PATH, CMD_PREFIX, push_bundle_if_needed, run_adb_command, write_qdc_log
22
 
23
- MODEL_PATH = "/data/local/tmp/model.gguf"
24
  PROMPT = "What is the capital of France?"
25
  CLI_OPTS = "--batch-size 128 -n 128 -no-cnv --seed 42"
26
 
@@ -29,13 +27,8 @@ CLI_OPTS = "--batch-size 128 -n 128 -no-cnv --seed 42"
29
  def install(driver):
30
  push_bundle_if_needed(f"{BIN_PATH}/llama-cli")
31
 
32
- # Skip model download if already present
33
- check = subprocess.run(
34
- ["adb", "shell", f"ls {MODEL_PATH}"],
35
- text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
36
- )
37
- if check.returncode != 0:
38
- run_adb_command(f'curl -L -J --output {MODEL_PATH} "<<MODEL_URL>>"')
39
 
40
 
41
  @pytest.mark.parametrize("device,extra_flags", [
@@ -44,7 +37,7 @@ def install(driver):
44
  pytest.param("HTP0", "-ctk q8_0 -ctv q8_0", id="npu"),
45
  ])
46
  def test_llama_completion(device, extra_flags):
47
- result = run_adb_command(
48
  f'{CMD_PREFIX} {BIN_PATH}/llama-completion'
49
  f' -m {MODEL_PATH} --device {device} -ngl 99 -t 4 {CLI_OPTS} {extra_flags} -fa on'
50
  f' -p "{PROMPT}"',
@@ -63,7 +56,7 @@ _DEVICE_LOG_NAME = {"none": "cpu", "GPUOpenCL": "gpu", "HTP0": "htp"}
63
  pytest.param("HTP0", id="npu"),
64
  ])
65
  def test_llama_bench(device):
66
- result = run_adb_command(
67
  f"{CMD_PREFIX} {BIN_PATH}/llama-bench"
68
  f" -m {MODEL_PATH} --device {device} -ngl 99 --batch-size 128 -t 4 -p 128 -n 32",
69
  check=False,
 
5
  """
6
  On-device bench and completion test runner for llama.cpp (CPU, GPU, NPU backends).
7
 
8
+ Runs directly on a Linux IoT device via QDC.
 
9
 
10
  Placeholders replaced at artifact creation time by run_qdc_jobs.py:
11
  <<MODEL_URL>> Direct URL to the GGUF model file (downloaded on-device via curl)
12
  """
13
 
14
  import os
 
15
  import sys
16
 
17
  import pytest
18
 
19
+ from utils import BIN_PATH, CMD_PREFIX, push_bundle_if_needed, run_shell_command, write_qdc_log
20
 
21
+ MODEL_PATH = "/tmp/model.gguf"
22
  PROMPT = "What is the capital of France?"
23
  CLI_OPTS = "--batch-size 128 -n 128 -no-cnv --seed 42"
24
 
 
27
  def install(driver):
28
  push_bundle_if_needed(f"{BIN_PATH}/llama-cli")
29
 
30
+ if not os.path.exists(MODEL_PATH):
31
+ run_shell_command(f'curl -L -J --output {MODEL_PATH} "<<MODEL_URL>>"')
 
 
 
 
 
32
 
33
 
34
  @pytest.mark.parametrize("device,extra_flags", [
 
37
  pytest.param("HTP0", "-ctk q8_0 -ctv q8_0", id="npu"),
38
  ])
39
  def test_llama_completion(device, extra_flags):
40
+ result = run_shell_command(
41
  f'{CMD_PREFIX} {BIN_PATH}/llama-completion'
42
  f' -m {MODEL_PATH} --device {device} -ngl 99 -t 4 {CLI_OPTS} {extra_flags} -fa on'
43
  f' -p "{PROMPT}"',
 
56
  pytest.param("HTP0", id="npu"),
57
  ])
58
  def test_llama_bench(device):
59
+ result = run_shell_command(
60
  f"{CMD_PREFIX} {BIN_PATH}/llama-bench"
61
  f" -m {MODEL_PATH} --device {device} -ngl 99 --batch-size 128 -t 4 -p 128 -n 32",
62
  check=False,
run_scorecard_posix.py CHANGED
@@ -3,69 +3,52 @@
3
  # SPDX-License-Identifier: BSD-3-Clause
4
  # ---------------------------------------------------------------------
5
  """
6
- Scorecard benchmark script for llama.cpp on Android devices via Appium.
7
 
8
- This script runs comprehensive benchmarks using the run-*.sh scripts from
9
- llama.cpp/scripts/snapdragon/adb/:
10
- 1. Performance benchmarks (CPU/GPU/HTP x 3 context lengths)
11
- 2. Fallback ops detection (SCHED=1)
12
- 3. Perplexity (WikiText-2)
13
 
14
  Placeholders are replaced at artifact creation time:
15
  - <<MODEL_URL>>: URL to download the model
 
16
  """
17
 
18
  import os
 
19
  import subprocess
20
  import sys
21
 
22
  import pytest
23
- from appium import webdriver
24
- from appium.options.common import AppiumOptions
25
 
26
- options = AppiumOptions()
27
- options.set_capability("automationName", "UiAutomator2")
28
- options.set_capability("platformName", "Android")
29
- options.set_capability("deviceName", os.getenv("ANDROID_DEVICE_VERSION"))
30
 
31
- # Context lengths to benchmark
32
  CONTEXT_LENGTHS = [128, 1024, 4096]
33
-
34
- # System prompt for completion benchmarks
35
  SYSTEM_PROMPT = "You are a helpful assistant. Be helpful but brief."
36
 
37
 
38
  class TestScorecard:
39
- @pytest.fixture
40
- def driver(self) -> webdriver.Remote:
41
- return webdriver.Remote(
42
- command_executor="http://127.0.0.1:4723/wd/hub", options=options
43
- )
44
-
45
- def test_scorecard(self, driver: webdriver.Remote) -> None:
46
  """Run comprehensive llama.cpp scorecard benchmarks."""
47
  model_url = "<<MODEL_URL>>"
48
  num_htps = "<<NUM_HTPS>>"
49
 
50
- # On-device paths (matching llama.cpp scripts/snapdragon/adb conventions)
51
- basedir = "/data/local/tmp/llama.cpp"
52
- model_path = "/data/local/tmp/gguf/model.gguf"
53
- log_file = "/data/local/tmp/QDC_logs/scorecard.log"
54
 
55
  scorecard_script = f"""
56
- cd /data/local/tmp/llama_cpp_bundle
57
 
58
- export LD_LIBRARY_PATH=/data/local/tmp/llama_cpp_bundle/lib:$LD_LIBRARY_PATH
59
- export ADSP_LIBRARY_PATH="/data/local/tmp/llama_cpp_bundle/lib;/system/lib/rfsa/adsp;/system/vendor/lib/rfsa/adsp;/dsp"
60
- chmod +x /data/local/tmp/llama_cpp_bundle/bin/*
61
 
62
- BASEDIR=/data/local/tmp/llama_cpp_bundle
63
  MODEL={model_path}
64
  LOG_FILE={log_file}
65
  NUM_HTPS={num_htps}
66
  HTP_FLAGS="--no-mmap --poll 1000 -t 6 --cpu-mask 0xfc --cpu-strict 1 -fa on -ngl 99"
67
 
68
- mkdir -p /data/local/tmp/gguf /data/local/tmp/QDC_logs
69
 
70
  echo "Downloading model from {model_url}..."
71
  curl -L -J --output $MODEL "{model_url}"
@@ -206,24 +189,13 @@ echo "=== SCORECARD COMPLETE ===" >> $LOG_FILE
206
  echo "============================================================" >> $LOG_FILE
207
  """
208
 
209
- # Push the bundle to the device
210
- subprocess.run(
211
- ["adb", "push", "/qdc/appium/llama_cpp_bundle/", "/data/local/tmp"],
212
- capture_output=True,
213
- encoding="utf-8",
214
- errors="replace",
215
- check=True,
216
- )
217
 
218
- # Run the scorecard script
219
  result = subprocess.run(
220
- [
221
- "adb",
222
- "shell",
223
- "sh",
224
- "-c",
225
- scorecard_script,
226
- ],
227
  capture_output=True,
228
  encoding="utf-8",
229
  errors="replace",
 
3
  # SPDX-License-Identifier: BSD-3-Clause
4
  # ---------------------------------------------------------------------
5
  """
6
+ Scorecard benchmark script for llama.cpp on Linux IoT devices.
7
 
8
+ Runs directly on a Linux IoT device via QDC — no Appium/ADB needed.
 
 
 
 
9
 
10
  Placeholders are replaced at artifact creation time:
11
  - <<MODEL_URL>>: URL to download the model
12
+ - <<NUM_HTPS>>: Number of HTP cores
13
  """
14
 
15
  import os
16
+ import shutil
17
  import subprocess
18
  import sys
19
 
20
  import pytest
 
 
21
 
22
+ from utils import write_qdc_log
 
 
 
23
 
 
24
  CONTEXT_LENGTHS = [128, 1024, 4096]
 
 
25
  SYSTEM_PROMPT = "You are a helpful assistant. Be helpful but brief."
26
 
27
 
28
  class TestScorecard:
29
+ def test_scorecard(self) -> None:
 
 
 
 
 
 
30
  """Run comprehensive llama.cpp scorecard benchmarks."""
31
  model_url = "<<MODEL_URL>>"
32
  num_htps = "<<NUM_HTPS>>"
33
 
34
+ bundle_path = "/tmp/llama_cpp_bundle"
35
+ model_path = "/tmp/gguf/model.gguf"
36
+ log_file = "/tmp/QDC_logs/scorecard.log"
 
37
 
38
  scorecard_script = f"""
39
+ cd {bundle_path}
40
 
41
+ export LD_LIBRARY_PATH={bundle_path}/lib:$LD_LIBRARY_PATH
42
+ export ADSP_LIBRARY_PATH={bundle_path}/lib
43
+ chmod +x {bundle_path}/bin/*
44
 
45
+ BASEDIR={bundle_path}
46
  MODEL={model_path}
47
  LOG_FILE={log_file}
48
  NUM_HTPS={num_htps}
49
  HTP_FLAGS="--no-mmap --poll 1000 -t 6 --cpu-mask 0xfc --cpu-strict 1 -fa on -ngl 99"
50
 
51
+ mkdir -p /tmp/gguf /tmp/QDC_logs
52
 
53
  echo "Downloading model from {model_url}..."
54
  curl -L -J --output $MODEL "{model_url}"
 
189
  echo "============================================================" >> $LOG_FILE
190
  """
191
 
192
+ src = "/qdc/appium/llama_cpp_bundle"
193
+ if os.path.isdir(src):
194
+ shutil.copytree(src, bundle_path, dirs_exist_ok=True)
195
+ subprocess.run(["chmod", "-R", "+x", f"{bundle_path}/bin"], check=False)
 
 
 
 
196
 
 
197
  result = subprocess.run(
198
+ ["sh", "-c", scorecard_script],
 
 
 
 
 
 
199
  capture_output=True,
200
  encoding="utf-8",
201
  errors="replace",
utils.py CHANGED
@@ -2,92 +2,53 @@
2
  # Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.
3
  # SPDX-License-Identifier: BSD-3-Clause
4
  # ---------------------------------------------------------------------
5
- """Shared helpers for QDC on-device test runners."""
6
 
7
  import os
 
8
  import subprocess
9
- import tempfile
10
-
11
- from appium.options.common import AppiumOptions
12
 
13
  # ---------------------------------------------------------------------------
14
  # On-device paths
15
  # ---------------------------------------------------------------------------
16
 
17
- BUNDLE_PATH = "/data/local/tmp/llama_cpp_bundle"
18
- QDC_LOGS_PATH = "/data/local/tmp/QDC_logs"
19
  LIB_PATH = f"{BUNDLE_PATH}/lib"
20
  BIN_PATH = f"{BUNDLE_PATH}/bin"
21
  ENV_PREFIX = (
22
- f"export LD_LIBRARY_PATH={LIB_PATH} && "
23
  f"export ADSP_LIBRARY_PATH={LIB_PATH} && "
24
  f"chmod +x {BIN_PATH}/* &&"
25
  )
26
  CMD_PREFIX = f"cd {BUNDLE_PATH} && {ENV_PREFIX}"
27
 
28
  # ---------------------------------------------------------------------------
29
- # Appium session options
30
- # ---------------------------------------------------------------------------
31
-
32
- options = AppiumOptions()
33
- options.set_capability("automationName", "UiAutomator2")
34
- options.set_capability("platformName", "Android")
35
- options.set_capability("deviceName", os.getenv("ANDROID_DEVICE_VERSION"))
36
-
37
- # ---------------------------------------------------------------------------
38
- # ADB helpers
39
  # ---------------------------------------------------------------------------
40
 
41
- def run_adb_command(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess:
42
- # Append exit-code sentinel because `adb shell` doesn't reliably propagate
43
- # the on-device exit code (older ADB versions always return 0).
44
- raw = subprocess.run(
45
- ["adb", "shell", f"{cmd}; echo __RC__:$?"],
46
  text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
47
  )
48
- stdout = raw.stdout
49
- returncode = raw.returncode
50
- if stdout:
51
- lines = stdout.rstrip("\n").split("\n")
52
- if lines and lines[-1].startswith("__RC__:"):
53
- try:
54
- returncode = int(lines[-1][7:])
55
- stdout = "\n".join(lines[:-1]) + "\n"
56
- except ValueError:
57
- pass
58
- print(stdout)
59
- result = subprocess.CompletedProcess(raw.args, returncode, stdout=stdout)
60
  if check:
61
- assert returncode == 0, f"Command failed (exit {returncode})"
62
  return result
63
 
64
 
65
  def write_qdc_log(filename: str, content: str) -> None:
66
- """Push content as a log file to QDC_LOGS_PATH on the device for QDC log collection."""
67
- subprocess.run(
68
- ["adb", "shell", f"mkdir -p {QDC_LOGS_PATH}"],
69
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
70
- )
71
- with tempfile.NamedTemporaryFile(mode="w", suffix=".log", delete=False) as f:
72
  f.write(content)
73
- tmp_path = f.name
74
- try:
75
- subprocess.run(
76
- ["adb", "push", tmp_path, f"{QDC_LOGS_PATH}/{filename}"],
77
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
78
- )
79
- finally:
80
- os.unlink(tmp_path)
81
 
82
 
83
  def push_bundle_if_needed(check_binary: str) -> None:
84
- """Push llama_cpp_bundle to the device if check_binary is not already present."""
85
- result = subprocess.run(
86
- ["adb", "shell", f"ls {check_binary}"],
87
- text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
88
- )
89
- if result.returncode != 0:
90
- subprocess.run(
91
- ["adb", "push", "/qdc/appium/llama_cpp_bundle/", "/data/local/tmp"],
92
- text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
93
- )
 
2
  # Copyright (c) 2025 Qualcomm Technologies, Inc. and/or its subsidiaries.
3
  # SPDX-License-Identifier: BSD-3-Clause
4
  # ---------------------------------------------------------------------
5
+ """Shared helpers for QDC on-device test runners (Linux IoT)."""
6
 
7
  import os
8
+ import shutil
9
  import subprocess
 
 
 
10
 
11
  # ---------------------------------------------------------------------------
12
  # On-device paths
13
  # ---------------------------------------------------------------------------
14
 
15
+ BUNDLE_PATH = "/tmp/llama_cpp_bundle"
16
+ QDC_LOGS_PATH = "/tmp/QDC_logs"
17
  LIB_PATH = f"{BUNDLE_PATH}/lib"
18
  BIN_PATH = f"{BUNDLE_PATH}/bin"
19
  ENV_PREFIX = (
20
+ f"export LD_LIBRARY_PATH={LIB_PATH}:$LD_LIBRARY_PATH && "
21
  f"export ADSP_LIBRARY_PATH={LIB_PATH} && "
22
  f"chmod +x {BIN_PATH}/* &&"
23
  )
24
  CMD_PREFIX = f"cd {BUNDLE_PATH} && {ENV_PREFIX}"
25
 
26
  # ---------------------------------------------------------------------------
27
+ # Shell helpers
 
 
 
 
 
 
 
 
 
28
  # ---------------------------------------------------------------------------
29
 
30
+ def run_shell_command(cmd: str, *, check: bool = True) -> subprocess.CompletedProcess:
31
+ result = subprocess.run(
32
+ ["sh", "-c", cmd],
 
 
33
  text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
34
  )
35
+ print(result.stdout)
 
 
 
 
 
 
 
 
 
 
 
36
  if check:
37
+ assert result.returncode == 0, f"Command failed (exit {result.returncode})"
38
  return result
39
 
40
 
41
  def write_qdc_log(filename: str, content: str) -> None:
42
+ """Write content as a log file to QDC_LOGS_PATH for QDC log collection."""
43
+ os.makedirs(QDC_LOGS_PATH, exist_ok=True)
44
+ with open(f"{QDC_LOGS_PATH}/{filename}", "w") as f:
 
 
 
45
  f.write(content)
 
 
 
 
 
 
 
 
46
 
47
 
48
  def push_bundle_if_needed(check_binary: str) -> None:
49
+ """Copy llama_cpp_bundle to /tmp if check_binary is not already present."""
50
+ if not os.path.exists(check_binary):
51
+ src = "/qdc/appium/llama_cpp_bundle"
52
+ if os.path.isdir(src):
53
+ shutil.copytree(src, BUNDLE_PATH, dirs_exist_ok=True)
54
+ subprocess.run(["chmod", "-R", "+x", BIN_PATH], check=False)