PageRenderTime 360ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/MongoDB.Bson.Tests/IO/MultiChunkBufferTests.cs

http://github.com/mongodb/mongo-csharp-driver
C# | 1089 lines | 852 code | 221 blank | 16 comment | 1 complexity | 1d91b7c2683f3598a302c0cbb9c4d8da MD5 | raw file
Possible License(s): Apache-2.0
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Reflection;
  19. using FluentAssertions;
  20. using MongoDB.Bson.IO;
  21. using MongoDB.Bson.TestHelpers;
  22. using MongoDB.Bson.TestHelpers.XunitExtensions;
  23. using Moq;
  24. using Xunit;
  25. namespace MongoDB.Bson.Tests.IO
  26. {
  27. public class MultiChunkBufferTests
  28. {
  29. [Theory]
  30. [InlineData(0, 0, 0, 1)]
  31. [InlineData(1, 1, 0, 2)]
  32. [InlineData(2, 1, 1, 1)]
  33. [InlineData(3, 2, 0, 3)]
  34. [InlineData(4, 2, 1, 2)]
  35. [InlineData(5, 2, 2, 1)]
  36. [InlineData(6, 2, 3, 0)]
  37. public void AccessBackingBytes_should_return_expected_result(int position, int expectedChunkIndex, int expectedOffset, int expectedCount)
  38. {
  39. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  40. var subject = CreateSubject(chunks);
  41. var result = subject.AccessBackingBytes(position);
  42. result.Array.Should().BeSameAs(chunks[expectedChunkIndex]);
  43. result.Offset.Should().Be(expectedOffset);
  44. result.Count.Should().Be(expectedCount);
  45. }
  46. [Fact]
  47. public void AccessBackingBytes_should_return_expected_result_when_there_are_zero_chunks()
  48. {
  49. var mockChunkSource = new Mock<IBsonChunkSource>();
  50. var subject = new MultiChunkBuffer(mockChunkSource.Object);
  51. var result = subject.AccessBackingBytes(0);
  52. result.Array.Should().HaveCount(0);
  53. result.Offset.Should().Be(0);
  54. result.Count.Should().Be(0);
  55. }
  56. [Theory]
  57. [ParameterAttributeData]
  58. public void AccessBackingBytes_should_throw_when_position_is_invalid(
  59. [Values(-1, 3)]
  60. int position)
  61. {
  62. var subject = CreateSubject(2);
  63. Action action = () => subject.AccessBackingBytes(position);
  64. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  65. }
  66. [Fact]
  67. public void AccessBackingBytes_should_throw_when_subject_is_disposed()
  68. {
  69. var subject = CreateDisposedSubject();
  70. Action action = () => subject.AccessBackingBytes(0);
  71. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  72. }
  73. [Theory]
  74. [InlineData(false, 2)]
  75. [InlineData(true, 1)]
  76. public void Capacity_get_should_return_expected_result(bool isReadOnly, int expectedResult)
  77. {
  78. var subject = CreateSubject(2, 1, isReadOnly);
  79. var result = subject.Capacity;
  80. result.Should().Be(expectedResult);
  81. }
  82. [Fact]
  83. public void Capacity_get_should_throw_when_subject_is_disposed()
  84. {
  85. var subject = CreateDisposedSubject();
  86. Action action = () => { var _ = subject.Capacity; };
  87. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  88. }
  89. [Theory]
  90. [ParameterAttributeData]
  91. public void ChunkSource_get_should_return_expected_result(
  92. [Values(false, true)]
  93. bool disposed)
  94. {
  95. var mockChunkSource = new Mock<IBsonChunkSource>();
  96. var subject = new MultiChunkBuffer(mockChunkSource.Object);
  97. if (disposed)
  98. {
  99. subject.Dispose();
  100. }
  101. var result = subject.ChunkSource;
  102. result.Should().BeSameAs(mockChunkSource.Object);
  103. }
  104. [Theory]
  105. [InlineData(0, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  106. [InlineData(0, 1, new byte[] { 0, 2, 3, 4, 5, 6 })]
  107. [InlineData(0, 2, new byte[] { 0, 0, 3, 4, 5, 6 })]
  108. [InlineData(0, 3, new byte[] { 0, 0, 0, 4, 5, 6 })]
  109. [InlineData(0, 4, new byte[] { 0, 0, 0, 0, 5, 6 })]
  110. [InlineData(0, 5, new byte[] { 0, 0, 0, 0, 0, 6 })]
  111. [InlineData(0, 6, new byte[] { 0, 0, 0, 0, 0, 0 })]
  112. [InlineData(1, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  113. [InlineData(1, 1, new byte[] { 1, 0, 3, 4, 5, 6 })]
  114. [InlineData(1, 2, new byte[] { 1, 0, 0, 4, 5, 6 })]
  115. [InlineData(1, 3, new byte[] { 1, 0, 0, 0, 5, 6 })]
  116. [InlineData(1, 4, new byte[] { 1, 0, 0, 0, 0, 6 })]
  117. [InlineData(1, 5, new byte[] { 1, 0, 0, 0, 0, 0 })]
  118. [InlineData(2, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  119. [InlineData(2, 1, new byte[] { 1, 2, 0, 4, 5, 6 })]
  120. [InlineData(2, 2, new byte[] { 1, 2, 0, 0, 5, 6 })]
  121. [InlineData(2, 3, new byte[] { 1, 2, 0, 0, 0, 6 })]
  122. [InlineData(2, 4, new byte[] { 1, 2, 0, 0, 0, 0 })]
  123. [InlineData(3, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  124. [InlineData(3, 1, new byte[] { 1, 2, 3, 0, 5, 6 })]
  125. [InlineData(3, 2, new byte[] { 1, 2, 3, 0, 0, 6 })]
  126. [InlineData(3, 3, new byte[] { 1, 2, 3, 0, 0, 0 })]
  127. [InlineData(4, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  128. [InlineData(4, 1, new byte[] { 1, 2, 3, 4, 0, 6 })]
  129. [InlineData(4, 2, new byte[] { 1, 2, 3, 4, 0, 0 })]
  130. [InlineData(5, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  131. [InlineData(5, 1, new byte[] { 1, 2, 3, 4, 5, 0 })]
  132. [InlineData(6, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  133. public void Clear_should_have_expected_effect(int position, int count, byte[] expectedBytes)
  134. {
  135. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  136. var subject = CreateSubject(chunks);
  137. subject.Clear(position, count);
  138. ToByteArray(subject).Should().Equal(expectedBytes);
  139. }
  140. [Theory]
  141. [InlineData(0, -1)]
  142. [InlineData(0, 3)]
  143. [InlineData(1, 2)]
  144. [InlineData(2, 1)]
  145. public void Clear_should_throw_when_count_is_invalid(int position, int count)
  146. {
  147. var subject = CreateSubject(2);
  148. Action action = () => subject.Clear(position, count);
  149. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("count");
  150. }
  151. [Theory]
  152. [ParameterAttributeData]
  153. public void Clear_should_throw_when_position_is_invalid(
  154. [Values(-1, 3)]
  155. int position)
  156. {
  157. var subject = CreateSubject(2);
  158. Action action = () => subject.Clear(position, 0);
  159. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  160. }
  161. [Fact]
  162. public void Clear_should_throw_when_subject_is_disposed()
  163. {
  164. var subject = CreateDisposedSubject();
  165. Action action = () => subject.Clear(0, 0);
  166. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  167. }
  168. [Fact]
  169. public void Clear_should_throw_when_subject_is_read_only()
  170. {
  171. var subject = CreateSubject(isReadOnly: true);
  172. Action action = () => subject.Clear(0, 0);
  173. action.ShouldThrow<InvalidOperationException>();
  174. }
  175. [Theory]
  176. [InlineData(0, 0)]
  177. [InlineData(1, 1)]
  178. [InlineData(2, 3)]
  179. public void constructor_with_chunks_should_compute_capacity(int numberOfChunks, int expectedCapacity)
  180. {
  181. var chunkSizes = Enumerable.Range(1, numberOfChunks);
  182. var chunks = CreateChunks(chunkSizes);
  183. var subject = new MultiChunkBuffer(chunks);
  184. subject.Capacity.Should().Be(expectedCapacity);
  185. }
  186. [Theory]
  187. [InlineData(0, new int[] { 0 })]
  188. [InlineData(1, new int[] { 0, 1 })]
  189. [InlineData(2, new int[] { 0, 1, 3 })]
  190. public void constructor_with_chunks_should_compute_positions(int numberOfChunks, int[] expectedPositions)
  191. {
  192. var chunkSizes = Enumerable.Range(1, numberOfChunks);
  193. var chunks = CreateChunks(chunkSizes);
  194. var subject = new MultiChunkBuffer(chunks);
  195. var reflector = new Reflector(subject);
  196. reflector._positions.Should().Equal(expectedPositions);
  197. }
  198. [Fact]
  199. public void constructor_with_chunks_should_default_isReadOnly_to_false()
  200. {
  201. var chunks = Enumerable.Empty<IBsonChunk>();
  202. var subject = new MultiChunkBuffer(chunks);
  203. subject.IsReadOnly.Should().BeFalse();
  204. }
  205. [Theory]
  206. [InlineData(0)]
  207. [InlineData(1)]
  208. [InlineData(2)]
  209. public void constructor_with_chunks_should_default_length_to_capacity(int numberOfChunks)
  210. {
  211. var chunkSizes = Enumerable.Range(1, numberOfChunks);
  212. var chunks = CreateChunks(chunkSizes);
  213. var subject = new MultiChunkBuffer(chunks);
  214. subject.Length.Should().Be(subject.Capacity);
  215. }
  216. [Fact]
  217. public void constructor_with_chunks_should_initialize_subject()
  218. {
  219. var chunks = Enumerable.Empty<IBsonChunk>();
  220. var subject = new MultiChunkBuffer(chunks, 0, false);
  221. var reflector = new Reflector(subject);
  222. subject.Capacity.Should().Be(0);
  223. subject.ChunkSource.Should().BeNull();
  224. subject.IsReadOnly.Should().BeFalse();
  225. subject.Length.Should().Be(0);
  226. reflector._chunks.Should().HaveCount(0);
  227. reflector._disposed.Should().BeFalse();
  228. reflector._positions.Should().Equal(new[] { 0 });
  229. }
  230. [Fact]
  231. public void constructor_with_chunks_should_throw_when_chunks_is_null()
  232. {
  233. IEnumerable<IBsonChunk> chunks = null;
  234. Action action = () => new MultiChunkBuffer(chunks);
  235. action.ShouldThrow<ArgumentNullException>().And.ParamName.Should().Be("chunks");
  236. }
  237. [Theory]
  238. [ParameterAttributeData]
  239. public void constructor_with_chunks_should_throw_when_length_is_invalid(
  240. [Values(-1, 4)]
  241. int length)
  242. {
  243. var chunkSizes = new[] { 1, 2 };
  244. var chunks = CreateChunks(chunkSizes);
  245. Action action = () => new MultiChunkBuffer(chunks, length);
  246. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("length");
  247. }
  248. [Fact]
  249. public void constructor_with_chunkSource_should_initialize_subject()
  250. {
  251. var mockChunkSource = new Mock<IBsonChunkSource>();
  252. var subject = new MultiChunkBuffer(mockChunkSource.Object);
  253. var reflector = new Reflector(subject);
  254. subject.Capacity.Should().Be(0);
  255. subject.ChunkSource.Should().BeSameAs(mockChunkSource.Object);
  256. subject.IsReadOnly.Should().BeFalse();
  257. subject.Length.Should().Be(0);
  258. reflector._chunks.Should().HaveCount(0);
  259. reflector._disposed.Should().BeFalse();
  260. reflector._positions.Should().Equal(new[] { 0 });
  261. }
  262. [Fact]
  263. public void constructor_with_chunkSource_should_throw_when_chunkSource_is_null()
  264. {
  265. IBsonChunkSource chunkSource = null;
  266. Action action = () => new MultiChunkBuffer(chunkSource);
  267. action.ShouldThrow<ArgumentNullException>().And.ParamName.Should().Be("chunkSource");
  268. }
  269. [Fact]
  270. public void Dispose_can_be_called_multiple_times()
  271. {
  272. var subject = CreateSubject();
  273. subject.Dispose();
  274. subject.Dispose();
  275. }
  276. [Theory]
  277. [ParameterAttributeData]
  278. public void Dispose_should_dispose_chunks(
  279. [Values(0, 1, 2, 3)]
  280. int numberOfChunks)
  281. {
  282. var chunks = Enumerable.Range(1, numberOfChunks).Select(_ => new Mock<IBsonChunk>().Object).ToList();
  283. var subject = new MultiChunkBuffer(chunks);
  284. subject.Dispose();
  285. foreach (var chunk in chunks)
  286. {
  287. var mockChunk = Mock.Get(chunk);
  288. mockChunk.Verify(c => c.Dispose(), Times.Once);
  289. }
  290. }
  291. [Fact]
  292. public void Dispose_should_dispose_subject()
  293. {
  294. var subject = CreateSubject();
  295. subject.Dispose();
  296. var reflector = new Reflector(subject);
  297. reflector._disposed.Should().BeTrue();
  298. }
  299. [Theory]
  300. [InlineData(0, new int[] { })]
  301. [InlineData(1, new int[] { 1 })]
  302. [InlineData(2, new int[] { 1, 2 })]
  303. [InlineData(3, new int[] { 1, 2 })]
  304. [InlineData(4, new int[] { 1, 2, 3 })]
  305. [InlineData(5, new int[] { 1, 2, 3 })]
  306. [InlineData(6, new int[] { 1, 2, 3 })]
  307. [InlineData(7, new int[] { 1, 2, 3, 4 })]
  308. public void EnsureCapacity_should_have_expected_effect(int minimumCapacity, int[] expectedChunkSizes)
  309. {
  310. var mockChunkSource = new Mock<IBsonChunkSource>();
  311. var subject = new MultiChunkBuffer(mockChunkSource.Object);
  312. var chunkSize = 1;
  313. mockChunkSource.Setup(s => s.GetChunk(It.IsAny<int>())).Returns(() => new ByteArrayChunk(chunkSize++));
  314. subject.EnsureCapacity(minimumCapacity);
  315. var reflector = new Reflector(subject);
  316. subject.Capacity.Should().BeGreaterOrEqualTo(minimumCapacity);
  317. reflector._chunks.Select(c => c.Bytes.Count).Should().Equal(expectedChunkSizes);
  318. }
  319. [Fact]
  320. public void EnsureCapacity_should_throw_when_chunkSource_is_null()
  321. {
  322. var subject = CreateSubject(0);
  323. Action action = () => subject.EnsureCapacity(1);
  324. action.ShouldThrow<InvalidOperationException>();
  325. }
  326. [Fact]
  327. public void EnsureCapacity_should_throw_when_minimumCapacity_is_invalid()
  328. {
  329. var subject = CreateSubject(2);
  330. Action action = () => subject.EnsureCapacity(-1);
  331. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("minimumCapacity");
  332. }
  333. [Fact]
  334. public void EnsureCapacity_should_throw_when_subject_is_disposed()
  335. {
  336. var subject = CreateDisposedSubject();
  337. Action action = () => subject.EnsureCapacity(1);
  338. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  339. }
  340. [Fact]
  341. public void EnsureCapacity_should_throw_when_subject_is_read_only()
  342. {
  343. var subject = CreateSubject(isReadOnly: true);
  344. Action action = () => subject.EnsureCapacity(1);
  345. action.ShouldThrow<InvalidOperationException>();
  346. }
  347. [SkippableFact]
  348. public void ExpandCapacity_should_throw_when_expanded_capacity_exceeds_2GB()
  349. {
  350. RequireProcess.Check().Bits(64);
  351. using (var subject = new MultiChunkBuffer(BsonChunkPool.Default))
  352. {
  353. subject.EnsureCapacity(int.MaxValue - 128 * 1024 * 1024);
  354. Action action = () => subject.EnsureCapacity(int.MaxValue); // indirectly calls private ExpandCapacity method
  355. action.ShouldThrow<InvalidOperationException>();
  356. }
  357. }
  358. [Theory]
  359. [InlineData(0, 1)]
  360. [InlineData(1, 2)]
  361. [InlineData(2, 3)]
  362. [InlineData(3, 4)]
  363. [InlineData(4, 5)]
  364. [InlineData(5, 6)]
  365. public void GetByte_should_return_expected_result(int position, byte expectedResult)
  366. {
  367. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  368. var subject = CreateSubject(chunks);
  369. var result = subject.GetByte(position);
  370. result.Should().Be(expectedResult);
  371. }
  372. [Theory]
  373. [ParameterAttributeData]
  374. public void GetByte_should_throw_when_position_is_invalid(
  375. [Values(-1, 3)]
  376. int position)
  377. {
  378. var subject = CreateSubject(2);
  379. Action action = () => subject.GetByte(position);
  380. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  381. }
  382. [Fact]
  383. public void GetByte_should_throw_when_subject_is_disposed()
  384. {
  385. var subject = CreateDisposedSubject();
  386. Action action = () => subject.GetByte(0);
  387. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  388. }
  389. [Theory]
  390. [InlineData(1, new byte[] { 0, 1, 2, 0 })]
  391. [InlineData(2, new byte[] { 0, 0, 1, 2 })]
  392. public void GetBytes_should_have_expected_effect_for_offset(int offset, byte[] expectedBytes)
  393. {
  394. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 } };
  395. var subject = CreateSubject(chunks);
  396. var destination = new byte[4];
  397. subject.GetBytes(0, destination, offset, 2);
  398. destination.Should().Equal(expectedBytes);
  399. }
  400. [Theory]
  401. [InlineData(0, 0, new byte[] { })]
  402. [InlineData(0, 1, new byte[] { 1 })]
  403. [InlineData(0, 2, new byte[] { 1, 2 })]
  404. [InlineData(0, 3, new byte[] { 1, 2, 3 })]
  405. [InlineData(0, 4, new byte[] { 1, 2, 3, 4 })]
  406. [InlineData(0, 5, new byte[] { 1, 2, 3, 4, 5 })]
  407. [InlineData(0, 6, new byte[] { 1, 2, 3, 4, 5, 6 })]
  408. [InlineData(1, 0, new byte[] { })]
  409. [InlineData(1, 1, new byte[] { 2 })]
  410. [InlineData(1, 2, new byte[] { 2, 3 })]
  411. [InlineData(1, 3, new byte[] { 2, 3, 4 })]
  412. [InlineData(1, 4, new byte[] { 2, 3, 4, 5 })]
  413. [InlineData(1, 5, new byte[] { 2, 3, 4, 5, 6 })]
  414. [InlineData(2, 0, new byte[] { })]
  415. [InlineData(2, 1, new byte[] { 3 })]
  416. [InlineData(2, 2, new byte[] { 3, 4 })]
  417. [InlineData(2, 3, new byte[] { 3, 4, 5 })]
  418. [InlineData(2, 4, new byte[] { 3, 4, 5, 6 })]
  419. [InlineData(3, 0, new byte[] { })]
  420. [InlineData(3, 1, new byte[] { 4 })]
  421. [InlineData(3, 2, new byte[] { 4, 5 })]
  422. [InlineData(3, 3, new byte[] { 4, 5, 6 })]
  423. [InlineData(4, 0, new byte[] { })]
  424. [InlineData(4, 1, new byte[] { 5 })]
  425. [InlineData(4, 2, new byte[] { 5, 6 })]
  426. [InlineData(5, 0, new byte[] { })]
  427. [InlineData(5, 1, new byte[] { 6 })]
  428. [InlineData(6, 0, new byte[] { })]
  429. public void GetBytes_should_expected_effect_for_position_and_count(int position, int count, byte[] expectedBytes)
  430. {
  431. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  432. var subject = CreateSubject(chunks);
  433. var destination = new byte[count];
  434. subject.GetBytes(position, destination, 0, count);
  435. destination.Should().Equal(expectedBytes);
  436. }
  437. [Theory]
  438. [InlineData(0, -1)]
  439. [InlineData(0, 3)]
  440. [InlineData(1, 2)]
  441. [InlineData(2, 1)]
  442. public void GetBytes_should_throw_when_count_is_invalid_for_buffer(int position, int count)
  443. {
  444. var subject = CreateSubject(2);
  445. var destination = new byte[3];
  446. Action action = () => subject.GetBytes(position, destination, 0, count);
  447. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("count");
  448. }
  449. [Theory]
  450. [InlineData(0, -1)]
  451. [InlineData(0, 3)]
  452. [InlineData(1, 2)]
  453. [InlineData(2, 1)]
  454. public void GetBytes_should_throw_when_count_is_invalid_for_destination(int offset, int count)
  455. {
  456. var subject = CreateSubject(3);
  457. var destination = new byte[2];
  458. Action action = () => subject.GetBytes(0, destination, offset, count);
  459. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("count");
  460. }
  461. [Fact]
  462. public void GetBytes_should_throw_when_destination_is_null()
  463. {
  464. var subject = CreateSubject();
  465. Action action = () => subject.GetBytes(0, null, 0, 0);
  466. action.ShouldThrow<ArgumentNullException>().And.ParamName.Should().Be("destination");
  467. }
  468. [Theory]
  469. [ParameterAttributeData]
  470. public void GetBytes_should_throw_when_offset_is_invalid(
  471. [Values(-1, 3)]
  472. int offset)
  473. {
  474. var subject = CreateSubject(4);
  475. var destination = new byte[2];
  476. Action action = () => subject.GetBytes(0, destination, offset, 0);
  477. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("offset");
  478. }
  479. [Theory]
  480. [ParameterAttributeData]
  481. public void GetBytes_should_throw_when_position_is_invalid(
  482. [Values(-1, 3)]
  483. int position)
  484. {
  485. var subject = CreateSubject(2);
  486. var destination = new byte[0];
  487. Action action = () => subject.GetBytes(position, destination, 0, 0);
  488. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  489. }
  490. [Fact]
  491. public void GetBytes_should_throw_when_subject_is_disposed()
  492. {
  493. var subject = CreateDisposedSubject();
  494. var destination = new byte[0];
  495. Action action = () => subject.GetBytes(0, destination, 0, 0);
  496. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  497. }
  498. [Theory]
  499. [InlineData(0, 0, new byte[] { })]
  500. [InlineData(0, 1, new byte[] { 1 })]
  501. [InlineData(0, 2, new byte[] { 1, 2 })]
  502. [InlineData(0, 3, new byte[] { 1, 2, 3 })]
  503. [InlineData(0, 4, new byte[] { 1, 2, 3, 4 })]
  504. [InlineData(0, 5, new byte[] { 1, 2, 3, 4, 5 })]
  505. [InlineData(0, 6, new byte[] { 1, 2, 3, 4, 5, 6 })]
  506. [InlineData(1, 0, new byte[] { })]
  507. [InlineData(1, 1, new byte[] { 2 })]
  508. [InlineData(1, 2, new byte[] { 2, 3 })]
  509. [InlineData(1, 3, new byte[] { 2, 3, 4 })]
  510. [InlineData(1, 4, new byte[] { 2, 3, 4, 5 })]
  511. [InlineData(1, 5, new byte[] { 2, 3, 4, 5, 6 })]
  512. [InlineData(2, 0, new byte[] { })]
  513. [InlineData(2, 1, new byte[] { 3 })]
  514. [InlineData(2, 2, new byte[] { 3, 4 })]
  515. [InlineData(2, 3, new byte[] { 3, 4, 5 })]
  516. [InlineData(2, 4, new byte[] { 3, 4, 5, 6 })]
  517. [InlineData(3, 0, new byte[] { })]
  518. [InlineData(3, 1, new byte[] { 4 })]
  519. [InlineData(3, 2, new byte[] { 4, 5 })]
  520. [InlineData(3, 3, new byte[] { 4, 5, 6 })]
  521. [InlineData(4, 0, new byte[] { })]
  522. [InlineData(4, 1, new byte[] { 5 })]
  523. [InlineData(4, 2, new byte[] { 5, 6 })]
  524. [InlineData(5, 0, new byte[] { })]
  525. [InlineData(5, 1, new byte[] { 6 })]
  526. [InlineData(6, 0, new byte[] { })]
  527. public void GetSlice_should_return_expected_result(int position, int length, byte[] expectedBytes)
  528. {
  529. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  530. var subject = CreateSubject(chunks, isReadOnly: true);
  531. var result = subject.GetSlice(position, length);
  532. ToByteArray(result).Should().Equal(expectedBytes);
  533. }
  534. [Fact]
  535. public void GetSlice_should_return_slice_that_does_not_dispose_subject_when_slice_is_disposed()
  536. {
  537. var chunks = new[] { new byte[] { 1, 2, 3 } };
  538. var subject = CreateSubject(chunks, isReadOnly: true);
  539. var slice = subject.GetSlice(1, 1);
  540. slice.Dispose();
  541. subject.GetByte(1).Should().Be(2);
  542. }
  543. [Fact]
  544. public void GetSlice_should_return_slice_that_is_not_disposed_when_subject_is_disposed()
  545. {
  546. var chunks = new[] { new byte[] { 1, 2, 3 } };
  547. var subject = CreateSubject(chunks, isReadOnly: true);
  548. var slice = subject.GetSlice(1, 1);
  549. subject.Dispose();
  550. slice.GetByte(0).Should().Be(2);
  551. }
  552. [Theory]
  553. [InlineData(0, -1)]
  554. [InlineData(0, 3)]
  555. [InlineData(1, 2)]
  556. [InlineData(2, 1)]
  557. public void GetSlice_should_throw_when_length_is_invalid(int position, int length)
  558. {
  559. var subject = CreateSubject(2, isReadOnly: true);
  560. Action action = () => subject.GetSlice(position, length);
  561. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("length");
  562. }
  563. [Theory]
  564. [ParameterAttributeData]
  565. public void GetSlice_should_throw_when_position_is_invalid(
  566. [Values(-1, 3)]
  567. int position)
  568. {
  569. var subject = CreateSubject(2, isReadOnly: true);
  570. Action action = () => subject.GetSlice(position, 0);
  571. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  572. }
  573. [Fact]
  574. public void GetSlice_should_throw_when_subject_is_disposed()
  575. {
  576. var subject = CreateDisposedSubject();
  577. Action action = () => subject.GetSlice(0, 0);
  578. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  579. }
  580. [Fact]
  581. public void GetSlice_should_throw_when_subject_is_not_read_only()
  582. {
  583. var subject = CreateSubject(isReadOnly: false);
  584. Action action = () => subject.GetSlice(0, 0);
  585. action.ShouldThrow<InvalidOperationException>();
  586. }
  587. [Theory]
  588. [ParameterAttributeData]
  589. public void IsReadOnly_get_should_return_expected_result(
  590. [Values(false, true)]
  591. bool isReadOnly)
  592. {
  593. var subject = CreateSubject(0, isReadOnly: isReadOnly);
  594. var result = subject.IsReadOnly;
  595. result.Should().Be(isReadOnly);
  596. }
  597. [Fact]
  598. public void IsReadOnly_get_should_throw_when_subject_is_disposed()
  599. {
  600. var subject = CreateDisposedSubject();
  601. Action action = () => { var _ = subject.IsReadOnly; };
  602. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  603. }
  604. [Theory]
  605. [ParameterAttributeData]
  606. public void Length_get_should_return_expected_result(
  607. [Values(1, 2)]
  608. int length)
  609. {
  610. var subject = CreateSubject(2, length);
  611. var result = subject.Length;
  612. result.Should().Be(length);
  613. }
  614. [Fact]
  615. public void Length_get_should_throw_when_subject_is_disposed()
  616. {
  617. var subject = CreateDisposedSubject();
  618. Action action = () => { var _ = subject.Length; };
  619. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  620. }
  621. [Theory]
  622. [ParameterAttributeData]
  623. public void Length_set_should_have_expected_effect(
  624. [Values(1, 2)]
  625. int length)
  626. {
  627. var subject = CreateSubject(2, 0);
  628. subject.Length = length;
  629. subject.Length.Should().Be(length);
  630. }
  631. [Fact]
  632. public void Length_set_should_throw_when_subject_is_read_only()
  633. {
  634. var subject = CreateSubject(0, 0, isReadOnly: true);
  635. Action action = () => subject.Length = 0;
  636. action.ShouldThrow<InvalidOperationException>();
  637. }
  638. [Theory]
  639. [InlineData(0, -1)]
  640. [InlineData(0, 1)]
  641. [InlineData(1, 2)]
  642. [InlineData(2, 3)]
  643. public void Length_set_should_throw_when_value_is_invalid(int size, int value)
  644. {
  645. var subject = CreateSubject(size, 0);
  646. Action action = () => subject.Length = value;
  647. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("value");
  648. }
  649. [Fact]
  650. public void Length_set_should_throw_when_subject_is_disposed()
  651. {
  652. var subject = CreateDisposedSubject();
  653. Action action = () => subject.Length = 0;
  654. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  655. }
  656. [Theory]
  657. [ParameterAttributeData]
  658. public void MakeReadOnly_should_have_expected_effect(
  659. [Values(false, true)]
  660. bool isReadOnly)
  661. {
  662. var subject = CreateSubject(isReadOnly: isReadOnly);
  663. subject.MakeReadOnly();
  664. subject.IsReadOnly.Should().BeTrue();
  665. }
  666. [Fact]
  667. public void MakeReadOnly_should_throw_when_subject_is_disposed()
  668. {
  669. var subject = CreateDisposedSubject();
  670. Action action = () => subject.MakeReadOnly();
  671. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  672. }
  673. [Theory]
  674. [InlineData(0, new byte[] { 0, 2, 3, 4, 5, 6 })]
  675. [InlineData(1, new byte[] { 1, 0, 3, 4, 5, 6 })]
  676. [InlineData(2, new byte[] { 1, 2, 0, 4, 5, 6 })]
  677. [InlineData(3, new byte[] { 1, 2, 3, 0, 5, 6 })]
  678. [InlineData(4, new byte[] { 1, 2, 3, 4, 0, 6 })]
  679. [InlineData(5, new byte[] { 1, 2, 3, 4, 5, 0 })]
  680. public void SetByte_should_have_expected_effect(int position, byte[] expectedBytes)
  681. {
  682. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  683. var subject = CreateSubject(chunks);
  684. subject.SetByte(position, 0);
  685. ToByteArray(subject).Should().Equal(expectedBytes);
  686. }
  687. [Theory]
  688. [ParameterAttributeData]
  689. public void SetByte_should_throw_when_position_is_invalid(
  690. [Values(-1, 3)]
  691. int position)
  692. {
  693. var subject = CreateSubject(2);
  694. Action action = () => subject.SetByte(position, 0);
  695. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  696. }
  697. [Fact]
  698. public void SetByte_should_throw_when_subject_is_disposed()
  699. {
  700. var subject = CreateDisposedSubject();
  701. Action action = () => subject.SetByte(0, 0);
  702. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  703. }
  704. [Fact]
  705. public void SetByte_should_throw_when_subject_is_read_only()
  706. {
  707. var subject = CreateSubject(1, isReadOnly: true);
  708. Action action = () => subject.SetByte(0, 0);
  709. action.ShouldThrow<InvalidOperationException>();
  710. }
  711. [Theory]
  712. [InlineData(0, 0, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  713. [InlineData(0, 0, 1, new byte[] { 7, 2, 3, 4, 5, 6 })]
  714. [InlineData(0, 0, 2, new byte[] { 7, 8, 3, 4, 5, 6 })]
  715. [InlineData(0, 0, 3, new byte[] { 7, 8, 9, 4, 5, 6 })]
  716. [InlineData(0, 1, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  717. [InlineData(0, 1, 1, new byte[] { 8, 2, 3, 4, 5, 6 })]
  718. [InlineData(0, 1, 2, new byte[] { 8, 9, 3, 4, 5, 6 })]
  719. [InlineData(1, 0, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  720. [InlineData(1, 0, 1, new byte[] { 1, 7, 3, 4, 5, 6 })]
  721. [InlineData(1, 0, 2, new byte[] { 1, 7, 8, 4, 5, 6 })]
  722. [InlineData(1, 0, 3, new byte[] { 1, 7, 8, 9, 5, 6 })]
  723. [InlineData(1, 1, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  724. [InlineData(1, 1, 1, new byte[] { 1, 8, 3, 4, 5, 6 })]
  725. [InlineData(1, 1, 2, new byte[] { 1, 8, 9, 4, 5, 6 })]
  726. [InlineData(2, 0, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  727. [InlineData(2, 0, 1, new byte[] { 1, 2, 7, 4, 5, 6 })]
  728. [InlineData(2, 0, 2, new byte[] { 1, 2, 7, 8, 5, 6 })]
  729. [InlineData(2, 0, 3, new byte[] { 1, 2, 7, 8, 9, 6 })]
  730. [InlineData(2, 1, 0, new byte[] { 1, 2, 3, 4, 5, 6 })]
  731. [InlineData(2, 1, 1, new byte[] { 1, 2, 8, 4, 5, 6 })]
  732. [InlineData(2, 1, 2, new byte[] { 1, 2, 8, 9, 5, 6 })]
  733. public void SetBytes_should_have_expected_effect(int position, int offset, int count, byte[] expectedBytes)
  734. {
  735. var chunks = new[] { new byte[] { 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5, 6 } };
  736. var subject = CreateSubject(chunks);
  737. var source = new byte[] { 7, 8, 9 };
  738. subject.SetBytes(position, source, offset, count);
  739. ToByteArray(subject).Should().Equal(expectedBytes);
  740. }
  741. [Theory]
  742. [InlineData(0, -1)]
  743. [InlineData(1, 2)]
  744. [InlineData(2, 1)]
  745. public void SetBytes_should_throw_when_count_is_invalid_for_buffer(int position, int count)
  746. {
  747. var subject = CreateSubject(2);
  748. var source = new byte[4];
  749. Action action = () => subject.SetBytes(position, source, 0, count);
  750. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("count");
  751. }
  752. [Theory]
  753. [InlineData(0, -1)]
  754. [InlineData(1, 2)]
  755. [InlineData(2, 1)]
  756. public void SetBytes_should_throw_when_count_is_invalid_for_source(int offset, int count)
  757. {
  758. var subject = CreateSubject(2);
  759. var source = new byte[2];
  760. Action action = () => subject.SetBytes(0, source, offset, count);
  761. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("count");
  762. }
  763. [Theory]
  764. [ParameterAttributeData]
  765. public void SetBytes_should_throw_when_offset_is_invalid(
  766. [Values(-1, 3)]
  767. int offset)
  768. {
  769. var subject = CreateSubject(0);
  770. var source = new byte[2];
  771. Action action = () => subject.SetBytes(0, source, offset, 0);
  772. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("offset");
  773. }
  774. [Theory]
  775. [ParameterAttributeData]
  776. public void SetBytes_should_throw_when_position_is_invalid(
  777. [Values(-1, 3)]
  778. int position)
  779. {
  780. var subject = CreateSubject(2);
  781. var source = new byte[0];
  782. Action action = () => subject.SetBytes(position, source, 0, 0);
  783. action.ShouldThrow<ArgumentOutOfRangeException>().And.ParamName.Should().Be("position");
  784. }
  785. [Fact]
  786. public void SetBytes_should_throw_when_source_is_null()
  787. {
  788. var subject = CreateSubject();
  789. Action action = () => subject.SetBytes(0, null, 0, 0);
  790. action.ShouldThrow<ArgumentNullException>().And.ParamName.Should().Be("source");
  791. }
  792. [Fact]
  793. public void SetBytes_should_throw_when_subject_is_disposed()
  794. {
  795. var subject = CreateDisposedSubject();
  796. var source = new byte[0];
  797. Action action = () => subject.SetBytes(0, source, 0, 0);
  798. action.ShouldThrow<ObjectDisposedException>().And.ObjectName.Should().Be("MultiChunkBuffer");
  799. }
  800. [Fact]
  801. public void SetBytes_should_throw_when_subject_is_read_only()
  802. {
  803. var subject = CreateSubject(isReadOnly: true);
  804. var source = new byte[0];
  805. Action action = () => subject.SetBytes(0, source, 0, 0);
  806. action.ShouldThrow<InvalidOperationException>();
  807. }
  808. // helper methods
  809. private IEnumerable<IBsonChunk> CreateChunks(IEnumerable<int> chunkSizes)
  810. {
  811. return chunkSizes.Select(s => new ByteArrayChunk(s));
  812. }
  813. private MultiChunkBuffer CreateDisposedSubject()
  814. {
  815. var subject = CreateSubject();
  816. subject.Dispose();
  817. return subject;
  818. }
  819. private MultiChunkBuffer CreateSubject(byte[][] chunks, int? length = null, bool isReadOnly = false)
  820. {
  821. return new MultiChunkBuffer(chunks.Select(c => new ByteArrayChunk(c)), length, isReadOnly);
  822. }
  823. private MultiChunkBuffer CreateSubject(int size = 0, int? length = null, bool isReadOnly = false)
  824. {
  825. var chunk = new ByteArrayChunk(size);
  826. return new MultiChunkBuffer(new[] { chunk }, length ?? size, isReadOnly);
  827. }
  828. private byte[] ToByteArray(IByteBuffer buffer)
  829. {
  830. var bytes = new byte[buffer.Length];
  831. buffer.GetBytes(0, bytes, 0, buffer.Length);
  832. return bytes;
  833. }
  834. // nested types
  835. private class Reflector
  836. {
  837. private readonly MultiChunkBuffer _instance;
  838. public Reflector(MultiChunkBuffer instance)
  839. {
  840. _instance = instance;
  841. }
  842. public List<IBsonChunk> _chunks
  843. {
  844. get
  845. {
  846. var field = typeof(MultiChunkBuffer).GetField("_chunks", BindingFlags.NonPublic | BindingFlags.Instance);
  847. return (List<IBsonChunk>)field.GetValue(_instance);
  848. }
  849. }
  850. public bool _disposed
  851. {
  852. get
  853. {
  854. var field = typeof(MultiChunkBuffer).GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance);
  855. return (bool)field.GetValue(_instance);
  856. }
  857. }
  858. public List<int> _positions
  859. {
  860. get
  861. {
  862. var field = typeof(MultiChunkBuffer).GetField("_positions", BindingFlags.NonPublic | BindingFlags.Instance);
  863. return (List<int>)field.GetValue(_instance);
  864. }
  865. }
  866. }
  867. }
  868. }