javaclass 0.0.2

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 (49) hide show
  1. data/Rakefile +51 -0
  2. data/Readme.txt +47 -0
  3. data/history.txt +29 -0
  4. data/lib/javaclass/access_flags.rb +44 -0
  5. data/lib/javaclass/class_magic.rb +27 -0
  6. data/lib/javaclass/class_version.rb +65 -0
  7. data/lib/javaclass/constant_pool.rb +85 -0
  8. data/lib/javaclass/constants/base.rb +31 -0
  9. data/lib/javaclass/constants/double_reference.rb +81 -0
  10. data/lib/javaclass/constants/single_reference.rb +58 -0
  11. data/lib/javaclass/constants/value.rb +114 -0
  12. data/lib/javaclass/java_class_header.rb +88 -0
  13. data/lib/javaclass/references.rb +31 -0
  14. data/lib/javaclass/string_ux.rb +39 -0
  15. data/lib/javaclass.rb +14 -0
  16. data/test/data/AccessFlagsTestAbstract.class +0 -0
  17. data/test/data/AccessFlagsTestAbstract.java +3 -0
  18. data/test/data/AccessFlagsTestFinal.class +0 -0
  19. data/test/data/AccessFlagsTestFinal.java +3 -0
  20. data/test/data/AccessFlagsTestInterface.class +0 -0
  21. data/test/data/AccessFlagsTestInterface.java +3 -0
  22. data/test/data/AccessFlagsTestPackage.class +0 -0
  23. data/test/data/AccessFlagsTestPackage.java +3 -0
  24. data/test/data/AccessFlagsTestPublic$InnerClass.class +0 -0
  25. data/test/data/AccessFlagsTestPublic.class +0 -0
  26. data/test/data/AccessFlagsTestPublic.java +7 -0
  27. data/test/data/ClassVersionTest.java +1 -0
  28. data/test/data/ClassVersionTest10.class +0 -0
  29. data/test/data/ClassVersionTest11.class +0 -0
  30. data/test/data/ClassVersionTest12.class +0 -0
  31. data/test/data/ClassVersionTest13.class +0 -0
  32. data/test/data/ClassVersionTest14.class +0 -0
  33. data/test/data/ClassVersionTest15.class +0 -0
  34. data/test/data/ClassVersionTest16.class +0 -0
  35. data/test/data/ConstantPoolTest.class +0 -0
  36. data/test/data/ConstantPoolTest.java +20 -0
  37. data/test/data/makeAccessFlagsTest.bat +9 -0
  38. data/test/data/makeClassVersionTest.bat +35 -0
  39. data/test/data/makeConstantPoolTest.bat +6 -0
  40. data/test/setup.rb +14 -0
  41. data/test/test_access_flags.rb +53 -0
  42. data/test/test_base.rb +22 -0
  43. data/test/test_class_version.rb +59 -0
  44. data/test/test_constant_pool.rb +90 -0
  45. data/test/test_java_class_header.rb +30 -0
  46. data/test/test_references.rb +29 -0
  47. data/test/test_string_ux.rb +30 -0
  48. data/test/ts_all_tests.rb +36 -0
  49. metadata +106 -0
@@ -0,0 +1,88 @@
1
+ require 'javaclass/string_ux'
2
+ require 'javaclass/class_magic'
3
+ require 'javaclass/class_version'
4
+ require 'javaclass/constant_pool'
5
+ require 'javaclass/references'
6
+ require 'javaclass/access_flags'
7
+
8
+ module JavaClass
9
+
10
+ # Provide all information of a Java class file.
11
+ # Author:: Peter Kofler
12
+ class JavaClassHeader
13
+
14
+ attr_reader :magic
15
+ attr_reader :version
16
+ attr_reader :constant_pool
17
+ attr_reader :access_flags
18
+ attr_reader :references
19
+
20
+ # Create a header with the binary _data_ from the class file.
21
+ def initialize(data)
22
+
23
+ # ClassFile {
24
+ # u4 magic;
25
+ # u2 minor_version;
26
+ # u2 major_version;
27
+ # u2 constant_pool_count;
28
+ # cp_info constant_pool[constant_pool_count-1];
29
+ # u2 access_flags;
30
+ # u2 this_class;
31
+ # u2 super_class;
32
+ # u2 interfaces_count;
33
+ # u2 interfaces[interfaces_count];
34
+ # u2 fields_count;
35
+ # field_info fields[fields_count];
36
+ # u2 methods_count;
37
+ # method_info methods[methods_count];
38
+ # u2 attributes_count;
39
+ # attribute_info attributes[attributes_count];
40
+ # }
41
+
42
+ @magic = ClassMagic.new(data)
43
+ @version = ClassVersion.new(data)
44
+
45
+ @constant_pool = ConstantPool.new(data)
46
+ pos = 8 + @constant_pool.size
47
+
48
+ @access_flags = AccessFlags.new(data, pos)
49
+ pos += 2
50
+
51
+ idx = data.u2(pos)
52
+ pos += 2
53
+ @this_class_idx = idx
54
+
55
+ @references = References.new(@constant_pool, @this_class_idx)
56
+
57
+ idx = data.u2(pos)
58
+ pos += 2
59
+ @super_class_idx = idx
60
+ end
61
+
62
+ # Return the name of this class.
63
+ def this_class
64
+ @constant_pool[@this_class_idx].to_s
65
+ end
66
+
67
+ # Return the name of the superclass of this class or +nil+.
68
+ def super_class
69
+ if @super_class_idx>0
70
+ @constant_pool[@super_class_idx].to_s
71
+ else
72
+ nil
73
+ end
74
+ end
75
+
76
+ # Return a debug output of this class that looks similar to +javap+ output.
77
+ def dump
78
+ d = []
79
+ # dump << " SourceFile: \"#{@value}\""
80
+ # dump << "Compiled from \"#{@value}\""
81
+ d += @version.dump
82
+ d += @constant_pool.dump
83
+ d
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,31 @@
1
+ require 'javaclass/constant_pool'
2
+
3
+ module JavaClass
4
+
5
+ # Container class for list of all classes, methods and fields referenced by this class.
6
+ # Author:: Peter Kofler
7
+ class References
8
+
9
+ # Create a references container with the constant _pool_ and skip references to _classidx_ which is the host class itself.
10
+ def initialize(pool, classidx)
11
+ @constant_pool = pool
12
+ @class_idx = classidx
13
+ end
14
+
15
+ # Return the constants referring to fields (Constants::ConstantField).
16
+ # If _includeown_ is +true+ then fields of this class are returned also.
17
+ def referenced_fields(includeown=false)
18
+ @constant_pool.find(ConstantPool::FIELD_TAG).find_all { |field| includeown || field.class_index != @class_idx }
19
+ end
20
+
21
+ # Return the constants referring to methods (Constants::ConstantMethod) in classes or interfaces.
22
+ # If _includeown_ is +true+ then methods of this class are returned also.
23
+ def referenced_methods(includeown=false)
24
+ @constant_pool.find(ConstantPool::METHOD_TAG, ConstantPool::INTERFACE_METHOD_TAG).find_all do |method|
25
+ includeown || method.class_index != @class_idx
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,39 @@
1
+
2
+ # Add some +unpack+ helper methods for HI-LO byte order (network byte order) contained in this string.
3
+ # Author:: Peter Kofler
4
+ class String
5
+
6
+ # Return the _index_'th element as byte.
7
+ def u1(index=0)
8
+ self[index]
9
+ end
10
+
11
+ # Return the _index_'th and the next element as unsigned word.
12
+ def u2(index=0)
13
+ self[index..index+1].unpack('n')[0]
14
+ # same as self[index]*256 + self[index+1]
15
+ end
16
+
17
+ # Return the _index_'th and the next 3 elements as unsigned dword.
18
+ def u4(index=0)
19
+ self[index..index+3].unpack('N')[0]
20
+ end
21
+
22
+ # Return the _index_'th and the next 7 elements as unsigned qword.
23
+ def u8(index=0)
24
+ u4(index) * 256**4 + u4(index+4)
25
+ end
26
+
27
+ # Return the _index_'th and the next 3 elements as single precision float.
28
+ # See:: http://steve.hollasch.net/cgindex/coding/ieeefloat.html
29
+ # See:: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/196633
30
+ def single(index=0)
31
+ self[index..index+3].unpack('g')[0]
32
+ end
33
+
34
+ # Return the _index_'th and the next 7 elements as double precision float.
35
+ def double(index=0)
36
+ self[index..index+7].unpack('G')[0]
37
+ end
38
+
39
+ end
data/lib/javaclass.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'javaclass/java_class_header'
2
+
3
+ # Parse and disassemble Java class files, similar to the +javap+ command.
4
+ # Author:: Peter Kofler
5
+ # See:: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
6
+ # See:: http://en.wikipedia.org/wiki/Class_(file_format)
7
+ module JavaClass
8
+
9
+ # Read and disassemble the given class called _name_ (full file name).
10
+ def self.parse(name)
11
+ JavaClassHeader.new(File.open(name, 'rb') {|io| io.read } )
12
+ end
13
+
14
+ end
@@ -0,0 +1,3 @@
1
+ package packagename;
2
+
3
+ public abstract class AccessFlagsTestAbstract { }
@@ -0,0 +1,3 @@
1
+ package packagename;
2
+
3
+ public final class AccessFlagsTestFinal { }
@@ -0,0 +1,3 @@
1
+ package packagename;
2
+
3
+ public interface AccessFlagsTestInterface { }
@@ -0,0 +1,3 @@
1
+ package packagename;
2
+
3
+ class AccessFlagsTestPackage { }
@@ -0,0 +1,7 @@
1
+ package packagename;
2
+
3
+ public class AccessFlagsTestPublic {
4
+
5
+ class InnerClass { }
6
+
7
+ }
@@ -0,0 +1 @@
1
+ public class ClassVersionTest { }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,20 @@
1
+ public class ConstantPoolTest implements Runnable {
2
+
3
+ // Class - it's own and Object
4
+ // Field ref (plus String)
5
+ String stringField = "String field";
6
+ // Method ref - Object ctor
7
+ // InterfaceMethod
8
+ {
9
+ ((Runnable)this).run();
10
+ }
11
+ final int intField = 42;
12
+ float floatField = 3.1415898996736E+22f;
13
+ long longField = 99999999L;
14
+ double doubleField = 3.14159265258979E-208;
15
+ // NameAndType - ctor init
16
+ // Asciz - classname
17
+
18
+ public void run(){}
19
+
20
+ }
@@ -0,0 +1,9 @@
1
+ if exist AccessFlagsTest*.class del AccessFlagsTest*.class
2
+
3
+ md classes
4
+ javac -d classes AccessFlagsTest*.java
5
+
6
+ copy classes\packagename\*.class .
7
+
8
+ rmdir /S /Q classes
9
+ pause
@@ -0,0 +1,35 @@
1
+ if exist ClassVersionTest*.class del ClassVersionTest*.class
2
+
3
+ setlocal
4
+
5
+ call profile102
6
+ javac -classpath %CLASSPATH% ClassVersionTest.java
7
+ ren ClassVersionTest.class ClassVersionTest10.class
8
+
9
+ call profile118
10
+ javac -classpath %CLASSPATH% ClassVersionTest.java
11
+ ren ClassVersionTest.class ClassVersionTest11.class
12
+
13
+ call profile122
14
+ javac -target 1.2 ClassVersionTest.java
15
+ ren ClassVersionTest.class ClassVersionTest12.class
16
+
17
+ call profile131
18
+ javac -target 1.3 ClassVersionTest.java
19
+ ren ClassVersionTest.class ClassVersionTest13.class
20
+
21
+ call profile141
22
+ javac -target 1.4 ClassVersionTest.java
23
+ ren ClassVersionTest.class ClassVersionTest14.class
24
+
25
+ call profile150
26
+ javac ClassVersionTest.java
27
+ ren ClassVersionTest.class ClassVersionTest15.class
28
+
29
+ call profile160
30
+ javac ClassVersionTest.java
31
+ ren ClassVersionTest.class ClassVersionTest16.class
32
+
33
+ endlocal
34
+
35
+ pause
@@ -0,0 +1,6 @@
1
+ if exist ConstantPoolTest.class del ConstantPoolTest.class
2
+
3
+ javac -g:none ConstantPoolTest.java
4
+ javap -verbose -private ConstantPoolTest
5
+
6
+ pause
data/test/setup.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'test/unit'
2
+
3
+ # add the lib to the load path
4
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
5
+
6
+ TEST_DATA_PATH = File.join(File.dirname(__FILE__), 'data')
7
+
8
+ # Load the binary class data of given _name_ from the test path.
9
+ def load_class(name)
10
+ File.open("#{TEST_DATA_PATH}/#{name}.class", 'rb') {|io| io.read }
11
+ end
12
+
13
+ # require 'javaclass'
14
+ # require File.join(File.dirname(__FILE__), %w[.. lib javaclass])
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/java_class_header'
3
+
4
+ module TestJavaClass
5
+
6
+ class TestAccessFlags < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @cls = {}
10
+ %w[Public Package Abstract Interface Final].each do |t|
11
+ eval("@#{t.downcase} = JavaClass::JavaClassHeader.new(load_class('AccessFlagsTest#{t}')).access_flags")
12
+ end
13
+ end
14
+
15
+ def test_accessible_eh
16
+ assert(@public.accessible?)
17
+ assert(!@package.accessible?)
18
+ assert(@abstract.accessible?)
19
+ assert(@interface.accessible?)
20
+ assert(@final.accessible?)
21
+ end
22
+
23
+ def test_public_eh
24
+ assert(@public.public?)
25
+ end
26
+
27
+ def test_interface_eh
28
+ assert(!@public.interface?)
29
+ assert(!@package.interface?)
30
+ assert(!@abstract.interface?)
31
+ assert(@interface.interface?)
32
+ assert(!@final.interface?)
33
+ end
34
+
35
+ def test_abstract_eh
36
+ assert(!@public.abstract?)
37
+ assert(!@package.abstract?)
38
+ assert(@abstract.abstract?)
39
+ assert(@interface.abstract?)
40
+ assert(!@final.abstract?)
41
+ end
42
+
43
+ def test_final_eh
44
+ assert(!@public.final?)
45
+ assert(!@package.final?)
46
+ assert(!@abstract.final?)
47
+ assert(!@interface.final?)
48
+ assert(@final.final?)
49
+ end
50
+
51
+ end
52
+
53
+ end
data/test/test_base.rb ADDED
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/constants/base'
3
+
4
+ module TestJavaClass
5
+
6
+ class TestConstantBase < Test::Unit::TestCase
7
+
8
+ class ConstantClass < JavaClass::Constants::Base;
9
+ def initialize(name=nil); super(name); end
10
+ end
11
+
12
+ def test_name
13
+ v = ConstantClass.new
14
+ assert_equal("Class", v.name)
15
+
16
+ v = ConstantClass.new("Bubu")
17
+ assert_equal("Bubu", v.name)
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/class_version'
3
+
4
+ module TestJavaClass
5
+
6
+ class TestClassVersion < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @v = (0..6).collect do |i|
10
+ JavaClass::ClassVersion.new(load_class("ClassVersionTest1#{i}"))
11
+ end
12
+ end
13
+
14
+ # Assert the list of +v+ against the list of _expectations_ when invoking the given block.
15
+ def assert_classes(expectations)
16
+ expectations.values_at.each do |i|
17
+ assert_equal(expectations[i], yield(@v[i]), "#{i}. element")
18
+ end
19
+ end
20
+
21
+ def test_major
22
+ assert_classes([45, 45, 46, 47, 48, 49, 50]) {|v| v.major}
23
+ end
24
+
25
+ def test_minor
26
+ assert_classes([3, 3, 0, 0, 0, 0, 0]) {|v| v.minor}
27
+ assert_equal(3, @v[1].minor) # shouldn't it be > 45.3
28
+ end
29
+
30
+ def test_to_s
31
+ assert_equal('50.0', @v[6].to_s)
32
+ assert_equal('45.3', @v[0].to_s)
33
+ end
34
+
35
+ def test_dump
36
+ assert_equal([" minor version: 0", " major version: 50"], @v[6].dump)
37
+ end
38
+
39
+ def test_to_f
40
+ assert_equal(50.0, @v[6].to_f)
41
+ assert_equal(45.3, @v[0].to_f)
42
+
43
+ v = JavaClass::ClassVersion.new("....\000\xff\0002")
44
+ assert_equal(50.255, v.to_f)
45
+
46
+ v = JavaClass::ClassVersion.new("....\xff\xff\0002")
47
+ assert_equal(50.65535, v.to_f)
48
+ end
49
+
50
+ def test_jdk_version
51
+ assert_classes(%w[1.0 1.0 1.2 1.3 1.4 1.5 1.6]) {|v| v.jdk_version}
52
+
53
+ v = JavaClass::ClassVersion.new("....\000\xff\0002")
54
+ assert_equal('unknown', v.jdk_version)
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,90 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/constant_pool'
3
+
4
+ module TestJavaClass
5
+
6
+ class TestConstantPool < Test::Unit::TestCase
7
+
8
+ # 1.6.0_11 javap.exe output of test class, only tweaked the float value a bit...
9
+ JAVAP_OUTPUT = ' Constant pool:
10
+ const #1 = Method #15.#33; // java/lang/Object."<init>":()V
11
+ const #2 = String #34; // String field
12
+ const #3 = Field #14.#35; // ConstantPoolTest.stringField:Ljava/lang/String;
13
+ const #4 = InterfaceMethod #16.#36; // java/lang/Runnable.run:()V
14
+ const #5 = Field #14.#37; // ConstantPoolTest.intField:I
15
+ const #6 = float 3.14158995322368e+022f;
16
+ const #7 = Field #14.#38; // ConstantPoolTest.floatField:F
17
+ const #8 = long 99999999l;
18
+ const #10 = Field #14.#39; // ConstantPoolTest.longField:J
19
+ const #11 = double 3.14159265258979e-208d;
20
+ const #13 = Field #14.#40; // ConstantPoolTest.doubleField:D
21
+ const #14 = class #41; // ConstantPoolTest
22
+ const #15 = class #42; // java/lang/Object
23
+ const #16 = class #43; // java/lang/Runnable
24
+ const #17 = Asciz stringField;
25
+ const #18 = Asciz Ljava/lang/String;;
26
+ const #19 = Asciz intField;
27
+ const #20 = Asciz I;
28
+ const #21 = Asciz ConstantValue;
29
+ const #22 = int 42;
30
+ const #23 = Asciz floatField;
31
+ const #24 = Asciz F;
32
+ const #25 = Asciz longField;
33
+ const #26 = Asciz J;
34
+ const #27 = Asciz doubleField;
35
+ const #28 = Asciz D;
36
+ const #29 = Asciz <init>;
37
+ const #30 = Asciz ()V;
38
+ const #31 = Asciz Code;
39
+ const #32 = Asciz run;
40
+ const #33 = NameAndType #29:#30;// "<init>":()V
41
+ const #34 = Asciz String field;
42
+ const #35 = NameAndType #17:#18;// stringField:Ljava/lang/String;
43
+ const #36 = NameAndType #32:#30;// run:()V
44
+ const #37 = NameAndType #19:#20;// intField:I
45
+ const #38 = NameAndType #23:#24;// floatField:F
46
+ const #39 = NameAndType #25:#26;// longField:J
47
+ const #40 = NameAndType #27:#28;// doubleField:D
48
+ const #41 = Asciz ConstantPoolTest;
49
+ const #42 = Asciz java/lang/Object;
50
+ const #43 = Asciz java/lang/Runnable;'
51
+
52
+ def setup
53
+ @cp = JavaClass::ConstantPool.new(load_class('ConstantPoolTest'))
54
+ end
55
+
56
+ def test_index
57
+ assert_equal('Method', @cp[1].name)
58
+ assert_equal('Field', @cp[10].name)
59
+ end
60
+
61
+ def test_size
62
+ assert_equal(331, @cp.size)
63
+ end
64
+
65
+ def test_item_count
66
+ assert_equal(43, @cp.item_count)
67
+ end
68
+
69
+ def test_dump
70
+ # puts @cp.dump.join("\n")
71
+ assert_equal(JAVAP_OUTPUT.gsub(/ +/,' ').gsub(/"</,'<').gsub(/>"/,'>'), @cp.dump.join("\n").gsub(/( |\t)+/,' '))
72
+ end
73
+
74
+ def test_items
75
+ pool = @cp.items
76
+ assert_equal(41, pool.size) # 2 double
77
+ assert_equal('Method', pool[0].name)
78
+ assert_equal('stringField', pool[14].value)
79
+ end
80
+
81
+ def test_find
82
+ found = @cp.find(JavaClass::ConstantPool::STRING_TAG)
83
+ assert_equal(1, found.size)
84
+ assert_equal('String', found[0].name)
85
+ assert_equal(34, found[0].string_index)
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/java_class_header'
3
+
4
+ module TestJavaClass
5
+
6
+ class TestJavaClassHeader < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @public = JavaClass::JavaClassHeader.new(load_class("AccessFlagsTestPublic"))
10
+ end
11
+
12
+ def test_magic
13
+ assert(@public.magic.valid?)
14
+ end
15
+
16
+ def test_version
17
+ assert_equal('50.0', @public.version.to_s) # JDK 6
18
+ end
19
+
20
+ def test_this_class
21
+ assert_equal('packagename/AccessFlagsTestPublic', @public.this_class)
22
+ end
23
+
24
+ def test_super_class
25
+ assert_equal('java/lang/Object', @public.super_class)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/java_class_header'
3
+
4
+ module TestJavaClass
5
+
6
+ class TestReferences < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @refs = JavaClass::JavaClassHeader.new(load_class("AccessFlagsTestPublic")).references
10
+ end
11
+
12
+ def test_referenced_fields
13
+ fields = @refs.referenced_fields(false)
14
+ assert_equal(0, fields.size)
15
+ end
16
+
17
+ def test_referenced_methods
18
+ methods = @refs.referenced_methods(false)
19
+ assert_equal(1, methods.size) # ctor init
20
+ assert_equal('java/lang/Object', methods[0].class_name)
21
+
22
+ # TODO test with myself (true)...
23
+ end
24
+
25
+ # TODO test referenced classes
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/setup'
2
+ require 'javaclass/string_ux'
3
+
4
+ class TestString < Test::Unit::TestCase
5
+
6
+ def test_u1
7
+ assert_equal(49, "1".u1(0))
8
+ assert_equal(49, "212".u1(1))
9
+ assert_equal(50, "212".u1(2))
10
+ end
11
+
12
+ def test_u2
13
+ assert_equal(49*256+50, "12".u2(0))
14
+ assert_equal(49*256+50, "312".u2(1))
15
+ end
16
+
17
+ def test_u4
18
+ assert_equal(1, "\0\0\0\1".u4(0))
19
+ assert_equal(256**4-2, "\xff\xff\xff\xfe".u4(0))
20
+ end
21
+
22
+ def test_single
23
+ assert_in_delta(3.14159, '@I�'.single(0), 0.0000002)
24
+ end
25
+
26
+ def test_double
27
+ assert_in_delta(3.14159265258981, "@\t!�T!� ".double(0), 0.00000000000001)
28
+ end
29
+
30
+ end