1# encoding: utf-8
2require_relative 'spec_helper'
3require_relative '../../shared/string/times'
4
5load_extension('string')
6
7describe :rb_str_new2, shared: true do
8  it "returns a new string object calling strlen on the passed C string" do
9    # Hardcoded to pass const char * = "hello\0invisible"
10    @s.send(@method, "hello\0not used").should == "hello"
11  end
12
13  it "encodes the string with ASCII_8BIT" do
14    @s.send(@method, "hello").encoding.should == Encoding::ASCII_8BIT
15  end
16end
17
18describe "C-API String function" do
19  before :each do
20    @s = CApiStringSpecs.new
21  end
22
23  class ValidTostrTest
24    def to_str
25      "ruby"
26    end
27  end
28
29  class InvalidTostrTest
30    def to_str
31      []
32    end
33  end
34
35  describe "rb_str_set_len" do
36    before :each do
37      # Make a completely new copy of the string
38      # for every example (#dup doesn't cut it).
39      @str = "abcdefghij"[0..-1]
40    end
41
42    it "reduces the size of the string" do
43      @s.rb_str_set_len(@str, 5).should == "abcde"
44    end
45
46    it "inserts a NULL byte at the length" do
47      @s.rb_str_set_len(@str, 5).should == "abcde"
48      @s.rb_str_set_len(@str, 8).should == "abcde\x00gh"
49    end
50
51    it "updates the byte size and character size" do
52      @s.rb_str_set_len(@str, 4)
53      @str.bytesize.should == 4
54      @str.size.should == 4
55      @str.should == "abcd"
56    end
57
58    it "updates the string's attributes visible in C code" do
59      @s.rb_str_set_len_RSTRING_LEN(@str, 4).should == 4
60    end
61
62    it "can reveal characters written from C with RSTRING_PTR" do
63      @s.rb_str_set_len(@str, 1)
64      @str.should == "a"
65
66      @str.force_encoding(Encoding::UTF_8)
67      @s.RSTRING_PTR_set(@str, 1, 'B'.ord)
68      @s.RSTRING_PTR_set(@str, 2, 'C'.ord)
69      @s.rb_str_set_len(@str, 3)
70
71      @str.bytesize.should == 3
72      @str.should == "aBC"
73    end
74  end
75
76  describe "rb_str_buf_new" do
77    it "returns the equivalent of an empty string" do
78      buf = @s.rb_str_buf_new(10, nil)
79      buf.should == ""
80      buf.bytesize.should == 0
81      buf.size.should == 0
82      @s.RSTRING_LEN(buf).should == 0
83    end
84
85    it "returns a string with the given capacity" do
86      buf = @s.rb_str_buf_new(256, nil)
87      @s.rb_str_capacity(buf).should == 256
88    end
89
90    it "returns a string that can be appended to" do
91      str = @s.rb_str_buf_new(10, "defg")
92      str << "abcde"
93      str.should == "abcde"
94    end
95
96    it "returns a string that can be concatenated to another string" do
97      str = @s.rb_str_buf_new(10, "defg")
98      ("abcde" + str).should == "abcde"
99    end
100
101    it "returns a string whose bytes can be accessed by RSTRING_PTR" do
102      str = @s.rb_str_buf_new(10, "abcdefghi")
103      @s.rb_str_new(str, 10).should == "abcdefghi\x00"
104    end
105
106    it "returns a string that can be modified by rb_str_set_len" do
107      str = @s.rb_str_buf_new(10, "abcdef")
108      @s.rb_str_set_len(str, 4)
109      str.should == "abcd"
110
111      @s.rb_str_set_len(str, 8)
112      str[0, 6].should == "abcd\x00f"
113      @s.RSTRING_LEN(str).should == 8
114    end
115
116    it "can be used as a general buffer and reveal characters with rb_str_set_len" do
117      str = @s.rb_str_buf_new(10, "abcdef")
118
119      @s.RSTRING_PTR_set(str, 0, 195)
120      @s.RSTRING_PTR_set(str, 1, 169)
121      @s.rb_str_set_len(str, 2)
122
123      str.force_encoding(Encoding::UTF_8)
124      str.bytesize.should == 2
125      str.size.should == 1
126      str.should == "é"
127    end
128  end
129
130  describe "rb_str_buf_new2" do
131    it "returns a new string object calling strlen on the passed C string" do
132      # Hardcoded to pass const char * = "hello\0invisible"
133      @s.rb_str_buf_new2.should == "hello"
134    end
135  end
136
137  describe "rb_str_new" do
138    it "creates a new String with ASCII-8BIT Encoding" do
139      @s.rb_str_new("", 0).encoding.should == Encoding::ASCII_8BIT
140    end
141
142    it "returns a new string object from a char buffer of len characters" do
143      @s.rb_str_new("hello", 3).should == "hel"
144    end
145
146    it "returns an empty string if len is 0" do
147      @s.rb_str_new("hello", 0).should == ""
148    end
149
150    it "copy length bytes and does not stop at the first \\0 byte" do
151      @s.rb_str_new("he\x00llo", 6).should == "he\x00llo"
152      @s.rb_str_new_native("he\x00llo", 6).should == "he\x00llo"
153    end
154
155    it "returns a string from an offset char buffer" do
156      @s.rb_str_new_offset("hello", 1, 3).should == "ell"
157    end
158  end
159
160  describe "rb_str_new2" do
161    it_behaves_like :rb_str_new2, :rb_str_new2
162  end
163
164  describe "rb_str_new_cstr" do
165    it_behaves_like :rb_str_new2, :rb_str_new_cstr
166  end
167
168  describe "rb_usascii_str_new" do
169    it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do
170      str = "abc".force_encoding("us-ascii")
171      result = @s.rb_usascii_str_new("abcdef", 3)
172      result.should == str
173      result.encoding.should == Encoding::US_ASCII
174    end
175  end
176
177  describe "rb_usascii_str_new_cstr" do
178    it "creates a new String with US-ASCII Encoding" do
179      str = "abc".force_encoding("us-ascii")
180      result = @s.rb_usascii_str_new_cstr("abc")
181      result.should == str
182      result.encoding.should == Encoding::US_ASCII
183    end
184  end
185
186  describe "rb_str_encode" do
187    it "returns a String in the destination encoding" do
188      result = @s.rb_str_encode("abc", Encoding::ISO_8859_1, 0, nil)
189      result.encoding.should == Encoding::ISO_8859_1
190    end
191
192    it "transcodes the String" do
193      result = @s.rb_str_encode("ありがとう", "euc-jp", 0, nil)
194      euc_jp = [0xa4, 0xa2, 0xa4, 0xea, 0xa4, 0xac, 0xa4, 0xc8, 0xa4, 0xa6].pack('C*').force_encoding("euc-jp")
195      result.should == euc_jp
196      result.encoding.should == Encoding::EUC_JP
197    end
198
199    it "returns a dup of the original String" do
200      a = "abc"
201      b = @s.rb_str_encode("abc", "us-ascii", 0, nil)
202      a.should_not equal(b)
203    end
204
205    it "returns a duplicate of the original when the encoding doesn't change" do
206      a = "abc"
207      b = @s.rb_str_encode("abc", Encoding::UTF_8, 0, nil)
208      a.should_not equal(b)
209    end
210
211    it "accepts encoding flags" do
212      xFF = [0xFF].pack('C').force_encoding('utf-8')
213      result = @s.rb_str_encode("a#{xFF}c", "us-ascii",
214                                Encoding::Converter::INVALID_REPLACE, nil)
215      result.should == "a?c"
216      result.encoding.should == Encoding::US_ASCII
217    end
218
219    it "accepts an encoding options Hash specifying replacement String" do
220      # Yeah, MRI aborts with rb_bug() if the options Hash is not frozen
221      options = { replace: "b" }.freeze
222      xFF = [0xFF].pack('C').force_encoding('utf-8')
223      result = @s.rb_str_encode("a#{xFF}c", "us-ascii",
224                                Encoding::Converter::INVALID_REPLACE,
225                                options)
226      result.should == "abc"
227      result.encoding.should == Encoding::US_ASCII
228    end
229  end
230
231  describe "rb_str_new3" do
232    it "returns a copy of the string" do
233      str1 = "hi"
234      str2 = @s.rb_str_new3 str1
235      str1.should == str2
236      str1.should_not equal str2
237    end
238  end
239
240  describe "rb_str_new4" do
241    it "returns the original string if it is already frozen" do
242      str1 = "hi"
243      str1.freeze
244      str2 = @s.rb_str_new4 str1
245      str1.should == str2
246      str1.should equal(str2)
247      str1.frozen?.should == true
248      str2.frozen?.should == true
249    end
250
251    it "returns a frozen copy of the string" do
252      str1 = "hi"
253      str2 = @s.rb_str_new4 str1
254      str1.should == str2
255      str1.should_not equal(str2)
256      str2.frozen?.should == true
257    end
258  end
259
260  describe "rb_str_dup" do
261    it "returns a copy of the string" do
262      str1 = "hi"
263      str2 = @s.rb_str_dup str1
264      str1.should == str2
265      str1.should_not equal str2
266    end
267  end
268
269  describe "rb_str_new5" do
270    it "returns a new string with the same class as the passed string" do
271      string_class = Class.new(String)
272      template_string = string_class.new("hello world")
273      new_string = @s.rb_str_new5(template_string, "hello world", 11)
274
275      new_string.should == "hello world"
276      new_string.class.should == string_class
277    end
278  end
279
280  describe "rb_tainted_str_new" do
281    it "creates a new tainted String" do
282      newstring = @s.rb_tainted_str_new("test", 4)
283      newstring.should == "test"
284      newstring.tainted?.should be_true
285    end
286  end
287
288  describe "rb_tainted_str_new2" do
289    it "creates a new tainted String" do
290      newstring = @s.rb_tainted_str_new2("test")
291      newstring.should == "test"
292      newstring.tainted?.should be_true
293    end
294  end
295
296  describe "rb_str_append" do
297    it "appends a string to another string" do
298      @s.rb_str_append("Hello", " Goodbye").should == "Hello Goodbye"
299    end
300
301    it "raises a TypeError trying to append non-String-like object" do
302      lambda { @s.rb_str_append("Hello", 32323)}.should raise_error(TypeError)
303    end
304
305    it "changes Encoding if a string is appended to an empty string" do
306      string = "パスタ".encode(Encoding::ISO_2022_JP)
307      @s.rb_str_append("", string).encoding.should == Encoding::ISO_2022_JP
308    end
309  end
310
311  describe "rb_str_plus" do
312    it "returns a new string from concatenating two other strings" do
313      @s.rb_str_plus("Hello", " Goodbye").should == "Hello Goodbye"
314    end
315  end
316
317  describe "rb_str_times" do
318    it_behaves_like :string_times, :rb_str_times, ->(str, times) { @s.rb_str_times(str, times) }
319  end
320
321  describe "rb_str_buf_cat" do
322    it "concatenates a C string to a ruby string" do
323      @s.rb_str_buf_cat("Your house is on fire").should == "Your house is on fire?"
324    end
325  end
326
327  describe "rb_str_cat" do
328    it "concatenates a C string to ruby string" do
329      @s.rb_str_cat("Your house is on fire").should == "Your house is on fire?"
330    end
331  end
332
333  describe "rb_str_cat2" do
334    it "concatenates a C string to a ruby string" do
335      @s.rb_str_cat2("Your house is on fire").should == "Your house is on fire?"
336    end
337  end
338
339  describe "rb_str_cmp" do
340    it "returns 0 if two strings are identical" do
341      @s.rb_str_cmp("ppp", "ppp").should == 0
342    end
343
344    it "returns -1 if the first string is shorter than the second" do
345      @s.rb_str_cmp("xxx", "xxxx").should == -1
346    end
347
348    it "returns -1 if the first string is lexically less than the second" do
349      @s.rb_str_cmp("xxx", "yyy").should == -1
350    end
351
352    it "returns 1 if the first string is longer than the second" do
353      @s.rb_str_cmp("xxxx", "xxx").should == 1
354    end
355
356    it "returns 1 if the first string is lexically greater than the second" do
357      @s.rb_str_cmp("yyy", "xxx").should == 1
358    end
359  end
360
361  describe "rb_str_split" do
362    it "splits strings over a splitter" do
363      @s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"]
364    end
365  end
366
367  describe "rb_str2inum" do
368    it "converts a string to a number given a base" do
369      @s.rb_str2inum("10", 10).should == 10
370      @s.rb_str2inum("A", 16).should == 10
371    end
372  end
373
374  describe "rb_cstr2inum" do
375    it "converts a C string to a Fixnum given a base" do
376      @s.rb_cstr2inum("10", 10).should == 10
377      @s.rb_cstr2inum("10", 16).should == 16
378    end
379
380    it "converts a C string to a Bignum given a base" do
381      @s.rb_cstr2inum(bignum_value.to_s, 10).should == bignum_value
382    end
383
384    it "converts a C string to a Fixnum non-strictly if base is not 0" do
385      @s.rb_cstr2inum("1234a", 10).should == 1234
386    end
387
388    it "converts a C string to a Fixnum strictly if base is 0" do
389      lambda { @s.rb_cstr2inum("1234a", 0) }.should raise_error(ArgumentError)
390    end
391  end
392
393  describe "rb_cstr_to_inum" do
394    it "converts a C string to a Fixnum given a base" do
395      @s.rb_cstr_to_inum("1234", 10, true).should == 1234
396    end
397
398    it "converts a C string to a Bignum given a base" do
399      @s.rb_cstr_to_inum(bignum_value.to_s, 10, true).should == bignum_value
400    end
401
402    it "converts a C string to a Fixnum non-strictly" do
403      @s.rb_cstr_to_inum("1234a", 10, false).should == 1234
404    end
405
406    it "converts a C string to a Fixnum strictly" do
407      lambda { @s.rb_cstr_to_inum("1234a", 10, true) }.should raise_error(ArgumentError)
408    end
409  end
410
411  describe "rb_str_subseq" do
412    it "returns a byte-indexed substring" do
413      str = "\x00\x01\x02\x03\x04".force_encoding("binary")
414      @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary")
415    end
416  end
417
418  describe "rb_str_substr" do
419    it "returns a substring" do
420      "hello".length.times do |time|
421        @s.rb_str_substr("hello", 0, time + 1).should == "hello"[0..time]
422      end
423    end
424  end
425
426  describe "rb_str_to_str" do
427    it "calls #to_str to coerce the value to a String" do
428      @s.rb_str_to_str("foo").should == "foo"
429      @s.rb_str_to_str(ValidTostrTest.new).should == "ruby"
430    end
431
432    it "raises a TypeError if coercion fails" do
433      lambda { @s.rb_str_to_str(0) }.should raise_error(TypeError)
434      lambda { @s.rb_str_to_str(InvalidTostrTest.new) }.should raise_error(TypeError)
435    end
436  end
437
438  describe "RSTRING_PTR" do
439    it "returns a pointer to the string's contents" do
440      str = "abc"
441      chars = []
442      @s.RSTRING_PTR_iterate(str) do |c|
443        chars << c
444      end
445      chars.should == [97, 98, 99]
446    end
447
448    it "allows changing the characters in the string" do
449      str = "abc"
450      @s.RSTRING_PTR_assign(str, 'A'.ord)
451      str.should == "AAA"
452    end
453
454    it "reflects changes after a rb_funcall" do
455      lamb = proc { |s| s.replace "NEW CONTENT" }
456
457      str = "beforebefore"
458
459      ret = @s.RSTRING_PTR_after_funcall(str, lamb)
460
461      str.should == "NEW CONTENT"
462      ret.should == str
463    end
464
465    it "reflects changes from native memory and from String#setbyte in bounds" do
466      str = "abc"
467      from_rstring_ptr = @s.RSTRING_PTR_after_yield(str) { str.setbyte(1, 'B'.ord) }
468      from_rstring_ptr.should == "1B2"
469      str.should == "1B2"
470    end
471
472    it "returns a pointer to the contents of encoded pointer-sized string" do
473      s = "70パク".
474        encode(Encoding::UTF_16LE).
475        force_encoding(Encoding::UTF_16LE).
476        encode(Encoding::UTF_8)
477
478      chars = []
479      @s.RSTRING_PTR_iterate(s) do |c|
480        chars << c
481      end
482      chars.should == [55, 48, 227, 131, 145, 227, 130, 175]
483    end
484  end
485
486  describe "RSTRING_LEN" do
487    it "returns the size of the string" do
488      @s.RSTRING_LEN("gumdrops").should == 8
489    end
490  end
491
492  describe "RSTRING_LENINT" do
493    it "returns the size of a string" do
494      @s.RSTRING_LENINT("silly").should == 5
495    end
496  end
497
498  describe :string_value_macro, shared: true do
499    before :each do
500      @s = CApiStringSpecs.new
501    end
502
503    it "does not call #to_str on a String" do
504      str = "genuine"
505      str.should_not_receive(:to_str)
506      @s.send(@method, str)
507    end
508
509    it "does not call #to_s on a String" do
510      str = "genuine"
511      str.should_not_receive(:to_str)
512      @s.send(@method, str)
513    end
514
515    it "calls #to_str on non-String objects" do
516      str = mock("fake")
517      str.should_receive(:to_str).and_return("wannabe")
518      @s.send(@method, str).should == "wannabe"
519    end
520
521    it "does not call #to_s on non-String objects" do
522      str = mock("fake")
523      str.should_not_receive(:to_s)
524      lambda { @s.send(@method, str) }.should raise_error(TypeError)
525    end
526  end
527
528  describe "StringValue" do
529    it_behaves_like :string_value_macro, :StringValue
530  end
531
532  describe "SafeStringValue" do
533    it "raises for tained string when $SAFE is 1" do
534      begin
535        Thread.new {
536          $SAFE = 1
537          lambda {
538            @s.SafeStringValue("str".taint)
539          }.should raise_error(SecurityError)
540        }.join
541      ensure
542        $SAFE = 0
543      end
544    end
545
546    it_behaves_like :string_value_macro, :SafeStringValue
547  end
548
549  describe "rb_str_resize" do
550    it "reduces the size of the string" do
551      str = @s.rb_str_resize("test", 2)
552      str.size.should == 2
553      @s.RSTRING_LEN(str).should == 2
554      str.should == "te"
555    end
556
557    it "updates the string's attributes visible in C code" do
558      @s.rb_str_resize_RSTRING_LEN("test", 2).should == 2
559    end
560
561    it "increases the size of the string" do
562      expected = "test".force_encoding("US-ASCII")
563      str = @s.rb_str_resize(expected.dup, 12)
564      str.size.should == 12
565      @s.RSTRING_LEN(str).should == 12
566      str[0, 4].should == expected
567    end
568  end
569
570  describe "rb_str_inspect" do
571    it "returns the equivalent of calling #inspect on the String" do
572      @s.rb_str_inspect("value").should == %["value"]
573    end
574  end
575
576  describe "rb_str_intern" do
577    it "returns a symbol created from the string" do
578      @s.rb_str_intern("symbol").should == :symbol
579    end
580
581    it "returns a symbol even if passed an empty string" do
582      @s.rb_str_intern("").should == "".to_sym
583    end
584
585    it "returns a symbol even if the passed string contains NULL characters" do
586      @s.rb_str_intern("no\0no").should == "no\0no".to_sym
587    end
588  end
589
590  describe "rb_str_freeze" do
591    it "freezes the string" do
592      s = ""
593      @s.rb_str_freeze(s).should == s
594      s.frozen?.should be_true
595    end
596  end
597
598  describe "rb_str_hash" do
599    it "hashes the string into a number" do
600      s = "hello"
601      @s.rb_str_hash(s).should be_kind_of(Integer)
602    end
603  end
604
605  describe "rb_str_update" do
606    it "splices the replacement string into the original at the given location" do
607      @s.rb_str_update("hello", 2, 3, "wuh").should == "hewuh"
608    end
609  end
610end
611
612describe "rb_str_free" do
613  # This spec only really exists to make sure the symbol
614  # is available. There is no guarantee this even does
615  # anything at all
616  it "indicates data for a string might be freed" do
617    @s.rb_str_free("xyz").should be_nil
618  end
619end
620
621describe :rb_external_str_new, shared: true do
622  it "returns a String in the default external encoding" do
623    Encoding.default_external = "UTF-8"
624    @s.send(@method, "abc").encoding.should == Encoding::UTF_8
625  end
626
627  it "returns an ASCII-8BIT encoded string if any non-ascii bytes are present and default external is US-ASCII" do
628    Encoding.default_external = "US-ASCII"
629    x80 = [0x80].pack('C')
630    @s.send(@method, "#{x80}abc").encoding.should == Encoding::ASCII_8BIT
631  end
632
633  it "returns a tainted String" do
634    @s.send(@method, "abc").tainted?.should be_true
635  end
636end
637
638describe "C-API String function" do
639  before :each do
640    @s = CApiStringSpecs.new
641    @external = Encoding.default_external
642    @internal = Encoding.default_internal
643  end
644
645  after :each do
646    Encoding.default_external = @external
647    Encoding.default_internal = @internal
648  end
649
650  describe "rb_str_length" do
651    it "returns the string's length" do
652      @s.rb_str_length("dewdrops").should == 8
653    end
654
655    it "counts characters in multi byte encodings" do
656      @s.rb_str_length("düwdrops").should == 8
657    end
658  end
659
660  describe "rb_str_equal" do
661    it "compares two same strings" do
662      s = "hello"
663      @s.rb_str_equal(s, "hello").should be_true
664    end
665
666    it "compares two different strings" do
667      s = "hello"
668      @s.rb_str_equal(s, "hella").should be_false
669    end
670  end
671
672  describe "rb_external_str_new" do
673    it_behaves_like :rb_external_str_new, :rb_external_str_new
674  end
675
676  describe "rb_external_str_new_cstr" do
677    it_behaves_like :rb_external_str_new, :rb_external_str_new_cstr
678  end
679
680  describe "rb_external_str_new_with_enc" do
681    it "returns a String in the specified encoding" do
682      s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::UTF_8)
683      s.encoding.should == Encoding::UTF_8
684    end
685
686    it "returns an ASCII-8BIT encoded String if any non-ascii bytes are present and the specified encoding is US-ASCII" do
687      x80 = [0x80].pack('C')
688      s = @s.rb_external_str_new_with_enc("#{x80}abc", 4, Encoding::US_ASCII)
689      s.encoding.should == Encoding::ASCII_8BIT
690    end
691
692
693#     it "transcodes a String to Encoding.default_internal if it is set" do
694#       Encoding.default_internal = Encoding::EUC_JP
695#
696#  -      a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8")
697#  +      a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8")
698#         s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8)
699#  -
700#  -      s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp")
701#  +      x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('ascii-8bit')
702#  +      s.should == x
703#         s.encoding.should equal(Encoding::EUC_JP)
704#     end
705
706    it "transcodes a String to Encoding.default_internal if it is set" do
707      Encoding.default_internal = Encoding::EUC_JP
708
709      a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8")
710      s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8)
711      x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('euc-jp')
712      s.should == x
713      s.encoding.should equal(Encoding::EUC_JP)
714    end
715
716    it "returns a tainted String" do
717      s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::US_ASCII)
718      s.tainted?.should be_true
719    end
720  end
721
722  describe "rb_locale_str_new" do
723    it "returns a String with 'locale' encoding" do
724      s = @s.rb_locale_str_new("abc", 3)
725      s.should == "abc".force_encoding(Encoding.find("locale"))
726      s.encoding.should equal(Encoding.find("locale"))
727    end
728  end
729
730  describe "rb_locale_str_new_cstr" do
731    it "returns a String with 'locale' encoding" do
732      s = @s.rb_locale_str_new_cstr("abc")
733      s.should == "abc".force_encoding(Encoding.find("locale"))
734      s.encoding.should equal(Encoding.find("locale"))
735    end
736  end
737
738  describe "rb_str_conv_enc" do
739    it "returns the original String when to encoding is not specified" do
740      a = "abc".force_encoding("us-ascii")
741      @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a)
742    end
743
744    it "returns the original String if a transcoding error occurs" do
745      a = [0xEE].pack('C').force_encoding("utf-8")
746      @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a)
747    end
748
749    it "returns a transcoded String" do
750      a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
751      result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP)
752      x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
753      result.should == x.force_encoding("euc-jp")
754      result.encoding.should equal(Encoding::EUC_JP)
755    end
756
757    describe "when the String encoding is equal to the destination encoding" do
758      it "returns the original String" do
759        a = "abc".force_encoding("us-ascii")
760        @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a)
761      end
762
763      it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do
764        a = "abc".encode("us-ascii")
765        @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should equal(a)
766      end
767
768      it "returns the origin String if the destination encoding is ASCII-8BIT" do
769        a = "abc".force_encoding("ascii-8bit")
770        @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::ASCII_8BIT).should equal(a)
771      end
772    end
773  end
774
775  describe "rb_str_conv_enc_opts" do
776    it "returns the original String when to encoding is not specified" do
777      a = "abc".force_encoding("us-ascii")
778      @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a)
779    end
780
781    it "returns the original String if a transcoding error occurs" do
782      a = [0xEE].pack('C').force_encoding("utf-8")
783      @s.rb_str_conv_enc_opts(a, Encoding::UTF_8,
784                              Encoding::EUC_JP, 0, nil).should equal(a)
785    end
786
787    it "returns a transcoded String" do
788      a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
789      result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil)
790      x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
791      result.should == x.force_encoding("euc-jp")
792      result.encoding.should equal(Encoding::EUC_JP)
793    end
794
795    describe "when the String encoding is equal to the destination encoding" do
796      it "returns the original String" do
797        a = "abc".force_encoding("us-ascii")
798        @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
799                                Encoding::US_ASCII, 0, nil).should equal(a)
800      end
801
802      it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do
803        a = "abc".encode("us-ascii")
804        @s.rb_str_conv_enc_opts(a, Encoding::UTF_8,
805                                Encoding::US_ASCII, 0, nil).should equal(a)
806      end
807
808      it "returns the origin String if the destination encoding is ASCII-8BIT" do
809        a = "abc".force_encoding("ascii-8bit")
810        @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
811                                Encoding::ASCII_8BIT, 0, nil).should equal(a)
812      end
813    end
814  end
815
816  describe "rb_str_export" do
817    it "returns the original String with the external encoding" do
818      Encoding.default_external = Encoding::ISO_8859_1
819      s = @s.rb_str_export("Hëllo")
820      s.encoding.should equal(Encoding::ISO_8859_1)
821    end
822  end
823
824  describe "rb_str_export_locale" do
825    it "returns the original String with the locale encoding" do
826      s = @s.rb_str_export_locale("abc")
827      s.should == "abc".force_encoding(Encoding.find("locale"))
828      s.encoding.should equal(Encoding.find("locale"))
829    end
830  end
831
832  describe "rb_sprintf" do
833    it "replaces the parts like sprintf" do
834      @s.rb_sprintf1("Awesome %s is replaced", "string").should == "Awesome string is replaced"
835      @s.rb_sprintf1("%s", "TestFoobarTest").should == "TestFoobarTest"
836    end
837
838    it "accepts multiple arguments" do
839      s = "Awesome %s is here with %s"
840      @s.rb_sprintf2(s, "string", "content").should == "Awesome string is here with content"
841    end
842  end
843
844  describe "rb_vsprintf" do
845    it "returns a formatted String from a variable number of arguments" do
846      s = @s.rb_vsprintf("%s, %d, %.2f", "abc", 42, 2.7);
847      s.should == "abc, 42, 2.70"
848    end
849  end
850
851  describe "rb_String" do
852    it "returns the passed argument if it is a string" do
853      @s.rb_String("a").should == "a"
854    end
855
856    it "tries to convert the passed argument to a string by calling #to_str first" do
857      @s.rb_String(ValidTostrTest.new).should == "ruby"
858    end
859
860    it "raises a TypeError if #to_str does not return a string" do
861      lambda { @s.rb_String(InvalidTostrTest.new) }.should raise_error(TypeError)
862    end
863
864    it "tries to convert the passed argument to a string by calling #to_s" do
865      @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}'
866    end
867  end
868
869  describe "rb_string_value_cstr" do
870    it "returns a non-null pointer for a simple string" do
871      @s.rb_string_value_cstr("Hello").should == true
872    end
873
874    it "returns a non-null pointer for a UTF-16 string" do
875      @s.rb_string_value_cstr("Hello".encode('UTF-16BE')).should == true
876    end
877
878    it "raises an error if a string contains a null" do
879      lambda { @s.rb_string_value_cstr("Hello\0 with a null.") }.should raise_error(ArgumentError)
880    end
881
882    it "raises an error if a UTF-16 string contains a null" do
883      lambda { @s.rb_string_value_cstr("Hello\0 with a null.".encode('UTF-16BE')) }.should raise_error(ArgumentError)
884    end
885
886  end
887end
888