JXPlaybackProgressStorageTests.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. //
  2. // JXPlaybackProgressStorageTests.m
  3. // AICityTests
  4. //
  5. // Feature: 003-ios-api-https
  6. // 播放进度本地存储单元测试
  7. //
  8. #import <XCTest/XCTest.h>
  9. #import "JXPlaybackProgressStorage.h"
  10. #import "JXPlaybackProgressModel.h"
  11. @interface JXPlaybackProgressStorageTests : XCTestCase
  12. @property (nonatomic, strong) JXPlaybackProgressStorage *storage;
  13. @end
  14. @implementation JXPlaybackProgressStorageTests
  15. - (void)setUp {
  16. [super setUp];
  17. self.storage = [JXPlaybackProgressStorage sharedStorage];
  18. // 清空所有数据
  19. [self.storage clearAll];
  20. }
  21. - (void)tearDown {
  22. // 清理测试数据
  23. [self.storage clearAll];
  24. [super tearDown];
  25. }
  26. #pragma mark - 基础功能测试
  27. - (void)testSaveAndGetProgress {
  28. // Given
  29. NSString *episodeId = @"test_ep_001";
  30. NSString *dramaId = @"test_drama_001";
  31. int64_t position = 30000;
  32. int64_t duration = 120000;
  33. // When
  34. XCTestExpectation *expectation = [self expectationWithDescription:@"Save progress"];
  35. [self.storage saveProgressWithEpisodeId:episodeId
  36. dramaId:dramaId
  37. position:position
  38. duration:duration
  39. isCompleted:NO
  40. synced:NO
  41. completion:^(BOOL success, NSError *error) {
  42. XCTAssertTrue(success, @"保存应该成功");
  43. XCTAssertNil(error, @"不应该有错误");
  44. [expectation fulfill];
  45. }];
  46. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  47. // Then
  48. JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId];
  49. XCTAssertNotNil(progress, @"应该能获取到进度");
  50. XCTAssertEqualObjects(progress.episodeId, episodeId);
  51. XCTAssertEqualObjects(progress.dramaId, dramaId);
  52. XCTAssertEqual(progress.position, position);
  53. XCTAssertEqual(progress.duration, duration);
  54. XCTAssertEqualWithAccuracy(progress.progress, 0.25f, 0.01f); // 30000/120000 = 0.25
  55. XCTAssertFalse(progress.isCompleted);
  56. XCTAssertFalse(progress.synced);
  57. }
  58. - (void)testUpdateExistingProgress {
  59. // Given
  60. NSString *episodeId = @"test_ep_002";
  61. XCTestExpectation *expectation1 = [self expectationWithDescription:@"Save initial"];
  62. [self.storage saveProgressWithEpisodeId:episodeId
  63. dramaId:@"test_drama_002"
  64. position:10000
  65. duration:120000
  66. isCompleted:NO
  67. synced:NO
  68. completion:^(BOOL success, NSError *error) {
  69. [expectation1 fulfill];
  70. }];
  71. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  72. // When - 更新进度
  73. XCTestExpectation *expectation2 = [self expectationWithDescription:@"Update progress"];
  74. [self.storage saveProgressWithEpisodeId:episodeId
  75. dramaId:@"test_drama_002"
  76. position:60000
  77. duration:120000
  78. isCompleted:NO
  79. synced:YES
  80. completion:^(BOOL success, NSError *error) {
  81. [expectation2 fulfill];
  82. }];
  83. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  84. // Then
  85. JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId];
  86. XCTAssertEqual(progress.position, 60000);
  87. XCTAssertEqualWithAccuracy(progress.progress, 0.5f, 0.01f);
  88. XCTAssertTrue(progress.synced);
  89. }
  90. - (void)testGetProgressByDrama {
  91. // Given
  92. NSString *dramaId = @"test_drama_003";
  93. XCTestExpectation *expectation = [self expectationWithDescription:@"Save multiple"];
  94. [self.storage saveProgressWithEpisodeId:@"ep001" dramaId:dramaId position:10000 duration:120000 isCompleted:NO synced:NO completion:nil];
  95. [self.storage saveProgressWithEpisodeId:@"ep002" dramaId:dramaId position:20000 duration:120000 isCompleted:NO synced:NO completion:nil];
  96. [self.storage saveProgressWithEpisodeId:@"ep003" dramaId:@"other_drama" position:30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) {
  97. [expectation fulfill];
  98. }];
  99. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  100. // When
  101. NSArray<JXPlaybackProgressModel *> *progresses = [self.storage getProgressByDramaId:dramaId];
  102. // Then
  103. XCTAssertEqual(progresses.count, 2, @"应该有2条记录");
  104. BOOL hasEp001 = NO;
  105. BOOL hasEp002 = NO;
  106. for (JXPlaybackProgressModel *p in progresses) {
  107. if ([p.episodeId isEqualToString:@"ep001"]) hasEp001 = YES;
  108. if ([p.episodeId isEqualToString:@"ep002"]) hasEp002 = YES;
  109. }
  110. XCTAssertTrue(hasEp001 && hasEp002, @"应该包含ep001和ep002");
  111. }
  112. - (void)testGetUnsyncedProgresses {
  113. // Given
  114. XCTestExpectation *expectation = [self expectationWithDescription:@"Save progresses"];
  115. [self.storage saveProgressWithEpisodeId:@"ep001" dramaId:@"drama" position:10000 duration:120000 isCompleted:NO synced:YES completion:nil];
  116. [self.storage saveProgressWithEpisodeId:@"ep002" dramaId:@"drama" position:20000 duration:120000 isCompleted:NO synced:NO completion:nil];
  117. [self.storage saveProgressWithEpisodeId:@"ep003" dramaId:@"drama" position:30000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) {
  118. [expectation fulfill];
  119. }];
  120. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  121. // When
  122. NSArray<JXPlaybackProgressModel *> *unsyncedProgresses = [self.storage getUnsyncedProgresses];
  123. // Then
  124. XCTAssertEqual(unsyncedProgresses.count, 2, @"应该有2条未同步记录");
  125. for (JXPlaybackProgressModel *p in unsyncedProgresses) {
  126. XCTAssertFalse(p.synced, @"所有记录都应该是未同步的");
  127. }
  128. }
  129. - (void)testMarkAsSynced {
  130. // Given
  131. NSString *episodeId = @"test_ep_004";
  132. XCTestExpectation *expectation = [self expectationWithDescription:@"Save progress"];
  133. [self.storage saveProgressWithEpisodeId:episodeId
  134. dramaId:@"drama"
  135. position:10000
  136. duration:120000
  137. isCompleted:NO
  138. synced:NO
  139. completion:^(BOOL success, NSError *error) {
  140. [expectation fulfill];
  141. }];
  142. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  143. // When
  144. [self.storage markAsSyncedWithEpisodeId:episodeId];
  145. // Then
  146. JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId];
  147. XCTAssertTrue(progress.synced, @"应该被标记为已同步");
  148. }
  149. - (void)testDeleteProgress {
  150. // Given
  151. NSString *episodeId = @"test_ep_005";
  152. XCTestExpectation *expectation = [self expectationWithDescription:@"Save progress"];
  153. [self.storage saveProgressWithEpisodeId:episodeId dramaId:@"drama" position:10000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) {
  154. [expectation fulfill];
  155. }];
  156. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  157. XCTAssertNotNil([self.storage getProgressWithEpisodeId:episodeId], @"保存后应该存在");
  158. // When
  159. [self.storage deleteProgressWithEpisodeId:episodeId];
  160. // Then
  161. JXPlaybackProgressModel *progress = [self.storage getProgressWithEpisodeId:episodeId];
  162. XCTAssertNil(progress, @"删除后应该不存在");
  163. }
  164. - (void)testDeleteProgressByDrama {
  165. // Given
  166. NSString *dramaId = @"test_drama_006";
  167. XCTestExpectation *expectation = [self expectationWithDescription:@"Save progresses"];
  168. [self.storage saveProgressWithEpisodeId:@"ep001" dramaId:dramaId position:10000 duration:120000 isCompleted:NO synced:NO completion:nil];
  169. [self.storage saveProgressWithEpisodeId:@"ep002" dramaId:dramaId position:20000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) {
  170. [expectation fulfill];
  171. }];
  172. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  173. // When
  174. [self.storage deleteProgressByDramaId:dramaId];
  175. // Then
  176. NSArray *progresses = [self.storage getProgressByDramaId:dramaId];
  177. XCTAssertEqual(progresses.count, 0, @"删除后应该没有记录");
  178. }
  179. #pragma mark - 边界值测试
  180. - (void)testProgressCalculation {
  181. XCTestExpectation *expectation = [self expectationWithDescription:@"Save progresses"];
  182. // Test case 1: 25% progress
  183. [self.storage saveProgressWithEpisodeId:@"ep_25" dramaId:@"drama" position:30000 duration:120000 isCompleted:NO synced:NO completion:nil];
  184. // Test case 2: 50% progress
  185. [self.storage saveProgressWithEpisodeId:@"ep_50" dramaId:@"drama" position:60000 duration:120000 isCompleted:NO synced:NO completion:nil];
  186. // Test case 3: 95% progress (completed)
  187. [self.storage saveProgressWithEpisodeId:@"ep_95" dramaId:@"drama" position:114000 duration:120000 isCompleted:YES synced:NO completion:^(BOOL success, NSError *error) {
  188. [expectation fulfill];
  189. }];
  190. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  191. // Verify
  192. JXPlaybackProgressModel *progress25 = [self.storage getProgressWithEpisodeId:@"ep_25"];
  193. XCTAssertEqualWithAccuracy(progress25.progress, 0.25f, 0.01f);
  194. JXPlaybackProgressModel *progress50 = [self.storage getProgressWithEpisodeId:@"ep_50"];
  195. XCTAssertEqualWithAccuracy(progress50.progress, 0.5f, 0.01f);
  196. JXPlaybackProgressModel *progress95 = [self.storage getProgressWithEpisodeId:@"ep_95"];
  197. XCTAssertEqualWithAccuracy(progress95.progress, 0.95f, 0.01f);
  198. XCTAssertTrue(progress95.isCompleted);
  199. }
  200. - (void)testEdgeCases {
  201. XCTestExpectation *expectation = [self expectationWithDescription:@"Save edge cases"];
  202. // Test case 1: Zero duration
  203. [self.storage saveProgressWithEpisodeId:@"ep_zero" dramaId:@"drama" position:0 duration:0 isCompleted:NO synced:NO completion:nil];
  204. // Test case 2: Position exceeds duration
  205. [self.storage saveProgressWithEpisodeId:@"ep_exceed" dramaId:@"drama" position:150000 duration:120000 isCompleted:NO synced:NO completion:^(BOOL success, NSError *error) {
  206. [expectation fulfill];
  207. }];
  208. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  209. // Verify
  210. JXPlaybackProgressModel *progressZero = [self.storage getProgressWithEpisodeId:@"ep_zero"];
  211. XCTAssertEqual(progressZero.progress, 0.0f);
  212. JXPlaybackProgressModel *progressExceed = [self.storage getProgressWithEpisodeId:@"ep_exceed"];
  213. XCTAssertLessThanOrEqual(progressExceed.progress, 1.0f);
  214. }
  215. #pragma mark - 性能测试
  216. - (void)testPerformanceSaveMultipleProgresses {
  217. [self measureBlock:^{
  218. for (int i = 0; i < 100; i++) {
  219. NSString *episodeId = [NSString stringWithFormat:@"perf_ep_%d", i];
  220. [self.storage saveProgressWithEpisodeId:episodeId
  221. dramaId:@"perf_drama"
  222. position:i * 1000
  223. duration:120000
  224. isCompleted:NO
  225. synced:NO
  226. completion:nil];
  227. }
  228. }];
  229. }
  230. - (void)testPerformanceQueryProgresses {
  231. // 准备数据
  232. for (int i = 0; i < 100; i++) {
  233. NSString *episodeId = [NSString stringWithFormat:@"query_ep_%d", i];
  234. [self.storage saveProgressWithEpisodeId:episodeId dramaId:@"query_drama" position:i * 1000 duration:120000 isCompleted:NO synced:NO completion:nil];
  235. }
  236. // 测试查询性能
  237. [self measureBlock:^{
  238. NSArray *progresses = [self.storage getProgressByDramaId:@"query_drama"];
  239. XCTAssertGreaterThan(progresses.count, 0);
  240. }];
  241. }
  242. @end