bson 4.1.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +106 -7
  3. data/Rakefile +86 -43
  4. data/ext/bson/{native-endian.h → bson-endian.h} +14 -107
  5. data/ext/bson/bson-native.h +135 -0
  6. data/ext/bson/bytebuf.c +133 -0
  7. data/ext/bson/endian.c +117 -0
  8. data/ext/bson/extconf.rb +8 -3
  9. data/ext/bson/init.c +364 -0
  10. data/ext/bson/libbson-utf8.c +230 -0
  11. data/ext/bson/read.c +470 -0
  12. data/ext/bson/util.c +250 -0
  13. data/ext/bson/write.c +675 -0
  14. data/lib/bson/active_support.rb +19 -0
  15. data/lib/bson/array.rb +97 -30
  16. data/lib/bson/big_decimal.rb +77 -0
  17. data/lib/bson/binary.rb +510 -70
  18. data/lib/bson/boolean.rb +15 -4
  19. data/lib/bson/code.rb +25 -12
  20. data/lib/bson/code_with_scope.rb +41 -15
  21. data/lib/bson/config.rb +3 -28
  22. data/lib/bson/date.rb +16 -4
  23. data/lib/bson/date_time.rb +6 -4
  24. data/lib/bson/db_pointer.rb +110 -0
  25. data/lib/bson/dbref.rb +154 -0
  26. data/lib/bson/decimal128/builder.rb +456 -0
  27. data/lib/bson/decimal128.rb +272 -0
  28. data/lib/bson/document.rb +177 -7
  29. data/lib/bson/environment.rb +17 -2
  30. data/lib/bson/error/bson_decode_error.rb +11 -0
  31. data/lib/bson/error/ext_json_parse_error.rb +11 -0
  32. data/lib/bson/error/illegal_key.rb +23 -0
  33. data/lib/bson/error/invalid_binary_type.rb +37 -0
  34. data/lib/bson/error/invalid_dbref_argument.rb +12 -0
  35. data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
  36. data/lib/bson/error/invalid_decimal128_range.rb +27 -0
  37. data/lib/bson/error/invalid_decimal128_string.rb +26 -0
  38. data/lib/bson/error/invalid_key.rb +24 -0
  39. data/lib/bson/error/invalid_object_id.rb +11 -0
  40. data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
  41. data/lib/bson/error/unrepresentable_precision.rb +19 -0
  42. data/lib/bson/error/unserializable_class.rb +13 -0
  43. data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
  44. data/lib/bson/error/unsupported_type.rb +11 -0
  45. data/lib/bson/error.rb +22 -0
  46. data/lib/bson/ext_json.rb +389 -0
  47. data/lib/bson/false_class.rb +6 -4
  48. data/lib/bson/float.rb +43 -7
  49. data/lib/bson/hash.rb +152 -37
  50. data/lib/bson/int32.rb +104 -6
  51. data/lib/bson/int64.rb +111 -8
  52. data/lib/bson/integer.rb +43 -9
  53. data/lib/bson/json.rb +3 -1
  54. data/lib/bson/max_key.rb +21 -10
  55. data/lib/bson/min_key.rb +21 -10
  56. data/lib/bson/nil_class.rb +7 -3
  57. data/lib/bson/object.rb +25 -17
  58. data/lib/bson/object_id.rb +98 -113
  59. data/lib/bson/open_struct.rb +59 -0
  60. data/lib/bson/regexp.rb +129 -56
  61. data/lib/bson/registry.rb +7 -10
  62. data/lib/bson/specialized.rb +8 -4
  63. data/lib/bson/string.rb +12 -32
  64. data/lib/bson/symbol.rb +107 -11
  65. data/lib/bson/time.rb +68 -7
  66. data/lib/bson/time_with_zone.rb +67 -0
  67. data/lib/bson/timestamp.rb +50 -10
  68. data/lib/bson/true_class.rb +6 -4
  69. data/lib/bson/undefined.rb +28 -2
  70. data/lib/bson/vector.rb +44 -0
  71. data/lib/bson/version.rb +6 -14
  72. data/lib/bson.rb +22 -12
  73. data/spec/README.md +14 -0
  74. data/spec/bson/array_spec.rb +38 -62
  75. data/spec/bson/big_decimal_spec.rb +328 -0
  76. data/spec/bson/binary_spec.rb +199 -53
  77. data/spec/bson/binary_uuid_spec.rb +190 -0
  78. data/spec/bson/boolean_spec.rb +2 -1
  79. data/spec/bson/byte_buffer_read_spec.rb +198 -0
  80. data/spec/bson/byte_buffer_spec.rb +122 -381
  81. data/spec/bson/byte_buffer_write_spec.rb +855 -0
  82. data/spec/bson/code_spec.rb +6 -4
  83. data/spec/bson/code_with_scope_spec.rb +6 -4
  84. data/spec/bson/config_spec.rb +1 -35
  85. data/spec/bson/date_spec.rb +2 -1
  86. data/spec/bson/date_time_spec.rb +55 -1
  87. data/spec/bson/dbref_legacy_spec.rb +186 -0
  88. data/spec/bson/dbref_spec.rb +487 -0
  89. data/spec/bson/decimal128_spec.rb +1840 -0
  90. data/spec/bson/document_as_spec.rb +61 -0
  91. data/spec/bson/document_spec.rb +205 -32
  92. data/spec/bson/ext_json_parse_spec.rb +346 -0
  93. data/spec/bson/false_class_spec.rb +9 -1
  94. data/spec/bson/float_spec.rb +42 -1
  95. data/spec/bson/hash_as_spec.rb +58 -0
  96. data/spec/bson/hash_spec.rb +318 -66
  97. data/spec/bson/int32_spec.rb +248 -1
  98. data/spec/bson/int64_spec.rb +308 -1
  99. data/spec/bson/integer_spec.rb +61 -3
  100. data/spec/bson/json_spec.rb +2 -1
  101. data/spec/bson/max_key_spec.rb +6 -4
  102. data/spec/bson/min_key_spec.rb +6 -4
  103. data/spec/bson/nil_class_spec.rb +2 -1
  104. data/spec/bson/object_id_spec.rb +95 -5
  105. data/spec/bson/object_spec.rb +3 -2
  106. data/spec/bson/open_struct_spec.rb +87 -0
  107. data/spec/bson/raw_spec.rb +594 -0
  108. data/spec/bson/regexp_spec.rb +61 -8
  109. data/spec/bson/registry_spec.rb +3 -2
  110. data/spec/bson/string_spec.rb +26 -33
  111. data/spec/bson/symbol_raw_spec.rb +70 -0
  112. data/spec/bson/symbol_spec.rb +77 -20
  113. data/spec/bson/time_spec.rb +206 -2
  114. data/spec/bson/time_with_zone_spec.rb +69 -0
  115. data/spec/bson/timestamp_spec.rb +58 -2
  116. data/spec/bson/true_class_spec.rb +9 -1
  117. data/spec/bson/undefined_spec.rb +28 -1
  118. data/spec/bson/vector_spec.rb +33 -0
  119. data/spec/bson_spec.rb +2 -1
  120. data/spec/runners/binary_vector.rb +78 -0
  121. data/spec/runners/common_driver.rb +348 -0
  122. data/spec/runners/corpus.rb +191 -0
  123. data/spec/runners/corpus_legacy.rb +248 -0
  124. data/spec/shared/LICENSE +20 -0
  125. data/spec/shared/bin/get-mongodb-download-url +17 -0
  126. data/spec/shared/bin/s3-copy +45 -0
  127. data/spec/shared/bin/s3-upload +69 -0
  128. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  129. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  130. data/spec/shared/lib/mrss/constraints.rb +378 -0
  131. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  132. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  133. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  134. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  135. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  136. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  137. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  138. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  139. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  140. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  141. data/spec/shared/lib/mrss/utils.rb +37 -0
  142. data/spec/shared/lib/tasks/candidate.rake +64 -0
  143. data/spec/shared/share/Dockerfile.erb +251 -0
  144. data/spec/shared/share/haproxy-1.conf +16 -0
  145. data/spec/shared/share/haproxy-2.conf +17 -0
  146. data/spec/shared/shlib/config.sh +27 -0
  147. data/spec/shared/shlib/distro.sh +84 -0
  148. data/spec/shared/shlib/server.sh +423 -0
  149. data/spec/shared/shlib/set_env.sh +110 -0
  150. data/spec/spec_helper.rb +61 -1
  151. data/spec/spec_tests/binary_vector_spec.rb +82 -0
  152. data/spec/spec_tests/common_driver_spec.rb +84 -0
  153. data/spec/spec_tests/corpus_legacy_spec.rb +72 -0
  154. data/spec/spec_tests/corpus_spec.rb +134 -0
  155. data/spec/spec_tests/data/binary_vector/README.md +61 -0
  156. data/spec/spec_tests/data/binary_vector/float32.json +65 -0
  157. data/spec/spec_tests/data/binary_vector/int8.json +57 -0
  158. data/spec/spec_tests/data/binary_vector/packed_bit.json +83 -0
  159. data/spec/spec_tests/data/corpus/README.md +15 -0
  160. data/spec/spec_tests/data/corpus/array.json +49 -0
  161. data/spec/spec_tests/data/corpus/binary.json +153 -0
  162. data/spec/spec_tests/data/corpus/boolean.json +27 -0
  163. data/spec/spec_tests/data/corpus/code.json +67 -0
  164. data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
  165. data/spec/spec_tests/data/corpus/datetime.json +42 -0
  166. data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
  167. data/spec/spec_tests/data/corpus/dbref.json +51 -0
  168. data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
  169. data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
  170. data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
  171. data/spec/spec_tests/data/corpus/decimal128-4.json +165 -0
  172. data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
  173. data/spec/spec_tests/data/corpus/decimal128-6.json +131 -0
  174. data/spec/spec_tests/data/corpus/decimal128-7.json +327 -0
  175. data/spec/spec_tests/data/corpus/document.json +60 -0
  176. data/spec/spec_tests/data/corpus/double.json +87 -0
  177. data/spec/spec_tests/data/corpus/int32.json +43 -0
  178. data/spec/spec_tests/data/corpus/int64.json +43 -0
  179. data/spec/spec_tests/data/corpus/maxkey.json +12 -0
  180. data/spec/spec_tests/data/corpus/minkey.json +12 -0
  181. data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
  182. data/spec/spec_tests/data/corpus/multi-type.json +11 -0
  183. data/spec/spec_tests/data/corpus/null.json +12 -0
  184. data/spec/spec_tests/data/corpus/oid.json +28 -0
  185. data/spec/spec_tests/data/corpus/regex.json +65 -0
  186. data/spec/spec_tests/data/corpus/string.json +72 -0
  187. data/spec/spec_tests/data/corpus/symbol.json +80 -0
  188. data/spec/spec_tests/data/corpus/timestamp.json +34 -0
  189. data/spec/spec_tests/data/corpus/top.json +262 -0
  190. data/spec/spec_tests/data/corpus/undefined.json +15 -0
  191. data/spec/spec_tests/data/corpus_legacy/array.json +49 -0
  192. data/spec/spec_tests/data/corpus_legacy/binary.json +69 -0
  193. data/spec/spec_tests/data/corpus_legacy/boolean.json +27 -0
  194. data/spec/spec_tests/data/corpus_legacy/code.json +67 -0
  195. data/spec/spec_tests/data/corpus_legacy/code_w_scope.json +78 -0
  196. data/spec/spec_tests/data/corpus_legacy/document.json +36 -0
  197. data/spec/spec_tests/data/corpus_legacy/double.json +69 -0
  198. data/spec/spec_tests/data/corpus_legacy/failures/datetime.json +31 -0
  199. data/spec/spec_tests/data/corpus_legacy/failures/dbpointer.json +42 -0
  200. data/spec/spec_tests/data/corpus_legacy/failures/int64.json +38 -0
  201. data/spec/spec_tests/data/corpus_legacy/failures/symbol.json +62 -0
  202. data/spec/spec_tests/data/corpus_legacy/int32.json +38 -0
  203. data/spec/spec_tests/data/corpus_legacy/maxkey.json +12 -0
  204. data/spec/spec_tests/data/corpus_legacy/minkey.json +12 -0
  205. data/spec/spec_tests/data/corpus_legacy/null.json +12 -0
  206. data/spec/spec_tests/data/corpus_legacy/oid.json +28 -0
  207. data/spec/spec_tests/data/corpus_legacy/regex.json +37 -0
  208. data/spec/spec_tests/data/corpus_legacy/string.json +67 -0
  209. data/spec/spec_tests/data/corpus_legacy/timestamp.json +18 -0
  210. data/spec/spec_tests/data/corpus_legacy/top.json +62 -0
  211. data/spec/spec_tests/data/corpus_legacy/undefined.json +13 -0
  212. data/spec/spec_tests/data/decimal128/decimal128-1.json +363 -0
  213. data/spec/spec_tests/data/decimal128/decimal128-2.json +793 -0
  214. data/spec/spec_tests/data/decimal128/decimal128-3.json +1771 -0
  215. data/spec/spec_tests/data/decimal128/decimal128-4.json +165 -0
  216. data/spec/spec_tests/data/decimal128/decimal128-5.json +402 -0
  217. data/spec/spec_tests/data/decimal128/decimal128-6.json +131 -0
  218. data/spec/spec_tests/data/decimal128/decimal128-7.json +327 -0
  219. data/spec/support/shared_examples.rb +32 -11
  220. data/spec/support/spec_config.rb +17 -0
  221. data/spec/support/utils.rb +58 -0
  222. metadata +284 -45
  223. checksums.yaml.gz.sig +0 -3
  224. data/ext/bson/native.c +0 -722
  225. data.tar.gz.sig +0 -0
  226. metadata.gz.sig +0 -0
@@ -1,4 +1,5 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # rubocop:todo all
2
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
4
5
  # you may not use this file except in compliance with the License.
@@ -26,4 +27,30 @@ describe BSON::Undefined do
26
27
  it_behaves_like "a serializable bson element"
27
28
  it_behaves_like "a deserializable bson element"
28
29
  end
30
+
31
+ describe "#as_json" do
32
+
33
+ let(:object) do
34
+ described_class.new
35
+ end
36
+
37
+ it "returns nil" do
38
+ expect(object.as_json).to eq(nil)
39
+ end
40
+
41
+ it_behaves_like 'a JSON serializable object'
42
+ end
43
+
44
+ describe "#as_extended_json" do
45
+
46
+ let(:object) do
47
+ described_class.new
48
+ end
49
+
50
+ it "returns the binary data plus type" do
51
+ expect(object.as_extended_json).to eq({ "$undefined" => true })
52
+ end
53
+
54
+ it_behaves_like 'an Extended JSON serializable object'
55
+ end
29
56
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025-present MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'spec_helper'
18
+
19
+ describe BSON::Vector do
20
+ it 'behaves like an Array' do
21
+ expect(described_class.new([ 1, 2, 3 ], :int8)).to be_a(Array)
22
+ end
23
+
24
+ describe '#initialize' do
25
+ context 'when padding is not provided' do
26
+ let(:vector) { described_class.new([ 1, 2, 3 ], :int8) }
27
+
28
+ it 'sets the padding to 0' do
29
+ expect(vector.padding).to eq(0)
30
+ end
31
+ end
32
+ end
33
+ end
data/spec/bson_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # rubocop:todo all
2
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
3
  #
3
4
  # Licensed under the Apache License, Version 2.0 (the "License");
4
5
  # you may not use this file except in compliance with the License.
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'runners/common_driver'
4
+
5
+ module BSON
6
+ module BinaryVector
7
+ class Spec < CommonDriver::Spec
8
+ def initialize(file)
9
+ super
10
+ @valid = @invalid = nil
11
+ end
12
+
13
+ def tests
14
+ @spec['tests'].collect do |test|
15
+ BSON::BinaryVector::Test.new(self, test)
16
+ end
17
+ end
18
+
19
+ def valid_tests
20
+ tests.select(&:valid?)
21
+ end
22
+
23
+ def invalid_tests
24
+ tests.reject(&:valid?)
25
+ end
26
+ end
27
+
28
+ class Test
29
+ attr_reader :canonical_bson, :description, :dtype, :padding, :vector
30
+
31
+ def initialize(spec, test)
32
+ @spec = spec
33
+ @description = test['description']
34
+ @valid = test['valid']
35
+ @vector = ExtJSON.parse_obj(test['vector'])
36
+ @dtype_hex = test['dtype_hex']
37
+ @dtype_alias = test['dtype_alias']
38
+ @dtype = @dtype_alias.downcase.to_sym
39
+ @padding = test['padding']
40
+ @canonical_bson = test['canonical_bson']
41
+ end
42
+
43
+ def valid?
44
+ @valid
45
+ end
46
+
47
+ def document_from_canonical_bson
48
+ bson_bytes = decode_hex(@canonical_bson)
49
+ buffer = BSON::ByteBuffer.new(bson_bytes)
50
+ BSON::Document.from_bson(buffer)
51
+ end
52
+
53
+ def canonical_bson_from_document(use_vector_type: false, validate_vector_data: false)
54
+ args = if use_vector_type
55
+ [ BSON::Vector.new(@vector, @dtype, @padding) ]
56
+ else
57
+ [ @vector, @dtype, @padding ]
58
+ end
59
+ {
60
+ @spec.test_key => BSON::Binary.from_vector(
61
+ *args,
62
+ validate_vector_data: validate_vector_data
63
+ ),
64
+ }.to_bson.to_s
65
+ end
66
+
67
+ def bson
68
+ decode_hex(@canonical_bson)
69
+ end
70
+
71
+ private
72
+
73
+ def decode_hex(obj)
74
+ [ obj ].pack('H*')
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,348 @@
1
+ # rubocop:todo all
2
+ # Copyright (C) 2016-2020 MongoDB Inc.
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
+ require 'json'
17
+ require 'bigdecimal'
18
+
19
+ module BSON
20
+ module CommonDriver
21
+
22
+ # Represents a Common Driver specification test.
23
+ #
24
+ # @since 4.2.0
25
+ class Spec
26
+
27
+ # The spec description.
28
+ #
29
+ # @return [ String ] The spec description.
30
+ #
31
+ # @since 4.2.0
32
+ attr_reader :description
33
+
34
+ # The document key of the object to test.
35
+ #
36
+ # @return [ String ] The document key.
37
+ #
38
+ # @since 4.2.0
39
+ attr_reader :test_key
40
+
41
+ # Instantiate the new spec.
42
+ #
43
+ # @example Create the spec.
44
+ # Spec.new(file)
45
+ #
46
+ # @param [ String ] file The name of the yaml file.
47
+ #
48
+ # @since 4.2.0
49
+ def initialize(file)
50
+ @spec = ::JSON.parse(File.read(file))
51
+ @valid = @spec['valid'] || []
52
+ @invalid = @spec['parseErrors'] || []
53
+ @description = @spec['description']
54
+ @test_key = @spec['test_key']
55
+ end
56
+
57
+ # Get a list of tests that don't raise exceptions.
58
+ #
59
+ # @example Get the list of valid tests.
60
+ # spec.valid_tests
61
+ #
62
+ # @return [ Array<BSON::CommonDriver::Test> ] The list of valid Tests.
63
+ #
64
+ # @since 4.2.0
65
+ def valid_tests
66
+ @valid_tests ||=
67
+ @valid.collect do |test|
68
+ BSON::CommonDriver::Test.new(self, test)
69
+ end
70
+ end
71
+
72
+ # Get a list of tests that raise exceptions.
73
+ #
74
+ # @example Get the list of invalid tests.
75
+ # spec.invalid_tests
76
+ #
77
+ # @return [ Array<BSON::CommonDriver::Test> ] The list of invalid Tests.
78
+ #
79
+ # @since 4.2.0
80
+ def invalid_tests
81
+ @invalid_tests ||=
82
+ @invalid.collect do |test|
83
+ BSON::CommonDriver::Test.new(self, test)
84
+ end
85
+ end
86
+
87
+ # The class of the bson object to test.
88
+ #
89
+ # @example Get the class of the object to test.
90
+ # spec.klass
91
+ #
92
+ # @return [ Class ] The object class.
93
+ #
94
+ # @since 4.2.0
95
+ def klass
96
+ @klass ||= BSON.const_get(description)
97
+ end
98
+ end
99
+
100
+ # Represents a single CommonDriver test.
101
+ #
102
+ # @since 4.2.0
103
+ class Test
104
+
105
+ # The test description.
106
+ #
107
+ # @return [ String ] The test description.
108
+ #
109
+ # @since 4.2.0
110
+ attr_reader :description
111
+
112
+ # The test subject.
113
+ #
114
+ # @return [ String ] The test subject.
115
+ #
116
+ # @since 4.2.0
117
+ attr_reader :subject
118
+
119
+ # The string to use to create a Decimal128.
120
+ #
121
+ # @return [ String ] The string to use in creating a Decimal128 object.
122
+ #
123
+ # @since 4.2.0
124
+ attr_reader :string
125
+
126
+ # The expected string representation of the Decimal128 object.
127
+ #
128
+ # @return [ String ] The object as a string.
129
+ #
130
+ # @since 4.2.0
131
+ attr_reader :match_string
132
+
133
+ # The json representation of the object.
134
+ #
135
+ # @return [ Hash ] The json representation of the object.
136
+ #
137
+ # @since 4.2.0
138
+ attr_reader :ext_json
139
+
140
+ # Instantiate the new Test.
141
+ #
142
+ # @example Create the test.
143
+ # Test.new(test)
144
+ #
145
+ # @param [ CommonDriver::Spec ] spec The test specification.
146
+ # @param [ Hash ] test The test specification.
147
+ #
148
+ # @since 4.2.0
149
+ def initialize(spec, test)
150
+ @spec = spec
151
+ @description = test['description']
152
+ @string = test['string']
153
+ @match_string = test['match_string']
154
+ @ext_json = ::JSON.parse(test['extjson']) if test['extjson']
155
+ @from_ext_json = test['from_extjson'].nil? ? true : test['from_extjson']
156
+ @to_ext_json = test['to_extjson'].nil? ? true : test['to_extjson']
157
+ @subject = test['subject']
158
+ @test_key = spec.test_key
159
+ end
160
+
161
+ # Get the reencoded document in hex format.
162
+ #
163
+ # @example Get the reencoded document as hex.
164
+ # test.reencoded_hex
165
+ #
166
+ # @return [ String ] The reencoded document in hex format.
167
+ #
168
+ # @since 4.2.0
169
+ def reencoded_hex
170
+ decoded_document.to_bson.to_s.unpack1("H*").upcase
171
+ end
172
+
173
+ # The object tested.
174
+ #
175
+ # @example Get the object for this test.
176
+ # test.object
177
+ #
178
+ # @return [ BSON::Object ] The object.
179
+ #
180
+ # @since 4.2.0
181
+ def object
182
+ @object ||= decoded_document[@test_key]
183
+ end
184
+
185
+ # The object as json, in a document with the test key.
186
+ #
187
+ # @example Get a document with the object at the test key.
188
+ # test.document_as_extended_json
189
+ #
190
+ # @return [ Hash ] The extended_json representation of document.
191
+ #
192
+ # @since 4.2.0
193
+ def document_as_extended_json
194
+ { @test_key => object.as_extended_json }
195
+ end
196
+
197
+ # Use the string in the extended json to instantiate the bson object.
198
+ #
199
+ # @example Get a bson object from the string in the extended json.
200
+ # test.from_json
201
+ #
202
+ # @return [ BSON::Object ] The BSON object.
203
+ #
204
+ # @since 4.2.0
205
+ def from_json_string
206
+ klass.from_string(@ext_json[@test_key][klass::EXTENDED_JSON_KEY])
207
+ end
208
+
209
+ # Create an object from the given test string.
210
+ #
211
+ # @example
212
+ # test.parse_string
213
+ #
214
+ # @return [ BSON::Object ] The object.
215
+ #
216
+ # @since 4.2.0
217
+ def parse_string
218
+ klass.from_string(string)
219
+ end
220
+
221
+ # Attempt to create an object from an invalid string.
222
+ #
223
+ # @example
224
+ # test.parse_invalid_string
225
+ #
226
+ # @raise [ Error ] Parsing an invalid string will raise an error.
227
+ #
228
+ # @since 4.2.0
229
+ def parse_invalid_string
230
+ klass.from_string(subject)
231
+ end
232
+
233
+ # The class of the object being tested.
234
+ #
235
+ # @example
236
+ # test.klass
237
+ #
238
+ # @return [ Class ] The object class.
239
+ #
240
+ # @since 4.2.0
241
+ def klass
242
+ @spec.klass
243
+ end
244
+
245
+ # The error class of a parse error.
246
+ #
247
+ # @example
248
+ # test.parse_error
249
+ #
250
+ # @return [ Class ] The parse error class.
251
+ #
252
+ # @since 4.2.0
253
+ def parse_error
254
+ klass::InvalidRange
255
+ end
256
+
257
+ # Whether the object can be instantiated from extended json.
258
+ #
259
+ # @example Check if an object can be instantiated from the extended json.
260
+ # test.from_ex_json?
261
+ #
262
+ # @return [ true, false ] If the object can be instantiated from
263
+ # the provided extended json.
264
+ #
265
+ # @since 4.2.0
266
+ def from_ext_json?
267
+ @ext_json && @from_ext_json
268
+ end
269
+
270
+ # Whether the object can be represented as extended json.
271
+ #
272
+ # @example Check if an object can be represented as extended json.
273
+ # test.to_ext_json?
274
+ #
275
+ # @return [ true, false ] If the object can be represented as
276
+ # extended json.
277
+ #
278
+ # @since 4.2.0
279
+ def to_ext_json?
280
+ @ext_json && @to_ext_json
281
+ end
282
+
283
+ # Whether the object can be instantiated from a string.
284
+ #
285
+ # @example Check if an object can be instantiated from a string.
286
+ # test.from_string?
287
+ #
288
+ # @return [ true, false ] If the object can be instantiated from a string.
289
+ #
290
+ # @since 4.2.0
291
+ def from_string?
292
+ @string && @from_ext_json
293
+ end
294
+
295
+ # The expected string representation of the test object.
296
+ #
297
+ # @example Get the expected String representation of the test object.
298
+ # test.expected_to_string
299
+ #
300
+ # @return [ String ] The expected string representation.
301
+ #
302
+ # @since 4.2.0
303
+ def expected_to_string
304
+ match_string || string
305
+ end
306
+
307
+ # The Ruby class to which this bson object can be converted via a helper.
308
+ #
309
+ # @example Get the native type to which this object can be converted.
310
+ # test.native_type
311
+ #
312
+ # @return [ Class ] The Ruby native type.
313
+ #
314
+ # @since 4.2.0
315
+ def native_type
316
+ klass::NATIVE_TYPE
317
+ end
318
+
319
+ # Get the object converted to an instance of the native Ruby type.
320
+ #
321
+ # @example Get a native Ruby instance.
322
+ # test.native_type_conversion
323
+ #
324
+ # @return [ Object ] An instance of the Ruby native type.
325
+ #
326
+ # @since 4.2.0
327
+ def native_type_conversion
328
+ object.send("to_#{to_snake_case(native_type)}")
329
+ end
330
+
331
+ private
332
+
333
+ def to_snake_case(string)
334
+ string.to_s.gsub(/::/, '/').
335
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
336
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
337
+ tr("-", "_").
338
+ downcase
339
+ end
340
+
341
+ def decoded_document
342
+ @document ||= (data = [ @subject ].pack('H*')
343
+ buffer = BSON::ByteBuffer.new(data)
344
+ BSON::Document.from_bson(buffer, mode: :bson))
345
+ end
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,191 @@
1
+ # rubocop:todo all
2
+ # Copyright (C) 2016-2020 MongoDB Inc.
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
+ require 'json'
17
+ require 'forwardable'
18
+
19
+ module BSON
20
+ module Corpus
21
+
22
+ # Represents a test from the driver BSON Corpus.
23
+ #
24
+ # @since 4.2.0
25
+ class Spec
26
+
27
+ # The spec description.
28
+ #
29
+ # @return [ String ] The spec description.
30
+ #
31
+ # @since 4.2.0
32
+ attr_reader :description
33
+
34
+ # The document key of the object to test.
35
+ #
36
+ # @return [ String ] The document key.
37
+ #
38
+ # @since 4.2.0
39
+ attr_reader :test_key
40
+
41
+ # Instantiate the new spec.
42
+ #
43
+ # @example Create the spec.
44
+ # Spec.new(file)
45
+ #
46
+ # @param [ String ] file The name of the json file.
47
+ #
48
+ # @since 4.2.0
49
+ def initialize(file)
50
+ @spec = ::JSON.parse(File.read(file).force_encoding('utf-8'))
51
+ end
52
+
53
+ def description
54
+ @spec['description']
55
+ end
56
+
57
+ def test_key
58
+ @spec['test_key']
59
+ end
60
+
61
+ def bson_type
62
+ @bson_type ||= @spec['bson_type'].to_i(16).chr
63
+ end
64
+
65
+ def valid_tests
66
+ @valid_tests ||=
67
+ @spec['valid']&.map do |test_spec|
68
+ ValidTest.new(self, test_spec)
69
+ end
70
+ end
71
+
72
+ def decode_error_tests
73
+ @decode_error_tests ||=
74
+ @spec['decodeErrors']&.map do |test_spec|
75
+ DecodeErrorTest.new(self, test_spec)
76
+ end
77
+ end
78
+
79
+ def parse_error_tests
80
+ @parse_error_tests ||=
81
+ @spec['parseErrors']&.map do |test_spec|
82
+ ParseErrorTest.new(self, test_spec)
83
+ end
84
+ end
85
+
86
+ # The class of the bson object to test.
87
+ #
88
+ # @example Get the class of the object to test.
89
+ # spec.klass
90
+ #
91
+ # @return [ Class ] The object class.
92
+ #
93
+ # @since 4.2.0
94
+ def klass
95
+ @klass ||= BSON.const_get(description)
96
+ end
97
+ end
98
+
99
+ class TestBase
100
+
101
+ private
102
+
103
+ def decode_hex(obj)
104
+ [ obj ].pack('H*')
105
+ end
106
+ end
107
+
108
+ # Represents a single BSON Corpus test.
109
+ #
110
+ # @since 4.2.0
111
+ class ValidTest < TestBase
112
+ extend Forwardable
113
+
114
+ # Instantiate the new Test.
115
+ #
116
+ # @example Create the test.
117
+ # Test.new(test)
118
+ #
119
+ # @param [ Corpus::Spec ] spec The test specification.
120
+ # @param [ Hash ] test The test specification.
121
+ #
122
+ # @since 4.2.0
123
+ def initialize(spec, test_params)
124
+ @spec = spec
125
+ test_params = test_params.dup
126
+ %w(
127
+ description canonical_extjson relaxed_extjson
128
+ degenerate_extjson converted_extjson
129
+ lossy
130
+ ).each do |key|
131
+ instance_variable_set("@#{key}", test_params.delete(key))
132
+ end
133
+ %w(
134
+ canonical_bson degenerate_bson converted_bson
135
+ lossy
136
+ ).each do |key|
137
+ if test_params.key?(key)
138
+ instance_variable_set("@#{key}", decode_hex(test_params.delete(key)))
139
+ end
140
+ end
141
+ unless test_params.empty?
142
+ raise "Test params has unprocessed keys: #{test_params}"
143
+ end
144
+ end
145
+
146
+ def_delegators :@spec, :test_key
147
+
148
+ attr_reader :description,
149
+ :canonical_bson,
150
+ :degenerate_bson,
151
+ :converted_bson,
152
+ :canonical_extjson,
153
+ :relaxed_extjson,
154
+ :degenerate_extjson,
155
+ :converted_extjson
156
+
157
+ def lossy?
158
+ !!@lossy
159
+ end
160
+
161
+ def canonical_extjson_doc
162
+ ::JSON.parse(canonical_extjson)
163
+ end
164
+
165
+ def relaxed_extjson_doc
166
+ relaxed_extjson && ::JSON.parse(relaxed_extjson)
167
+ end
168
+ end
169
+
170
+ class DecodeErrorTest < TestBase
171
+ def initialize(spec, test_params)
172
+ @spec = spec
173
+ @description = test_params['description']
174
+ @bson = decode_hex(test_params['bson'])
175
+ end
176
+
177
+ attr_reader :description, :bson
178
+ end
179
+
180
+ class ParseErrorTest
181
+ def initialize(spec, test_params)
182
+ @spec = spec
183
+ @description = test_params['description']
184
+ @string = test_params['string']
185
+ end
186
+
187
+ attr_reader :spec
188
+ attr_reader :description, :string
189
+ end
190
+ end
191
+ end