mirror of
https://github.com/scito/extract_otp_secret_keys.git
synced 2025-12-12 17:46:36 +01:00
test extract_otps_from_camera()
This commit is contained in:
parent
851cb6532c
commit
b215b78dad
2 changed files with 127 additions and 37 deletions
|
|
@ -220,42 +220,6 @@ def extract_otps(args: Args) -> Otps:
|
||||||
return extract_otps_from_files(args)
|
return extract_otps_from_files(args)
|
||||||
|
|
||||||
|
|
||||||
def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
|
|
||||||
if new_otps_count:
|
|
||||||
return SUCCESS_COLOR
|
|
||||||
else:
|
|
||||||
if otp_url:
|
|
||||||
return FAILURE_COLOR
|
|
||||||
else:
|
|
||||||
return NORMAL_COLOR
|
|
||||||
|
|
||||||
|
|
||||||
# TODO use cv2 types if available
|
|
||||||
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
|
|
||||||
pts = np.array([raw_pts], np.int32)
|
|
||||||
pts = pts.reshape((-1, 1, 2))
|
|
||||||
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
|
||||||
return pts
|
|
||||||
|
|
||||||
|
|
||||||
# TODO use cv2 types if available
|
|
||||||
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
|
||||||
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
|
||||||
out_text = text
|
|
||||||
if opposite_len:
|
|
||||||
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
|
||||||
actual_width = text_dim[TEXT_WIDTH] + opposite_len * CHAR_DX + 4 * BORDER
|
|
||||||
if actual_width >= window_dim[WINDOW_WIDTH]:
|
|
||||||
out_text = out_text[:(window_dim[WINDOW_WIDTH] - actual_width) // CHAR_DX] + '.'
|
|
||||||
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
|
||||||
if position == TextPosition.LEFT:
|
|
||||||
pos = BORDER, START_Y + line_number * FONT_DY
|
|
||||||
else:
|
|
||||||
pos = window_dim[WINDOW_WIDTH] - text_dim[TEXT_WIDTH] - BORDER, START_Y + line_number * FONT_DY
|
|
||||||
|
|
||||||
cv2.putText(img, out_text, pos, FONT, FONT_SCALE, color, FONT_THICKNESS, FONT_LINE_STYLE)
|
|
||||||
|
|
||||||
|
|
||||||
def extract_otps_from_camera(args: Args) -> Otps:
|
def extract_otps_from_camera(args: Args) -> Otps:
|
||||||
if verbose: print("Capture QR codes from camera")
|
if verbose: print("Capture QR codes from camera")
|
||||||
otp_urls: OtpUrls = []
|
otp_urls: OtpUrls = []
|
||||||
|
|
@ -325,6 +289,42 @@ def extract_otps_from_camera(args: Args) -> Otps:
|
||||||
return otps
|
return otps
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
|
||||||
|
if new_otps_count:
|
||||||
|
return SUCCESS_COLOR
|
||||||
|
else:
|
||||||
|
if otp_url:
|
||||||
|
return FAILURE_COLOR
|
||||||
|
else:
|
||||||
|
return NORMAL_COLOR
|
||||||
|
|
||||||
|
|
||||||
|
# TODO use cv2 types if available
|
||||||
|
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
|
||||||
|
pts = np.array([raw_pts], np.int32)
|
||||||
|
pts = pts.reshape((-1, 1, 2))
|
||||||
|
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
||||||
|
return pts
|
||||||
|
|
||||||
|
|
||||||
|
# TODO use cv2 types if available
|
||||||
|
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
||||||
|
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
||||||
|
out_text = text
|
||||||
|
if opposite_len:
|
||||||
|
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
||||||
|
actual_width = text_dim[TEXT_WIDTH] + opposite_len * CHAR_DX + 4 * BORDER
|
||||||
|
if actual_width >= window_dim[WINDOW_WIDTH]:
|
||||||
|
out_text = out_text[:(window_dim[WINDOW_WIDTH] - actual_width) // CHAR_DX] + '.'
|
||||||
|
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
||||||
|
if position == TextPosition.LEFT:
|
||||||
|
pos = BORDER, START_Y + line_number * FONT_DY
|
||||||
|
else:
|
||||||
|
pos = window_dim[WINDOW_WIDTH] - text_dim[TEXT_WIDTH] - BORDER, START_Y + line_number * FONT_DY
|
||||||
|
|
||||||
|
cv2.putText(img, out_text, pos, FONT, FONT_SCALE, color, FONT_THICKNESS, FONT_LINE_STYLE)
|
||||||
|
|
||||||
|
|
||||||
def cv2_handle_pressed_keys(qr_mode: QRMode) -> Tuple[bool, QRMode]:
|
def cv2_handle_pressed_keys(qr_mode: QRMode) -> Tuple[bool, QRMode]:
|
||||||
key = cv2.waitKey(1) & 0xFF
|
key = cv2.waitKey(1) & 0xFF
|
||||||
quit = False
|
quit = False
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ import pathlib
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from enum import Enum
|
||||||
|
from typing import Any, List, Optional, Tuple
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -37,6 +38,12 @@ from utils import (count_files_in_dir, file_exits, read_binary_file_as_stream,
|
||||||
|
|
||||||
import extract_otp_secrets
|
import extract_otp_secrets
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cv2 # type: ignore
|
||||||
|
except ImportError:
|
||||||
|
# ignore
|
||||||
|
pass
|
||||||
|
|
||||||
qreader_available: bool = extract_otp_secrets.qreader_available
|
qreader_available: bool = extract_otp_secrets.qreader_available
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -494,6 +501,89 @@ def test_extract_no_arguments(capsys: pytest.CaptureFixture[str], mocker: Mocker
|
||||||
assert e.type == SystemExit
|
assert e.type == SystemExit
|
||||||
|
|
||||||
|
|
||||||
|
MockMode = Enum('MockMode', ['REPEAT_FIRST_ENDLESS', 'LOOP_LIST'])
|
||||||
|
|
||||||
|
|
||||||
|
class MockCam:
|
||||||
|
|
||||||
|
read_counter: int = 0
|
||||||
|
read_files: List[str] = []
|
||||||
|
mock_mode: MockMode
|
||||||
|
|
||||||
|
def __init__(self, files: List[str] = ['example_export.png'], mock_mode: MockMode = MockMode.REPEAT_FIRST_ENDLESS):
|
||||||
|
self.read_files = files
|
||||||
|
self.image_mode = mock_mode
|
||||||
|
|
||||||
|
def read(self) -> Tuple[bool, Any]:
|
||||||
|
if self.image_mode == MockMode.REPEAT_FIRST_ENDLESS:
|
||||||
|
file = self.read_files[0]
|
||||||
|
elif self.image_mode == MockMode.LOOP_LIST:
|
||||||
|
file = self.read_files[self.read_counter]
|
||||||
|
self.read_counter += 1
|
||||||
|
|
||||||
|
if file:
|
||||||
|
img = cv2.imread(file)
|
||||||
|
return True, img
|
||||||
|
else:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
def release(self) -> None:
|
||||||
|
# ignore
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("qr_reader", [
|
||||||
|
None,
|
||||||
|
'ZBAR',
|
||||||
|
'QREADER',
|
||||||
|
'QREADER_DEEP',
|
||||||
|
'CV2',
|
||||||
|
'CV2_WECHAT'
|
||||||
|
])
|
||||||
|
def test_extract_otps_from_camera(qr_reader: Optional[str], capsys: pytest.CaptureFixture[str], mocker: MockerFixture) -> None:
|
||||||
|
if qreader_available:
|
||||||
|
# Arrange
|
||||||
|
mockCam = MockCam()
|
||||||
|
mocker.patch('cv2.VideoCapture', return_value=mockCam)
|
||||||
|
mocker.patch('cv2.namedWindow')
|
||||||
|
mocker.patch('cv2.rectangle')
|
||||||
|
mocker.patch('cv2.polylines')
|
||||||
|
mocker.patch('cv2.imshow')
|
||||||
|
mocker.patch('cv2.getTextSize', return_value=([8, 200], False))
|
||||||
|
mocker.patch('cv2.putText')
|
||||||
|
mocker.patch('cv2.getWindowImageRect', return_value=[0, 0, 640, 480])
|
||||||
|
mocker.patch('cv2.waitKey', return_value=27)
|
||||||
|
mocker.patch('cv2.getWindowProperty', return_value=False)
|
||||||
|
mocker.patch('cv2.destroyAllWindows')
|
||||||
|
|
||||||
|
args = []
|
||||||
|
if qr_reader:
|
||||||
|
args.append('-Q')
|
||||||
|
args.append(qr_reader)
|
||||||
|
# Act
|
||||||
|
extract_otp_secrets.main(args)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||||
|
assert captured.err == ''
|
||||||
|
else:
|
||||||
|
# Act
|
||||||
|
with pytest.raises(SystemExit) as e:
|
||||||
|
extract_otp_secrets.main([])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
expected_err_msg = 'error: the following arguments are required: infile'
|
||||||
|
|
||||||
|
assert expected_err_msg in captured.err
|
||||||
|
assert captured.out == ''
|
||||||
|
assert e.value.code == 2
|
||||||
|
assert e.type == SystemExit
|
||||||
|
|
||||||
|
|
||||||
def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None:
|
def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
with pytest.raises(SystemExit) as e:
|
with pytest.raises(SystemExit) as e:
|
||||||
# Act
|
# Act
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue