Skip to content

Commit 1c7334d

Browse files
committed
Implement the rest of the binary stuff.
1 parent b0d9b95 commit 1c7334d

File tree

3 files changed

+81
-67
lines changed

3 files changed

+81
-67
lines changed

python/selfie-lib/selfie_lib/SelfieImplementations.py

Lines changed: 57 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -186,83 +186,71 @@ def __init__(self, actual: Snapshot, disk: DiskStorage, only_facet: str):
186186
f"The facet {only_facet} is a string, not a binary snapshot"
187187
)
188188

189+
def _actual_bytes(self) -> bytes:
190+
return self.actual.subject_or_facet(self.only_facet).value_binary()
191+
192+
def to_match_disk(self, sub: str = "") -> "BinarySelfie":
193+
super().to_match_disk(sub)
194+
return self
195+
196+
def to_match_disk_TODO(self, sub: str = "") -> "BinarySelfie":
197+
super().to_match_disk_TODO(sub)
198+
return self
199+
200+
def to_be_base64_TODO(self, _: Any = None) -> bytes:
201+
_toBeDidntMatch(None, self._actual_string(), LiteralString())
202+
return self._actual_bytes()
203+
189204
def to_be_base64(self, expected: str) -> bytes:
190-
actual_bytes = self.actual.subject_or_facet(self.only_facet).value_binary()
191205
expected_bytes = base64.b64decode(expected)
206+
actual_bytes = self._actual_bytes()
192207
if actual_bytes == expected_bytes:
193208
return _checkSrc(actual_bytes)
194209
else:
195-
actual_b64 = base64.b64encode(actual_bytes).decode().replace("\r", "")
196-
_toBeDidntMatch(expected, actual_b64, LiteralString())
210+
_toBeDidntMatch(expected, self._actual_string(), LiteralString())
197211
return actual_bytes
198212

199-
def to_be_base64_TODO(self, _: Any = None) -> bytes:
200-
call = recordCall(False)
201-
if not _selfieSystem().mode.can_write(True, call, _selfieSystem()):
202-
raise _selfieSystem().fs.assert_failed(
203-
message=f"Can't call `to_be_base64_TODO` in {Mode.readonly} mode!"
204-
)
205-
return self._actual_bytes()
206-
207-
def _actual_bytes(self) -> bytes:
208-
return self.actual.subject_or_facet(self.only_facet).value_binary()
209-
210213
def _actual_string(self) -> str:
211214
return base64.b64encode(self._actual_bytes()).decode().replace("\r", "")
212215

213-
def to_be_file_impl(self, subpath: str, is_todo: bool) -> bytes:
216+
def _to_be_file_impl(self, subpath: str, is_todo: bool) -> bytes:
214217
call = recordCall(False)
215-
actual_bytes = self.actual.subject_or_facet(self.only_facet).value_binary()
216218
writable = _selfieSystem().mode.can_write(is_todo, call, _selfieSystem())
217-
if is_todo and not writable:
218-
raise _selfieSystem().fs.assert_failed(
219-
message=f"Can't call `to_be_file_TODO` in {Mode.readonly} mode!"
220-
)
221-
if not writable:
222-
path = (
223-
_selfieSystem()
224-
.layout.sourcefile_for_call(call.location)
225-
.parent_folder()
226-
.resolve_file(subpath)
227-
)
228-
if not _selfieSystem().fs.file_exists(path):
229-
raise _selfieSystem().fs.assert_failed(
230-
message=_selfieSystem().mode.msg_snapshot_not_found_no_such_file(
231-
path
232-
)
233-
)
234-
expected = _selfieSystem().fs.file_read_binary(path)
235-
if expected == actual_bytes:
236-
return actual_bytes
237-
else:
238-
raise _selfieSystem().fs.assert_failed(
239-
message=_selfieSystem().mode.msg_snapshot_mismatch(),
240-
expected=expected,
241-
actual=actual_bytes,
242-
)
243-
else:
219+
actual_bytes = self._actual_bytes()
220+
path = _selfieSystem().layout.root_folder().resolve_file(subpath)
221+
222+
if writable:
244223
if is_todo:
245224
_selfieSystem().write_inline(TodoStub.to_be_file.create_literal(), call)
246-
_selfieSystem().write_to_be_file(
247-
_selfieSystem()
248-
.layout.sourcefile_for_call(call.location)
249-
.parent_folder()
250-
.resolve_file(subpath),
251-
actual_bytes,
252-
call,
253-
)
225+
_selfieSystem().write_to_be_file(path, actual_bytes, call)
254226
return actual_bytes
227+
else:
228+
if is_todo:
229+
raise _selfieSystem().fs.assert_failed(
230+
f"Can't call `to_be_file_TODO` in {Mode.readonly} mode!"
231+
)
232+
else:
233+
if not _selfieSystem().fs.file_exists(path):
234+
raise _selfieSystem().fs.assert_failed(
235+
_selfieSystem().mode.msg_snapshot_not_found_no_such_file(path)
236+
)
237+
expected = _selfieSystem().fs.file_read_binary(path)
238+
if expected == actual_bytes:
239+
return actual_bytes
240+
else:
241+
raise _selfieSystem().fs.assert_failed(
242+
message=_selfieSystem().mode.msg_snapshot_mismatch_binary(
243+
expected, actual_bytes
244+
),
245+
expected=expected,
246+
actual=actual_bytes,
247+
)
255248

256249
def to_be_file_TODO(self, subpath: str) -> bytes:
257-
call = recordCall(False)
258-
if not _selfieSystem().mode.can_write(True, call, _selfieSystem()):
259-
raise _selfieSystem().fs.assert_failed(
260-
message=f"Can't call `to_be_file_TODO` in {Mode.readonly} mode!"
261-
)
262-
return self.to_be_file_impl(subpath, True)
250+
return self._to_be_file_impl(subpath, True)
263251

264252
def to_be_file(self, subpath: str) -> bytes:
265-
return self.to_be_file_impl(subpath, False)
253+
return self._to_be_file_impl(subpath, False)
266254

267255

268256
def _checkSrc(value: T) -> T:
@@ -279,11 +267,13 @@ def _toBeDidntMatch(expected: Optional[T], actual: T, fmt: LiteralFormat[T]) ->
279267
else:
280268
if expected is None:
281269
raise _selfieSystem().fs.assert_failed(
282-
message=f"Can't call `toBe_TODO` in {Mode.readonly} mode!"
270+
f"Can't call `toBe_TODO` in {Mode.readonly} mode!"
283271
)
284272
else:
285273
raise _selfieSystem().fs.assert_failed(
286-
message=_selfieSystem().mode.msg_snapshot_mismatch(),
274+
message=_selfieSystem().mode.msg_snapshot_mismatch(
275+
expected=repr(expected), actual=repr(actual)
276+
),
287277
expected=expected,
288278
actual=actual,
289279
)
@@ -308,10 +298,14 @@ def _assertEqual(
308298
),
309299
)
310300
)
301+
expectedFacets = _serializeOnlyFacets(expected, mismatched_keys)
302+
actualFacets = _serializeOnlyFacets(actual, mismatched_keys)
311303
raise storage.fs.assert_failed(
312-
message=storage.mode.msg_snapshot_mismatch(),
313-
expected=_serializeOnlyFacets(expected, mismatched_keys),
314-
actual=_serializeOnlyFacets(actual, mismatched_keys),
304+
message=storage.mode.msg_snapshot_mismatch(
305+
expected=expectedFacets, actual=actualFacets
306+
),
307+
expected=expectedFacets,
308+
actual=actualFacets,
315309
)
316310

317311

python/selfie-lib/selfie_lib/SnapshotSystem.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,24 @@ def msg_snapshot_not_found(self) -> str:
124124
def msg_snapshot_not_found_no_such_file(self, file) -> str:
125125
return self.msg(f"Snapshot not found: no such file {file}")
126126

127-
def msg_snapshot_mismatch(self) -> str:
128-
return self.msg("Snapshot mismatch")
127+
def msg_snapshot_mismatch(self, expected: str, actual: str) -> str: # noqa: ARG002
128+
return self.msg("Snapshot mismatch, TODO: string comparison")
129+
130+
def msg_snapshot_mismatch_binary(self, expected: bytes, actual: bytes) -> str:
131+
return self.msg_snapshot_mismatch(
132+
self._to_quoted_printable(expected), self._to_quoted_printable(actual)
133+
)
134+
135+
def _to_quoted_printable(self, byte_data: bytes) -> str:
136+
result = []
137+
for b in byte_data:
138+
# b is already an integer in [0..255] when iterating through a bytes object
139+
if 33 <= b <= 126 and b != 61: # '=' is ASCII 61, so skip it
140+
result.append(chr(b))
141+
else:
142+
# Convert to uppercase hex, pad to 2 digits, and prepend '='
143+
result.append(f"={b:02X}")
144+
return "".join(result)
129145

130146
def msg(self, headline: str) -> str:
131147
if self == Mode.interactive:

python/selfie-lib/selfie_lib/WriteTracker.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import inspect
22
import os
33
import threading
4-
from abc import ABC
4+
from abc import ABC, abstractmethod
55
from functools import total_ordering
66
from pathlib import Path
77
from typing import Generic, Optional, TypeVar, cast
@@ -82,10 +82,14 @@ def __hash__(self):
8282
return hash((self.location, tuple(self.rest_of_stack)))
8383

8484

85-
class SnapshotFileLayout:
85+
class SnapshotFileLayout(ABC):
8686
def __init__(self, fs: FS):
8787
self.fs = fs
8888

89+
@abstractmethod
90+
def root_folder() -> TypedPath:
91+
pass
92+
8993
def sourcefile_for_call(self, call: CallLocation) -> TypedPath:
9094
file_path = call.file_name
9195
if not file_path:

0 commit comments

Comments
 (0)