Files
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

169 lines
5.6 KiB
Python

# Copyright 2023 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==============================================================================
"""Tests for ProtoSplitter."""
import os
import random
import string
from typing import Optional
from absl.testing import parameterized
import riegeli
from tensorflow.python.platform import test
from tensorflow.tools.proto_splitter import chunk_pb2
from tensorflow.tools.proto_splitter import split
from tensorflow.tools.proto_splitter.testdata import test_message_pb2
class RepeatedStringSplitter(split.ComposableSplitter):
"""Splits a RepeatedString proto with N repeated strings into N chunks."""
def __init__(self, proto, **kwargs):
if not isinstance(proto, test_message_pb2.RepeatedString):
raise TypeError("Can only split RepeatedString type protos")
empty_strings = not bool(proto.strings)
super().__init__(proto, proto_as_initial_chunk=empty_strings, **kwargs)
def build_chunks(self):
for n, s in enumerate(self._proto.strings):
b = bytes(s, encoding="utf-8")
self.add_chunk(b, ["strings", n])
self._proto.ClearField("strings")
def _random_string(length):
return bytes(
"".join(random.choices(string.ascii_lowercase, k=length)),
encoding="utf-8",
)
class SplitRepeatedStringTest(test.TestCase):
def _to_proto(self, strings):
return test_message_pb2.RepeatedString(strings=strings)
def testSplit(self):
s = RepeatedStringSplitter(test_message_pb2.RepeatedString(strings=[]))
chunks = s.split()[0]
self.assertLen(chunks, 1)
self.assertIsInstance(chunks[0], test_message_pb2.RepeatedString)
s = RepeatedStringSplitter(
test_message_pb2.RepeatedString(strings=["a", "b", "c"])
)
chunks, chunked_message = s.split()
self.assertListEqual([b"a", b"b", b"c"], chunks)
self.assertLen(chunked_message.chunked_fields, 3)
def testWrite(self):
path = os.path.join(self.create_tempdir(), "split-repeat")
data = [_random_string(5), _random_string(10), _random_string(15)]
returned_path = RepeatedStringSplitter(
test_message_pb2.RepeatedString(strings=data)
).write(path)
self.assertEqual(returned_path, f"{path}.cpb")
with riegeli.RecordReader(open(f"{path}.cpb", "rb")) as reader:
self.assertTrue(reader.check_file_format())
records = list(reader.read_records())
self.assertLen(records, 4)
proto = chunk_pb2.ChunkMetadata()
proto.ParseFromString(records[-1])
self.assertLen(proto.message.chunked_fields, 3)
self.assertFalse(proto.message.HasField("chunk_index"))
expected_indices = [0, 1, 2]
# Check that the chunk indices and info are correct.
for expected_index, expected_data, chunk in zip(
expected_indices, data, proto.message.chunked_fields
):
i = chunk.message.chunk_index
self.assertEqual(expected_index, i)
chunk_info = proto.chunks[i]
self.assertEqual(chunk_pb2.ChunkInfo.Type.BYTES, chunk_info.type)
self.assertEqual(len(expected_data), chunk_info.size)
reader.seek_numeric(chunk_info.offset)
self.assertEqual(expected_data, reader.read_record())
def test_child_splitter(self):
proto = test_message_pb2.RepeatedRepeatedString(
rs=[
test_message_pb2.RepeatedString(strings=["a", "b", "c"]),
test_message_pb2.RepeatedString(strings=["d", "e"]),
]
)
splitter = NoOpSplitter(proto)
self.assertLen(splitter.split()[0], 1)
splitter.add_chunk("", [])
self.assertLen(splitter.split()[0], 2)
child = RepeatedStringSplitter(
proto.rs[0], parent_splitter=splitter, fields_in_parent=["rs", 0]
)
child.build_chunks()
self.assertLen(splitter.split()[0], 5) # Adds 3 chunks.
RepeatedStringSplitter(
proto.rs[1], parent_splitter=splitter, fields_in_parent=["rs", 1]
).build_chunks()
self.assertLen(splitter.split()[0], 7) # Adds 2 chunks.
with self.assertRaisesRegex(
ValueError, " `split` method should not be called directly"
):
child.split()
path = os.path.join(self.create_tempdir(), "na-split")
with self.assertRaisesRegex(
ValueError, " `write` method should not be called directly"
):
child.write(path)
class NoOpSplitter(split.ComposableSplitter):
def build_chunks(self):
pass
class NoOpSplitterTest(test.TestCase, parameterized.TestCase):
@parameterized.parameters([None, "uncompressed"])
def testWriteNoChunks(self, writer_options: Optional[str]):
path = os.path.join(self.create_tempdir(), "split-none")
proto = test_message_pb2.RepeatedString(strings=["a", "bc", "de"])
returned_path = NoOpSplitter(proto).write(path, writer_options)
expected_file_path = path + ".pb"
self.assertTrue(os.path.isfile(expected_file_path))
self.assertEqual(returned_path, expected_file_path)
parsed_proto = test_message_pb2.RepeatedString()
with open(expected_file_path, "rb") as f:
parsed_proto.ParseFromString(f.read())
self.assertProtoEquals(proto, parsed_proto)
if __name__ == "__main__":
test.main()