2013-08-03 20:47:57 +00:00
/ *
2018-01-04 00:52:49 +00:00
Copyright 2013 The Perkeep Authors
2013-08-03 20:47:57 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package blob
import (
2013-08-04 02:08:17 +00:00
"encoding/json"
"strings"
2013-08-03 20:47:57 +00:00
"testing"
)
2014-04-11 22:50:35 +00:00
var parseFn = map [ string ] func ( string ) ( Ref , bool ) {
"Parse" : Parse ,
"ParseKnown" : ParseKnown ,
}
2013-08-04 02:08:17 +00:00
var parseTests = [ ] struct {
2014-04-11 22:50:35 +00:00
in string
bad bool
fn string
skipBytes bool // skip ParseBytes test
2013-08-04 02:08:17 +00:00
} {
2013-08-04 02:54:30 +00:00
{ in : "sha1-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" } ,
2018-01-08 18:38:20 +00:00
{ in : "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" } ,
2013-08-04 02:54:30 +00:00
{ in : "foo-0b" } ,
{ in : "foo-0b0c" } ,
2014-04-11 22:50:35 +00:00
{ in : "sha1-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a34" , fn : "ParseKnown" } ,
{ in : "foo-0b0c" , bad : true , skipBytes : true , fn : "ParseKnown" } ,
{ in : "perma-1243" , fn : "ParseKnown" } ,
{ in : "fakeref-0012" , fn : "ParseKnown" } ,
2013-08-04 02:54:30 +00:00
{ in : "/camli/sha1-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" , bad : true } ,
2013-08-04 02:08:17 +00:00
{ in : "" , bad : true } ,
{ in : "foo" , bad : true } ,
{ in : "-0f" , bad : true } ,
{ in : "sha1-xx" , bad : true } ,
{ in : "-" , bad : true } ,
{ in : "sha1-0b" , bad : true } ,
2013-08-04 02:54:30 +00:00
// TODO: renable this later, once we clean all tests:
//{in: "foo-0b0cd", bad: true}, // odd number
{ in : "foo-abc" } , // accepted for now. will delete later.
2013-08-04 02:08:17 +00:00
}
2013-08-03 20:47:57 +00:00
func TestParse ( t * testing . T ) {
2013-08-04 02:08:17 +00:00
for _ , tt := range parseTests {
2014-04-11 22:50:35 +00:00
fn := tt . fn
if fn == "" {
fn = "Parse"
}
r , ok := parseFn [ fn ] ( tt . in )
2013-08-04 02:08:17 +00:00
if r . Valid ( ) != ok {
t . Errorf ( "Valid != ok for %q" , tt . in )
}
2013-08-04 02:54:30 +00:00
if ok && tt . bad {
2014-04-11 22:50:35 +00:00
t . Errorf ( "%s(%q) didn't fail. It should've." , fn , tt . in )
2013-08-04 02:54:30 +00:00
continue
}
2013-08-04 02:08:17 +00:00
if ! ok {
if ! tt . bad {
2014-04-11 22:50:35 +00:00
t . Errorf ( "%s(%q) failed to parse" , fn , tt . in )
2013-08-04 02:08:17 +00:00
continue
}
continue
}
2014-04-11 22:50:35 +00:00
if ! tt . skipBytes {
2013-12-04 13:38:13 +00:00
r2 , ok := ParseBytes ( [ ] byte ( tt . in ) )
if r != r2 {
t . Errorf ( "ParseBytes(%q) = %v, %v; want %v" , tt . in , r2 , ok , r )
}
}
2013-08-04 02:08:17 +00:00
str := r . String ( )
if str != tt . in {
t . Errorf ( "Parsed %q but String() value differs: %q" , tt . in , str )
}
wantDig := str [ strings . Index ( str , "-" ) + 1 : ]
if dig := r . Digest ( ) ; dig != wantDig {
t . Errorf ( "Digest(%q) = %q; want %q" , tt . in , dig , wantDig )
}
2013-08-04 05:25:39 +00:00
_ = r == r // test that concrete type of r supports equality
2013-08-03 20:47:57 +00:00
}
}
func TestEquality ( t * testing . T ) {
in := "sha1-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
in3 := "sha1-ffffffffffffffffffffffffffffffffffffffff"
r := ParseOrZero ( in )
r2 := ParseOrZero ( in )
r3 := ParseOrZero ( in3 )
if ! r . Valid ( ) || ! r2 . Valid ( ) || ! r3 . Valid ( ) {
t . Fatal ( "not valid" )
}
if r != r2 {
t . Errorf ( "r and r2 should be equal" )
}
if r == r3 {
t . Errorf ( "r and r3 should not be equal" )
}
}
2013-08-04 02:08:17 +00:00
func TestSum32 ( t * testing . T ) {
got := MustParse ( "sha1-1234567800000000000000000000000000000000" ) . Sum32 ( )
want := uint32 ( 0x12345678 )
if got != want {
t . Errorf ( "Sum32 = %x, want %x" , got , want )
}
}
func TestSum64 ( t * testing . T ) {
got := MustParse ( "sha1-12345678876543210000000000000000000000ff" ) . Sum64 ( )
want := uint64 ( 0x1234567887654321 )
if got != want {
t . Errorf ( "Sum64 = %x, want %x" , got , want )
}
}
type Foo struct {
B Ref ` json:"foo" `
}
func TestJSONUnmarshal ( t * testing . T ) {
var f Foo
if err := json . Unmarshal ( [ ] byte ( ` { "foo": "abc-def123", "other": 123} ` ) , & f ) ; err != nil {
t . Fatalf ( "Unmarshal: %v" , err )
}
if ! f . B . Valid ( ) {
t . Fatal ( "blobref is nil" )
}
if g , e := f . B . String ( ) , "abc-def123" ; g != e {
t . Errorf ( "got %q, want %q" , g , e )
}
2014-01-01 03:33:56 +00:00
f = Foo { }
if err := json . Unmarshal ( [ ] byte ( ` { } ` ) , & f ) ; err != nil {
t . Fatalf ( "Unmarshal: %v" , err )
}
if f . B . Valid ( ) {
t . Fatal ( "blobref is valid and shouldn't be" )
}
f = Foo { }
if err := json . Unmarshal ( [ ] byte ( ` { "foo":null} ` ) , & f ) ; err != nil {
t . Fatalf ( "Unmarshal: %v" , err )
}
if f . B . Valid ( ) {
t . Fatal ( "blobref is valid and shouldn't be" )
}
2013-08-04 02:08:17 +00:00
}
func TestJSONMarshal ( t * testing . T ) {
2014-01-01 03:33:56 +00:00
f := & Foo { }
2013-08-04 02:08:17 +00:00
bs , err := json . Marshal ( f )
if err != nil {
t . Fatalf ( "Marshal: %v" , err )
}
2014-01-01 03:33:56 +00:00
if g , e := string ( bs ) , ` { "foo":null} ` ; g != e {
t . Errorf ( "got %q, want %q" , g , e )
}
f = & Foo { B : MustParse ( "def-1234abcd" ) }
bs , err = json . Marshal ( f )
if err != nil {
t . Fatalf ( "Marshal: %v" , err )
}
2013-08-04 02:08:17 +00:00
if g , e := string ( bs ) , ` { "foo":"def-1234abcd"} ` ; g != e {
t . Errorf ( "got %q, want %q" , g , e )
}
}
func TestSizedBlobRefString ( t * testing . T ) {
sr := SizedRef { Ref : MustParse ( "abc-1234" ) , Size : 456 }
want := "[abc-1234; 456 bytes]"
if got := sr . String ( ) ; got != want {
t . Errorf ( "SizedRef.String() = %q, want %q" , got , want )
}
}
2013-08-23 18:30:18 +00:00
2014-12-24 03:10:13 +00:00
func TestRefStringMinusOne ( t * testing . T ) {
br := MustParse ( "abc-1234" )
want := "abc-1233"
if got := br . StringMinusOne ( ) ; got != want {
t . Errorf ( "StringMinusOne = %q; want %q" , got , want )
}
}
2013-08-23 18:30:18 +00:00
func TestMarshalBinary ( t * testing . T ) {
br := MustParse ( "abc-00ff4869" )
data , _ := br . MarshalBinary ( )
if got , want := string ( data ) , "abc-\x00\xffHi" ; got != want {
t . Fatalf ( "MarshalBinary = %q; want %q" , got , want )
}
br2 := new ( Ref )
if err := br2 . UnmarshalBinary ( data ) ; err != nil {
t . Fatal ( err )
}
if * br2 != br {
t . Error ( "UnmarshalBinary result != original" )
}
if err := br2 . UnmarshalBinary ( data ) ; err == nil {
t . Error ( "expect error on second UnmarshalBinary" )
}
}
2013-12-06 06:19:20 +00:00
func BenchmarkParseBlob ( b * testing . B ) {
b . ReportAllocs ( )
ref := "sha1-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
refb := [ ] byte ( ref )
for i := 0 ; i < b . N ; i ++ {
if _ , ok := Parse ( ref ) ; ! ok {
2014-11-12 22:21:42 +00:00
b . FailNow ( )
2013-12-06 06:19:20 +00:00
}
if _ , ok := ParseBytes ( refb ) ; ! ok {
2014-11-12 22:21:42 +00:00
b . FailNow ( )
2013-12-06 06:19:20 +00:00
}
}
}
2014-08-13 18:07:37 +00:00
func TestJSONUnmarshalSized ( t * testing . T ) {
var sb SizedRef
if err := json . Unmarshal ( [ ] byte ( ` {
"blobRef" : "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ,
"size" : 123 } ` ) , & sb ) ; err != nil {
t . Fatalf ( "Unmarshal: %v" , err )
}
want := SizedRef {
Ref : MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) ,
Size : 123 ,
}
if sb != want {
t . Fatalf ( "got %q, want %q" , sb , want )
}
sb = SizedRef { }
if err := json . Unmarshal ( [ ] byte ( ` { } ` ) , & sb ) ; err != nil {
t . Fatalf ( "Unmarshal: %v" , err )
}
if sb . Valid ( ) {
t . Fatal ( "sized blobref is valid and shouldn't be" )
}
sb = SizedRef { }
if err := json . Unmarshal ( [ ] byte ( ` { "blobRef":null, "size": 456} ` ) , & sb ) ; err != nil {
t . Fatalf ( "Unmarshal: %v" , err )
}
if sb . Valid ( ) {
t . Fatal ( "sized blobref is valid and shouldn't be" )
}
}
func TestJSONMarshalSized ( t * testing . T ) {
sb := SizedRef {
Ref : MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) ,
Size : 123 ,
}
b , err := json . Marshal ( sb )
if err != nil {
t . Fatalf ( "Marshal: %v" , err )
}
if g , e := string ( b ) , ` { "blobRef":"sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659","size":123} ` ; g != e {
t . Fatalf ( "got %q, want %q" , g , e )
}
sb = SizedRef { }
b , err = json . Marshal ( sb )
if err != nil {
t . Fatalf ( "Marshal: %v" , err )
}
if g , e := string ( b ) , ` { "blobRef":null,"size":0} ` ; g != e {
t . Fatalf ( "got %q, want %q" , g , e )
}
}
2017-12-18 20:03:34 +00:00
var equalStringTests = [ ] struct {
ref Ref
str string
want bool
} {
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" , true } ,
// last digit wrong:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9658" , false } ,
// second to last digit wrong:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9669" , false } ,
// hyphen wrong:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1xce284c167558a9ef22df04390c87a6d0c9ed9659" , false } ,
// truncated:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-" , false } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1" , false } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "" , false } ,
// right length, wrong hash:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha2-ce284c167558a9ef22df04390c87a6d0c9ed9659" , false } ,
// Other hashes:
{ MustParse ( "foo-cafe" ) , "foo-cafe" , true } ,
{ MustParse ( "foo-caf" ) , "foo-caf" , true } ,
{ MustParse ( "foo-cafe" ) , "foo-beef" , false } ,
{ MustParse ( "foo-cafe" ) , "bar-cafe" , false } ,
{ MustParse ( "foo-cafe" ) , "fooxbeef" , false } ,
{ MustParse ( "foo-caf" ) , "foo-cae" , false } ,
{ MustParse ( "foo-caf" ) , "foo-ca" , false } ,
2018-01-08 18:38:20 +00:00
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" , true } ,
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42g" , false } , // last byte wrong
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42e" , false } , // last byte wrong
2017-12-18 20:03:34 +00:00
}
func TestEqualString ( t * testing . T ) {
for _ , tt := range equalStringTests {
got := tt . ref . EqualString ( tt . str )
if got != tt . want {
t . Errorf ( "ref %q EqualString(%q) = %v; want %v" , tt . ref , tt . str , got , tt . want )
}
}
}
func BenchmarkEqualString ( b * testing . B ) {
b . ReportAllocs ( )
for i := 0 ; i < b . N ; i ++ {
for _ , tt := range equalStringTests {
got := tt . ref . EqualString ( tt . str )
if got != tt . want {
b . Fatalf ( "ref %q EqualString(%q) = %v; want %v" , tt . ref , tt . str , got , tt . want )
}
}
}
}
2018-01-01 17:57:13 +00:00
var hasPrefixTests = [ ] struct {
ref Ref
str string
want bool
} {
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" , true } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9ed" , true } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9e" , true } ,
// last digit wrong:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9ee" , false } ,
// second to last digit wrong:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-ce284c167558a9ef22df04390c87a6d0c9f" , false } ,
// hyphen wrong:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1xce284c167558a9ef22df04390c87a6d0c9ed" , false } ,
// truncated:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-c" , true } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1-" , false } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha1" , false } ,
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "" , false } ,
// wrong hash:
{ MustParse ( "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659" ) , "sha2-ce284c167558a9ef22df04390c87a6d0c9ed96" , false } ,
// Other hashes:
{ MustParse ( "foo-cafe" ) , "foo-cafe" , true } ,
{ MustParse ( "foo-cafe" ) , "foo-caf" , true } ,
{ MustParse ( "foo-cafe" ) , "foo-ca" , true } ,
{ MustParse ( "foo-cafe" ) , "foo-c" , true } ,
{ MustParse ( "foo-cafe" ) , "foo-" , false } ,
{ MustParse ( "foo-cafe" ) , "" , false } ,
{ MustParse ( "foo-cafe" ) , "foo-beef" , false } ,
{ MustParse ( "foo-cafe" ) , "foo-bee" , false } ,
{ MustParse ( "foo-cafe" ) , "bar-cafe" , false } ,
{ MustParse ( "foo-cafe" ) , "fooxbe" , false } ,
{ MustParse ( "foo-cafe" ) , "foo-c" , true } ,
{ MustParse ( "foo-caf" ) , "foo-cae" , false } ,
{ MustParse ( "foo-caf" ) , "foo-cb" , false } ,
2018-01-08 18:38:20 +00:00
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d14a" , true } ,
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d14b" , false } ,
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d14" , true } ,
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-d15" , false } ,
{ MustParse ( "sha224-d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" ) , "sha224-" , false } , // TODO: make this true?
2018-01-01 17:57:13 +00:00
}
func TestHasPrefix ( t * testing . T ) {
for _ , tt := range hasPrefixTests {
got := tt . ref . HasPrefix ( tt . str )
if got != tt . want {
t . Errorf ( "ref %q HasPrefix(%q) = %v; want %v" , tt . ref , tt . str , got , tt . want )
}
}
}
func BenchmarkHasPrefix ( b * testing . B ) {
b . ReportAllocs ( )
for i := 0 ; i < b . N ; i ++ {
for _ , tt := range hasPrefixTests {
got := tt . ref . HasPrefix ( tt . str )
if got != tt . want {
b . Fatalf ( "ref %q HasPrefix(%q) = %v; want %v" , tt . ref , tt . str , got , tt . want )
}
}
}
}