aboutsummaryrefslogblamecommitdiffstats
path: root/plumbing/format/packfile/encoder_test.go
blob: 1a94d16c39dd6a4fecc458ff5e3642fd29b37090 (plain) (tree)













































                                                                            
                                             























                                                                            
                                       

                                        
                                             





                                                            






                                                                        











                                                                              
                                                                          

                                    
                                                     
                                           
                                                                            
                                                    
                                                                

                                            















                                                     
                                                                         
                                    

                                                                            


















                                                                    







































                                                                           
                                                                                


                                               
                                                                                         

































                                                                                        
                                                                                


                                               
                                                                                         


                                                     
                                                                                                        



                                                               
                                                                                                        









                                                
                                                                           






                                    
package packfile

import (
	"bytes"

	"gopkg.in/src-d/go-git.v4/fixtures"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/storage/memory"

	. "gopkg.in/check.v1"
)

type EncoderSuite struct {
	fixtures.Suite
	buf   *bytes.Buffer
	store *memory.Storage
	enc   *Encoder
}

var _ = Suite(&EncoderSuite{})

func (s *EncoderSuite) SetUpTest(c *C) {
	s.buf = bytes.NewBuffer(nil)
	s.store = memory.NewStorage()
	s.enc = NewEncoder(s.buf, s.store)
}

func (s *EncoderSuite) TestCorrectPackHeader(c *C) {
	hash, err := s.enc.Encode([]plumbing.Hash{})
	c.Assert(err, IsNil)

	hb := [20]byte(hash)

	// PACK + VERSION + OBJECTS + HASH
	expectedResult := []byte{'P', 'A', 'C', 'K', 0, 0, 0, 2, 0, 0, 0, 0}
	expectedResult = append(expectedResult, hb[:]...)

	result := s.buf.Bytes()

	c.Assert(result, DeepEquals, expectedResult)
}

func (s *EncoderSuite) TestCorrectPackWithOneEmptyObject(c *C) {
	o := &plumbing.MemoryObject{}
	o.SetType(plumbing.CommitObject)
	o.SetSize(0)
	_, err := s.store.SetEncodedObject(o)
	c.Assert(err, IsNil)

	hash, err := s.enc.Encode([]plumbing.Hash{o.Hash()})
	c.Assert(err, IsNil)

	// PACK + VERSION(2) + OBJECT NUMBER(1)
	expectedResult := []byte{'P', 'A', 'C', 'K', 0, 0, 0, 2, 0, 0, 0, 1}
	// OBJECT HEADER(TYPE + SIZE)= 0001 0000
	expectedResult = append(expectedResult, []byte{16}...)

	// Zlib header
	expectedResult = append(expectedResult,
		[]byte{120, 156, 1, 0, 0, 255, 255, 0, 0, 0, 1}...)

	// + HASH
	hb := [20]byte(hash)
	expectedResult = append(expectedResult, hb[:]...)

	result := s.buf.Bytes()

	c.Assert(result, DeepEquals, expectedResult)
}

func (s *EncoderSuite) TestMaxObjectSize(c *C) {
	o := s.store.NewEncodedObject()
	o.SetSize(9223372036854775807)
	o.SetType(plumbing.CommitObject)
	_, err := s.store.SetEncodedObject(o)
	c.Assert(err, IsNil)
	hash, err := s.enc.Encode([]plumbing.Hash{o.Hash()})
	c.Assert(err, IsNil)
	c.Assert(hash.IsZero(), Not(Equals), true)
}

func (s *EncoderSuite) TestHashNotFound(c *C) {
	h, err := s.enc.Encode([]plumbing.Hash{plumbing.NewHash("BAD")})
	c.Assert(h, Equals, plumbing.ZeroHash)
	c.Assert(err, NotNil)
	c.Assert(err, Equals, plumbing.ErrObjectNotFound)
}

func (s *EncoderSuite) TestDecodeEncodeDecode(c *C) {
	fixtures.Basic().ByTag("packfile").Test(c, func(f *fixtures.Fixture) {
		scanner := NewScanner(f.Packfile())
		storage := memory.NewStorage()

		d, err := NewDecoder(scanner, storage)
		c.Assert(err, IsNil)

		ch, err := d.Decode()
		c.Assert(err, IsNil)
		c.Assert(ch, Equals, f.PackfileHash)

		objIter, err := d.o.IterEncodedObjects(plumbing.AnyObject)
		c.Assert(err, IsNil)

		objects := []plumbing.EncodedObject{}
		hashes := []plumbing.Hash{}
		err = objIter.ForEach(func(o plumbing.EncodedObject) error {
			objects = append(objects, o)
			hash, err := s.store.SetEncodedObject(o)
			c.Assert(err, IsNil)

			hashes = append(hashes, hash)

			return err

		})
		c.Assert(err, IsNil)
		_, err = s.enc.Encode(hashes)
		c.Assert(err, IsNil)

		scanner = NewScanner(s.buf)
		storage = memory.NewStorage()
		d, err = NewDecoder(scanner, storage)
		c.Assert(err, IsNil)
		_, err = d.Decode()
		c.Assert(err, IsNil)

		objIter, err = d.o.IterEncodedObjects(plumbing.AnyObject)
		c.Assert(err, IsNil)
		obtainedObjects := []plumbing.EncodedObject{}
		err = objIter.ForEach(func(o plumbing.EncodedObject) error {
			obtainedObjects = append(obtainedObjects, o)

			return nil
		})
		c.Assert(err, IsNil)
		c.Assert(len(obtainedObjects), Equals, len(objects))

		equals := 0
		for _, oo := range obtainedObjects {
			for _, o := range objects {
				if o.Hash() == oo.Hash() {
					equals++
				}
			}
		}

		c.Assert(len(obtainedObjects), Equals, equals)
	})
}

func (s *EncoderSuite) TestDecodeEncodeWithDeltaDecodeREF(c *C) {
	s.simpleDeltaTest(c, plumbing.REFDeltaObject)
}

func (s *EncoderSuite) TestDecodeEncodeWithDeltaDecodeOFS(c *C) {
	s.simpleDeltaTest(c, plumbing.OFSDeltaObject)
}

func (s *EncoderSuite) TestDecodeEncodeWithDeltasDecodeREF(c *C) {
	s.deltaOverDeltaTest(c, plumbing.REFDeltaObject)
}

func (s *EncoderSuite) TestDecodeEncodeWithDeltasDecodeOFS(c *C) {
	s.deltaOverDeltaTest(c, plumbing.OFSDeltaObject)
}

func (s *EncoderSuite) simpleDeltaTest(c *C, t plumbing.ObjectType) {
	srcObject := newObject(plumbing.BlobObject, []byte("0"))
	targetObject := newObject(plumbing.BlobObject, []byte("01"))

	deltaObject, err := delta(srcObject, targetObject, t)
	c.Assert(err, IsNil)

	srcToPack := newObjectToPack(srcObject)
	_, err = s.enc.encode([]*ObjectToPack{
		srcToPack,
		newDeltaObjectToPack(srcToPack, targetObject, deltaObject),
	})
	c.Assert(err, IsNil)

	scanner := NewScanner(s.buf)

	storage := memory.NewStorage()
	d, err := NewDecoder(scanner, storage)
	c.Assert(err, IsNil)

	_, err = d.Decode()
	c.Assert(err, IsNil)

	decSrc, err := storage.EncodedObject(srcObject.Type(), srcObject.Hash())
	c.Assert(err, IsNil)
	c.Assert(decSrc, DeepEquals, srcObject)

	decTarget, err := storage.EncodedObject(targetObject.Type(), targetObject.Hash())
	c.Assert(err, IsNil)
	c.Assert(decTarget, DeepEquals, targetObject)
}

func (s *EncoderSuite) deltaOverDeltaTest(c *C, t plumbing.ObjectType) {
	srcObject := newObject(plumbing.BlobObject, []byte("0"))
	targetObject := newObject(plumbing.BlobObject, []byte("01"))
	otherTargetObject := newObject(plumbing.BlobObject, []byte("011111"))

	deltaObject, err := delta(srcObject, targetObject, t)
	c.Assert(err, IsNil)
	c.Assert(deltaObject.Hash(), Not(Equals), plumbing.ZeroHash)

	otherDeltaObject, err := delta(targetObject, otherTargetObject, t)
	c.Assert(err, IsNil)
	c.Assert(otherDeltaObject.Hash(), Not(Equals), plumbing.ZeroHash)

	srcToPack := newObjectToPack(srcObject)
	targetToPack := newObjectToPack(targetObject)
	_, err = s.enc.encode([]*ObjectToPack{
		srcToPack,
		newDeltaObjectToPack(srcToPack, targetObject, deltaObject),
		newDeltaObjectToPack(targetToPack, otherTargetObject, otherDeltaObject),
	})
	c.Assert(err, IsNil)

	scanner := NewScanner(s.buf)
	storage := memory.NewStorage()
	d, err := NewDecoder(scanner, storage)
	c.Assert(err, IsNil)

	_, err = d.Decode()
	c.Assert(err, IsNil)

	decSrc, err := storage.EncodedObject(srcObject.Type(), srcObject.Hash())
	c.Assert(err, IsNil)
	c.Assert(decSrc, DeepEquals, srcObject)

	decTarget, err := storage.EncodedObject(targetObject.Type(), targetObject.Hash())
	c.Assert(err, IsNil)
	c.Assert(decTarget, DeepEquals, targetObject)

	decOtherTarget, err := storage.EncodedObject(otherTargetObject.Type(), otherTargetObject.Hash())
	c.Assert(err, IsNil)
	c.Assert(decOtherTarget, DeepEquals, otherTargetObject)
}

func delta(base, target plumbing.EncodedObject, t plumbing.ObjectType) (plumbing.EncodedObject, error) {
	switch t {
	case plumbing.OFSDeltaObject:
		return GetOFSDelta(base, target)
	case plumbing.REFDeltaObject:
		return GetRefDelta(base, target)
	default:
		panic("delta type not found")
	}
}

func newObject(t plumbing.ObjectType, cont []byte) plumbing.EncodedObject {
	o := plumbing.MemoryObject{}
	o.SetType(t)
	o.SetSize(int64(len(cont)))
	o.Write(cont)

	return &o
}