ZlibTests.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * Copyright 2020, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import NIOCore
  17. import XCTest
  18. @testable import GRPC
  19. class ZlibTests: GRPCTestCase {
  20. var allocator = ByteBufferAllocator()
  21. var inputSize = 4096
  22. func makeBytes(count: Int) -> [UInt8] {
  23. return (0 ..< count).map { _ in
  24. UInt8.random(in: UInt8(ascii: "a") ... UInt8(ascii: "z"))
  25. }
  26. }
  27. @discardableResult
  28. func doCompressAndDecompress(
  29. of bytes: [UInt8],
  30. format: Zlib.CompressionFormat,
  31. initialInflateBufferSize: Int? = nil
  32. ) throws -> Int {
  33. var data = self.allocator.buffer(capacity: 0)
  34. data.writeBytes(bytes)
  35. // Compress it.
  36. let deflate = Zlib.Deflate(format: format)
  37. var compressed = self.allocator.buffer(capacity: 0)
  38. let compressedBytesWritten = try deflate.deflate(&data, into: &compressed)
  39. // Did we write the right number of bytes?
  40. XCTAssertEqual(compressedBytesWritten, compressed.readableBytes)
  41. // Decompress it.
  42. let inflate = Zlib.Inflate(format: format, limit: .absolute(bytes.count * 2))
  43. var decompressed = self.allocator.buffer(capacity: initialInflateBufferSize ?? self.inputSize)
  44. let decompressedBytesWritten = try inflate.inflate(&compressed, into: &decompressed)
  45. // Did we write the right number of bytes?
  46. XCTAssertEqual(decompressedBytesWritten, decompressed.readableBytes)
  47. // Did we get back to where we started?
  48. XCTAssertEqual(decompressed.readBytes(length: decompressed.readableBytes), bytes)
  49. return compressedBytesWritten
  50. }
  51. func testCompressionAndDecompressionOfASCIIBytes() throws {
  52. let bytes = self.makeBytes(count: self.inputSize)
  53. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  54. try self.doCompressAndDecompress(of: bytes, format: format)
  55. }
  56. }
  57. func testCompressionAndDecompressionOfZeros() throws {
  58. // This test makes sure the decompressor is capable of increasing the output buffer size a
  59. // number of times.
  60. let bytes: [UInt8] = Array(repeating: 0, count: self.inputSize)
  61. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  62. let compressedSize = try self.doCompressAndDecompress(of: bytes, format: format)
  63. // Is the compressed size significantly smaller than the input size?
  64. XCTAssertLessThan(compressedSize, bytes.count / 4)
  65. }
  66. }
  67. func testCompressionAndDecompressionOfHardToCompressData() throws {
  68. let bytes: [UInt8] = (0 ..< self.inputSize).map { _ in
  69. UInt8.random(in: UInt8.min ... UInt8.max)
  70. }
  71. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  72. // Is the compressed size larger than the input size?
  73. let compressedSize = try self.doCompressAndDecompress(of: bytes, format: format)
  74. XCTAssertGreaterThan(compressedSize, bytes.count)
  75. }
  76. }
  77. func testDecompressionAutomaticallyResizesOutputBuffer() throws {
  78. let bytes = self.makeBytes(count: self.inputSize)
  79. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  80. try self.doCompressAndDecompress(of: bytes, format: format, initialInflateBufferSize: 0)
  81. }
  82. }
  83. func testCompressionAndDecompressionWithResets() throws {
  84. // Generate some input.
  85. let byteArrays = (0 ..< 5).map { _ in
  86. self.makeBytes(count: self.inputSize)
  87. }
  88. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  89. let deflate = Zlib.Deflate(format: format)
  90. let inflate = Zlib.Inflate(format: format, limit: .absolute(self.inputSize * 2))
  91. for bytes in byteArrays {
  92. var data = self.allocator.buffer(capacity: 0)
  93. data.writeBytes(bytes)
  94. // Compress it.
  95. var compressed = self.allocator.buffer(capacity: 0)
  96. let compressedBytesWritten = try deflate.deflate(&data, into: &compressed)
  97. deflate.reset()
  98. // Did we write the right number of bytes?
  99. XCTAssertEqual(compressedBytesWritten, compressed.readableBytes)
  100. // Decompress it.
  101. var decompressed = self.allocator.buffer(capacity: self.inputSize)
  102. let decompressedBytesWritten = try inflate.inflate(&compressed, into: &decompressed)
  103. inflate.reset()
  104. // Did we write the right number of bytes?
  105. XCTAssertEqual(decompressedBytesWritten, decompressed.readableBytes)
  106. // Did we get back to where we started?
  107. XCTAssertEqual(decompressed.readBytes(length: decompressed.readableBytes), bytes)
  108. }
  109. }
  110. }
  111. func testDecompressThrowsOnGibberish() throws {
  112. let bytes = self.makeBytes(count: self.inputSize)
  113. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  114. var buffer = self.allocator.buffer(capacity: bytes.count)
  115. buffer.writeBytes(bytes)
  116. let inflate = Zlib.Inflate(format: format, limit: .ratio(1))
  117. var output = self.allocator.buffer(capacity: 0)
  118. XCTAssertThrowsError(try inflate.inflate(&buffer, into: &output)) { error in
  119. let withContext = error as? GRPCError.WithContext
  120. XCTAssert(withContext?.error is GRPCError.ZlibCompressionFailure)
  121. }
  122. }
  123. }
  124. func testAbsoluteDecompressionLimit() throws {
  125. let bytes = self.makeBytes(count: self.inputSize)
  126. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  127. var data = self.allocator.buffer(capacity: 0)
  128. data.writeBytes(bytes)
  129. // Compress it.
  130. let deflate = Zlib.Deflate(format: format)
  131. var compressed = self.allocator.buffer(capacity: 0)
  132. let compressedBytesWritten = try deflate.deflate(&data, into: &compressed)
  133. // Did we write the right number of bytes?
  134. XCTAssertEqual(compressedBytesWritten, compressed.readableBytes)
  135. let inflate = Zlib.Inflate(format: format, limit: .absolute(compressedBytesWritten - 1))
  136. var output = self.allocator.buffer(capacity: 0)
  137. XCTAssertThrowsError(try inflate.inflate(&compressed, into: &output)) { error in
  138. let withContext = error as? GRPCError.WithContext
  139. XCTAssert(withContext?.error is GRPCError.DecompressionLimitExceeded)
  140. }
  141. }
  142. }
  143. func testRatioDecompressionLimit() throws {
  144. let bytes = self.makeBytes(count: self.inputSize)
  145. for format in [Zlib.CompressionFormat.deflate, .gzip] {
  146. var data = self.allocator.buffer(capacity: 0)
  147. data.writeBytes(bytes)
  148. // Compress it.
  149. let deflate = Zlib.Deflate(format: format)
  150. var compressed = self.allocator.buffer(capacity: 0)
  151. let compressedBytesWritten = try deflate.deflate(&data, into: &compressed)
  152. // Did we write the right number of bytes?
  153. XCTAssertEqual(compressedBytesWritten, compressed.readableBytes)
  154. let inflate = Zlib.Inflate(format: format, limit: .ratio(1))
  155. var output = self.allocator.buffer(capacity: 0)
  156. XCTAssertThrowsError(try inflate.inflate(&compressed, into: &output)) { error in
  157. let withContext = error as? GRPCError.WithContext
  158. XCTAssert(withContext?.error is GRPCError.DecompressionLimitExceeded)
  159. }
  160. }
  161. }
  162. func testAbsoluteDecompressionLimitMaximumSize() throws {
  163. let absolute: DecompressionLimit = .absolute(1234)
  164. // The compressed size is ignored here.
  165. XCTAssertEqual(absolute.maximumDecompressedSize(compressedSize: -42), 1234)
  166. }
  167. func testRatioDecompressionLimitMaximumSize() throws {
  168. let ratio: DecompressionLimit = .ratio(2)
  169. XCTAssertEqual(ratio.maximumDecompressedSize(compressedSize: 10), 20)
  170. }
  171. }