# Copyright (c) 2024 PaddlePaddle 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. # [AUTO-GENERATED] Unit test for paddle.io.dataloader.dataset # 自动生成的单测,覆盖 paddle.io.dataloader.dataset 模块中未覆盖的代码 # Target: cover uncovered lines 323-336, 340-344, 389-410, 453-462, 588, 629-630, 677-710 # in paddle/python/paddle/io/dataloader/dataset.py # 目标:覆盖 dataset.py 中 TensorDataset 的创建和使用、to_list 辅助函数、 # ComposeDataset 的初始化和错误检查、ChainDataset 的初始化和迭代、 # random_split 的长度校验错误、_accumulate 空列表路径、 # ConcatDataset 的创建和负索引 """ This test covers the following modules and code paths: 这个测试覆盖以下模块和代码路径: 1. TensorDataset - creation, __getitem__, __len__ (lines 322-336) TensorDataset - 创建、索引和长度 2. to_list() helper function - all three branches: None, list/tuple, scalar (lines 340-344) to_list() 辅助函数 - 三个分支:None、列表/元组、标量 3. ComposeDataset - init validation and __getitem__ (lines 389-410) ComposeDataset - 初始化校验和索引取值 4. ChainDataset - init validation and __iter__ (lines 453-462) ChainDataset - 初始化校验和迭代 5. random_split - sum-of-lengths mismatch error (line 588) random_split - 长度总和不匹配错误 6. _accumulate - empty iterable early return (lines 629-630) _accumulate - 空可迭代对象的提前返回 7. ConcatDataset - cumsum, init, __len__, negative indexing (lines 677-710) ConcatDataset - 累加和、初始化、长度、负索引 """ import unittest import numpy as np import paddle from paddle.io import ( ChainDataset, ComposeDataset, ConcatDataset, Dataset, IterableDataset, TensorDataset, random_split, ) from paddle.io.dataloader.dataset import _accumulate # Helper datasets # 辅助数据集类 class SimpleMapDataset(Dataset): """A simple map-style dataset for testing. 用于测试的简单映射式数据集。 """ def __init__(self, num_samples, return_type='tuple'): self.num_samples = num_samples self.return_type = return_type def __getitem__(self, idx): if self.return_type == 'tuple': return ( np.array([idx], dtype='float32'), np.array([idx * 2], dtype='int64'), ) elif self.return_type == 'scalar': return np.array([idx], dtype='float32') elif self.return_type == 'list': return [ np.array([idx], dtype='float32'), np.array([idx * 2], dtype='int64'), ] def __len__(self): return self.num_samples class SimpleIterableDataset(IterableDataset): """A simple iterable dataset for testing. 用于测试的简单可迭代数据集。 """ def __init__(self, num_samples): self.num_samples = num_samples def __iter__(self): for i in range(self.num_samples): yield np.array([i], dtype='float32') class TestTensorDataset(unittest.TestCase): """Test TensorDataset creation, __getitem__, and __len__. 测试 TensorDataset 的创建、索引取值和长度。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 322-336 行。 """ def test_tensor_dataset_basic(self): """TensorDataset should store and index tensors correctly. TensorDataset 应正确存储和索引张量。 """ data = paddle.to_tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]) labels = paddle.to_tensor([0, 1, 2]) dataset = TensorDataset([data, labels]) self.assertEqual(len(dataset), 3) item = dataset[0] self.assertEqual(len(item), 2) np.testing.assert_allclose(item[0].numpy(), [1.0, 2.0]) np.testing.assert_allclose(item[1].numpy(), 0) def test_tensor_dataset_all_items(self): """TensorDataset should return correct items for all indices. TensorDataset 应对所有索引返回正确的元素。 """ data = paddle.arange(0, 15, dtype='float32').reshape([5, 3]) labels = paddle.arange(0, 5, dtype='int64') dataset = TensorDataset([data, labels]) for i in range(len(dataset)): item_data, item_label = dataset[i] np.testing.assert_allclose(item_data.numpy(), data[i].numpy()) np.testing.assert_allclose(item_label.numpy(), labels[i].numpy()) def test_tensor_dataset_single_tensor(self): """TensorDataset should work with a single tensor. TensorDataset 应支持仅一个张量的情况。 """ data = paddle.to_tensor([[1.0], [2.0], [3.0]]) dataset = TensorDataset([data]) self.assertEqual(len(dataset), 3) item = dataset[1] self.assertEqual(len(item), 1) np.testing.assert_allclose(item[0].numpy(), [2.0]) def test_tensor_dataset_shape_mismatch(self): """TensorDataset should raise AssertionError for shape mismatch. 当张量第一维大小不一致时应抛出 AssertionError。 """ data = paddle.to_tensor([[1.0], [2.0], [3.0]]) labels = paddle.to_tensor([0, 1]) with self.assertRaises(AssertionError): TensorDataset([data, labels]) class TestToListFunction(unittest.TestCase): """Test the to_list() helper function used by ComposeDataset. 测试 ComposeDataset 使用的 to_list() 辅助函数。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 340-344 行。 """ def test_compose_with_tuple_return(self): """ComposeDataset with datasets returning tuples calls to_list with tuple. 当数据集返回元组时,to_list 接收到元组并转换为列表。 """ ds1 = SimpleMapDataset(5, return_type='tuple') ds2 = SimpleMapDataset(5, return_type='tuple') dataset = ComposeDataset([ds1, ds2]) item = dataset[0] # ds1 returns (data, label), ds2 returns (data, label) => 4 items self.assertEqual(len(item), 4) def test_compose_with_scalar_return(self): """ComposeDataset with datasets returning scalars calls to_list with scalar. 当数据集返回标量时,to_list 接收到标量并包装为列表(覆盖第 344 行)。 """ ds1 = SimpleMapDataset(5, return_type='scalar') ds2 = SimpleMapDataset(5, return_type='scalar') dataset = ComposeDataset([ds1, ds2]) item = dataset[0] # Each dataset returns a single value, wrapped in list => 2 items self.assertEqual(len(item), 2) def test_compose_with_list_return(self): """ComposeDataset with datasets returning lists calls to_list with list. 当数据集返回列表时,to_list 接收到列表并转换(覆盖第 342-343 行)。 """ ds1 = SimpleMapDataset(5, return_type='list') ds2 = SimpleMapDataset(5, return_type='list') dataset = ComposeDataset([ds1, ds2]) item = dataset[0] self.assertEqual(len(item), 4) class TestComposeDatasetValidation(unittest.TestCase): """Test ComposeDataset initialization validation. 测试 ComposeDataset 初始化校验。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 389-401 行。 """ def test_empty_datasets(self): """ComposeDataset should raise AssertionError for empty datasets. 空数据集列表应抛出 AssertionError。 """ with self.assertRaises(AssertionError): ComposeDataset([]) def test_non_dataset(self): """ComposeDataset should raise AssertionError for non-Dataset items. 非 Dataset 对象应抛出 AssertionError。 """ with self.assertRaises(AssertionError): ComposeDataset(["not_a_dataset"]) def test_iterable_dataset_rejected(self): """ComposeDataset should reject IterableDataset. 应拒绝 IterableDataset。 """ with self.assertRaises(AssertionError): ComposeDataset([SimpleIterableDataset(10)]) def test_length_mismatch(self): """ComposeDataset should raise AssertionError for length-mismatched datasets. 长度不匹配的数据集应抛出 AssertionError。 """ with self.assertRaises(AssertionError): ComposeDataset([SimpleMapDataset(10), SimpleMapDataset(5)]) def test_compose_len(self): """ComposeDataset.__len__ should delegate to first dataset. ComposeDataset 的长度应等于第一个子数据集的长度。 """ dataset = ComposeDataset([SimpleMapDataset(7), SimpleMapDataset(7)]) self.assertEqual(len(dataset), 7) class TestChainDatasetValidation(unittest.TestCase): """Test ChainDataset initialization and iteration. 测试 ChainDataset 的初始化校验和迭代。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 453-462 行。 """ def test_empty_datasets(self): """ChainDataset should raise AssertionError for empty datasets. 空数据集列表应抛出 AssertionError。 """ with self.assertRaises(AssertionError): ChainDataset([]) def test_non_iterable_dataset(self): """ChainDataset should raise AssertionError for non-IterableDataset. 非 IterableDataset 应抛出 AssertionError。 """ with self.assertRaises(AssertionError): ChainDataset([SimpleMapDataset(10)]) def test_chain_iteration(self): """ChainDataset should iterate through all datasets sequentially. ChainDataset 应按顺序迭代所有数据集。 """ ds1 = SimpleIterableDataset(3) ds2 = SimpleIterableDataset(4) chain = ChainDataset([ds1, ds2]) items = [] for item in chain: items.append(item) self.assertEqual(len(items), 7) # First 3 items from ds1 (0,1,2), next 4 from ds2 (0,1,2,3) np.testing.assert_allclose(items[0], [0]) np.testing.assert_allclose(items[2], [2]) np.testing.assert_allclose(items[3], [0]) np.testing.assert_allclose(items[6], [3]) class TestRandomSplitLengthError(unittest.TestCase): """Test random_split raises ValueError for mismatched lengths. 测试 random_split 在长度不匹配时抛出 ValueError。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 587-590 行。 """ def test_lengths_sum_mismatch(self): """random_split should raise ValueError when sum(lengths) != len(dataset). 当 lengths 之和不等于数据集长度时应抛出 ValueError。 """ dataset = SimpleMapDataset(10) with self.assertRaises(ValueError): random_split(dataset, [3, 3]) def test_lengths_sum_too_large(self): """random_split should raise ValueError when sum exceeds dataset length. 当 lengths 之和超过数据集长度时应抛出 ValueError。 """ dataset = SimpleMapDataset(5) with self.assertRaises(ValueError): random_split(dataset, [3, 5]) def test_valid_random_split(self): """random_split should work when sum(lengths) == len(dataset). 当 lengths 之和等于数据集长度时应正常工作。 """ dataset = SimpleMapDataset(10) splits = random_split(dataset, [3, 7]) self.assertEqual(len(splits), 2) self.assertEqual(len(splits[0]), 3) self.assertEqual(len(splits[1]), 7) class TestAccumulateEmpty(unittest.TestCase): """Test _accumulate with empty iterable. 测试 _accumulate 处理空可迭代对象。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 629-630 行。 """ def test_accumulate_empty_list(self): """_accumulate with empty list should return empty. 空列表应返回空结果。 """ result = list(_accumulate([])) self.assertEqual(result, []) def test_accumulate_normal(self): """_accumulate with normal list should return running totals. 正常列表应返回累积和。 """ result = list(_accumulate([1, 2, 3, 4, 5])) self.assertEqual(result, [1, 3, 6, 10, 15]) class TestConcatDataset(unittest.TestCase): """Test ConcatDataset creation, negative indexing, and validation. 测试 ConcatDataset 的创建、负索引和校验。 覆盖 paddle/python/paddle/io/dataloader/dataset.py 第 677-710 行。 """ def test_concat_basic(self): """ConcatDataset should concatenate multiple datasets. ConcatDataset 应正确连接多个数据集。 """ ds1 = SimpleMapDataset(5) ds2 = SimpleMapDataset(3) concat = ConcatDataset([ds1, ds2]) self.assertEqual(len(concat), 8) def test_concat_negative_index(self): """ConcatDataset should support negative indexing. ConcatDataset 应支持负索引。 覆盖第 699-704 行。 """ ds1 = SimpleMapDataset(5, return_type='scalar') ds2 = SimpleMapDataset(3, return_type='scalar') concat = ConcatDataset([ds1, ds2]) # Last item should be from ds2, index 2 last_item = concat[-1] np.testing.assert_allclose(last_item, [2]) # Second to last item second_last = concat[-2] np.testing.assert_allclose(second_last, [1]) def test_concat_negative_index_out_of_bounds(self): """ConcatDataset should raise ValueError for out-of-bounds negative index. 超出范围的负索引应抛出 ValueError。 覆盖第 700-703 行。 """ ds1 = SimpleMapDataset(3) ds2 = SimpleMapDataset(2) concat = ConcatDataset([ds1, ds2]) with self.assertRaises(ValueError): concat[-10] def test_concat_first_dataset_index(self): """ConcatDataset should correctly index into first dataset. ConcatDataset 应正确索引到第一个子数据集。 覆盖第 706-707 行 (dataset_idx == 0 分支)。 """ ds1 = SimpleMapDataset(5, return_type='scalar') ds2 = SimpleMapDataset(3, return_type='scalar') concat = ConcatDataset([ds1, ds2]) item = concat[2] np.testing.assert_allclose(item, [2]) def test_concat_second_dataset_index(self): """ConcatDataset should correctly index into second dataset. ConcatDataset 应正确索引到第二个子数据集。 覆盖第 708-709 行 (dataset_idx > 0 分支)。 """ ds1 = SimpleMapDataset(5, return_type='scalar') ds2 = SimpleMapDataset(3, return_type='scalar') concat = ConcatDataset([ds1, ds2]) # Index 5 should be the first item in ds2 item = concat[5] np.testing.assert_allclose(item, [0]) def test_concat_empty_raises(self): """ConcatDataset should raise AssertionError for empty datasets. 空数据集列表应抛出 AssertionError。 """ with self.assertRaises(AssertionError): ConcatDataset([]) def test_concat_iterable_rejected(self): """ConcatDataset should reject IterableDataset. 应拒绝 IterableDataset。 """ with self.assertRaises(AssertionError): ConcatDataset([SimpleIterableDataset(10)]) def test_concat_cumsum(self): """ConcatDataset.cumsum should compute correct cumulative sizes. ConcatDataset.cumsum 应计算正确的累积大小。 覆盖第 677-682 行。 """ ds1 = SimpleMapDataset(5) ds2 = SimpleMapDataset(3) ds3 = SimpleMapDataset(7) result = ConcatDataset.cumsum([ds1, ds2, ds3]) self.assertEqual(result, [5, 8, 15]) if __name__ == '__main__': unittest.main()