mirror of
https://github.com/expo/bsdiff.git
synced 2026-11-14 17:49:03 -10:00
Add gtest unittest framework.
bsdiff and bspatch are in C and had no unittest. This patch keeps those programs as C code, but adds the C++ gtest unittest framework to allow testing them. Two simple unittests added to validate that the unittests work. BUG=None TEST=make test; ./unittests Change-Id: I8bca6b0c6bc5d5880464183d50a602c9886d20d0
This commit is contained in:
1 parent
68b49a8191
commit
a5cff2283b
16 files changed
+485
-65
No files matched your search
@@ -11,3 +11,4 @@
|
||||
# Executables
|
||||
bsdiff
|
||||
bspatch
|
||||
unittests
|
||||
@@ -8,6 +8,7 @@ BINARIES += $(BINARIES-y)
|
||||
|
||||
INSTALL = install
|
||||
CFLAGS += -O3 -Wall -Werror
|
||||
CXXFLAGS += -std=c++11
|
||||
|
||||
DESTDIR ?=
|
||||
PREFIX = /usr
|
||||
@@ -18,19 +19,48 @@ MAN1DIR = $(MANDIR)/man1
|
||||
INSTALL_PROGRAM ?= $(INSTALL) -c -m 755
|
||||
INSTALL_MAN ?= $(INSTALL) -c -m 444
|
||||
|
||||
.PHONY: all clean
|
||||
.PHONY: all test clean
|
||||
all: $(BINARIES)
|
||||
test: unittests
|
||||
clean:
|
||||
rm -f *.o $(BINARIES)
|
||||
rm -f *.o $(BINARIES) unittests
|
||||
|
||||
bsdiff: bsdiff.o
|
||||
bsdiff: LDLIBS += -lbz2 -ldivsufsort -ldivsufsort64
|
||||
bspatch: bspatch.o extents.o exfile.o
|
||||
bspatch: LDLIBS += -lbz2
|
||||
BSDIFF_LIBS = -lbz2 -ldivsufsort -ldivsufsort64
|
||||
BSDIFF_OBJS = \
|
||||
bsdiff.o
|
||||
|
||||
bspatch.o: extents.h exfile.h
|
||||
extents.o: extents.h exfile.h
|
||||
exfile.o: exfile.h
|
||||
BSPATCH_LIBS = -lbz2
|
||||
BSPATCH_OBJS = \
|
||||
bspatch.o \
|
||||
exfile.o \
|
||||
extents.o
|
||||
|
||||
UNITTEST_LIBS = -lgtest
|
||||
UNITTEST_OBJS = \
|
||||
bsdiff_unittest.o \
|
||||
test_utils.o \
|
||||
testrunner.o
|
||||
|
||||
bsdiff: $(BSDIFF_OBJS) bsdiff_main.o
|
||||
bsdiff: LDLIBS += $(BSDIFF_LIBS)
|
||||
|
||||
bspatch: $(BSPATCH_OBJS) bspatch_main.o
|
||||
bspatch: LDLIBS += $(BSPATCH_LIBS)
|
||||
|
||||
unittests: LDLIBS += $(BSDIFF_LIBS) $(BSPATCH_LIBS) $(UNITTEST_LIBS)
|
||||
unittests: $(BSPATCH_OBJS) $(BSDIFF_OBJS) $(UNITTEST_OBJS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LDLIBS)
|
||||
|
||||
# Source file dependencies.
|
||||
bsdiff.o: bsdiff.c
|
||||
bsdiff_main.o: bsdiff_main.c bsdiff.h
|
||||
bsdiff_unittest.o: bsdiff_unittest.cc bsdiff.h test_utils.h
|
||||
bspatch.o: bspatch.c exfile.h extents.h
|
||||
bspatch_main.o: bspatch_main.c bspatch.h
|
||||
exfile.o: exfile.c exfile.h
|
||||
extents.o: extents.c extents.h exfile.h
|
||||
testrunner.o: testrunner.cc
|
||||
test_utils.o: test_utils.cc test_utils.h
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MAN1DIR)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* All rights reserved
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted providing that the following conditions
|
||||
* modification, are permitted providing that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
@@ -102,8 +102,8 @@ static void offtout(off_t x,u_char *buf)
|
||||
if(x<0) buf[7]|=0x80;
|
||||
}
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
int bsdiff(const char* old_filename, const char* new_filename,
|
||||
const char* patch_filename) {
|
||||
int fd;
|
||||
u_char *old,*new;
|
||||
off_t oldsize,newsize;
|
||||
@@ -122,16 +122,14 @@ int main(int argc,char *argv[])
|
||||
BZFILE * pfbz2;
|
||||
int bz2err;
|
||||
|
||||
if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);
|
||||
|
||||
/* Allocate oldsize+1 bytes instead of oldsize bytes to ensure
|
||||
that we never try to malloc(0) and get a NULL pointer */
|
||||
if(((fd=open(argv[1],O_RDONLY,0))<0) ||
|
||||
if(((fd=open(old_filename,O_RDONLY,0))<0) ||
|
||||
((oldsize=lseek(fd,0,SEEK_END))==-1) ||
|
||||
((old=malloc(oldsize+1))==NULL) ||
|
||||
(lseek(fd,0,SEEK_SET)!=0) ||
|
||||
(read(fd,old,oldsize)!=oldsize) ||
|
||||
(close(fd)==-1)) err(1,"%s",argv[1]);
|
||||
(close(fd)==-1)) err(1,"%s",old_filename);
|
||||
|
||||
if(((I=malloc((oldsize+1)*sizeof(saidx_t)))==NULL)) err(1,NULL);
|
||||
|
||||
@@ -139,12 +137,12 @@ int main(int argc,char *argv[])
|
||||
|
||||
/* Allocate newsize+1 bytes instead of newsize bytes to ensure
|
||||
that we never try to malloc(0) and get a NULL pointer */
|
||||
if(((fd=open(argv[2],O_RDONLY,0))<0) ||
|
||||
if(((fd=open(new_filename,O_RDONLY,0))<0) ||
|
||||
((newsize=lseek(fd,0,SEEK_END))==-1) ||
|
||||
((new=malloc(newsize+1))==NULL) ||
|
||||
(lseek(fd,0,SEEK_SET)!=0) ||
|
||||
(read(fd,new,newsize)!=newsize) ||
|
||||
(close(fd)==-1)) err(1,"%s",argv[2]);
|
||||
(close(fd)==-1)) err(1,"%s",new_filename);
|
||||
|
||||
if(((db=malloc(newsize+1))==NULL) ||
|
||||
((eb=malloc(newsize+1))==NULL)) err(1,NULL);
|
||||
@@ -152,8 +150,8 @@ int main(int argc,char *argv[])
|
||||
eblen=0;
|
||||
|
||||
/* Create the patch file */
|
||||
if ((pf = fopen(argv[3], "w")) == NULL)
|
||||
err(1, "%s", argv[3]);
|
||||
if ((pf = fopen(patch_filename, "w")) == NULL)
|
||||
err(1, "%s", patch_filename);
|
||||
|
||||
/* Header is
|
||||
0 8 "BSDIFF40"
|
||||
@@ -170,7 +168,7 @@ int main(int argc,char *argv[])
|
||||
offtout(0, header + 16);
|
||||
offtout(newsize, header + 24);
|
||||
if (fwrite(header, 32, 1, pf) != 1)
|
||||
err(1, "fwrite(%s)", argv[3]);
|
||||
err(1, "fwrite(%s)", patch_filename);
|
||||
|
||||
/* Compute the differences, writing ctrl as we go */
|
||||
if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL)
|
||||
@@ -199,7 +197,7 @@ int main(int argc,char *argv[])
|
||||
(old[scsc+lastoffset] == new[scsc]))
|
||||
oldscore++;
|
||||
|
||||
if(((len==oldscore) && (len!=0)) ||
|
||||
if(((len==oldscore) && (len!=0)) ||
|
||||
(len>oldscore+8)) break;
|
||||
|
||||
if((scan+lastoffset<oldsize) &&
|
||||
@@ -316,7 +314,7 @@ int main(int argc,char *argv[])
|
||||
if (fseeko(pf, 0, SEEK_SET))
|
||||
err(1, "fseeko");
|
||||
if (fwrite(header, 32, 1, pf) != 1)
|
||||
err(1, "fwrite(%s)", argv[3]);
|
||||
err(1, "fwrite(%s)", patch_filename);
|
||||
if (fclose(pf))
|
||||
err(1, "fclose");
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef _BSDIFF_BSDIFF_H_
|
||||
#define _BSDIFF_BSDIFF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int bsdiff(const char* old_filename, const char* new_filename,
|
||||
const char* patch_filename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _BSDIFF_BSDIFF_H_ */
|
||||
@@ -0,0 +1,15 @@
|
||||
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
|
||||
#include "bsdiff.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc!=4)
|
||||
errx(1, "usage: %s oldfile newfile patchfile\n", argv[0]);
|
||||
|
||||
return bsdiff(argv[1], argv[2], argv[3]);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "bsdiff.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "test_utils.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using test_utils::BsdiffPatchFile;
|
||||
|
||||
class BsdiffTest : public testing::Test {
|
||||
protected:
|
||||
BsdiffTest()
|
||||
: old_file_("bsdiff_oldfile.XXXXXX"),
|
||||
new_file_("bsdiff_newfile.XXXXXX"),
|
||||
patch_file_("bsdiff_patchfile.XXXXXX") {
|
||||
}
|
||||
~BsdiffTest() override {}
|
||||
|
||||
test_utils::ScopedTempFile old_file_;
|
||||
test_utils::ScopedTempFile new_file_;
|
||||
test_utils::ScopedTempFile patch_file_;
|
||||
};
|
||||
|
||||
// Check that a file with no changes has a very small patch (no extra data).
|
||||
TEST_F(BsdiffTest, EqualEmptyFiles) {
|
||||
// Empty old and new files.
|
||||
EXPECT_EQ(0, bsdiff(old_file_.filename().c_str(),
|
||||
new_file_.filename().c_str(),
|
||||
patch_file_.filename().c_str()));
|
||||
BsdiffPatchFile patch;
|
||||
EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
|
||||
EXPECT_TRUE(patch.IsValid());
|
||||
|
||||
// An empty bz2 file will have 14 bytes.
|
||||
EXPECT_EQ(14, patch.diff_len);
|
||||
EXPECT_EQ(14, patch.extra_len);
|
||||
}
|
||||
|
||||
TEST_F(BsdiffTest, EqualSmallFiles) {
|
||||
string some_text = "Hello world!";
|
||||
vector<uint8_t> vec_some_text(some_text.begin(), some_text.end());
|
||||
test_utils::WriteFile(old_file_.filename(), vec_some_text);
|
||||
EXPECT_EQ(0, bsdiff(old_file_.filename().c_str(),
|
||||
new_file_.filename().c_str(),
|
||||
patch_file_.filename().c_str()));
|
||||
BsdiffPatchFile patch;
|
||||
EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
|
||||
EXPECT_TRUE(patch.IsValid());
|
||||
|
||||
// An empty bz2 file will have 14 bytes.
|
||||
EXPECT_EQ(14, patch.diff_len);
|
||||
EXPECT_EQ(14, patch.extra_len);
|
||||
}
|
||||
@@ -76,12 +76,10 @@ static ex_t *parse_extent_str(const char *ex_str, size_t *ex_count_p)
|
||||
return ex_arr;
|
||||
}
|
||||
|
||||
#define USAGE_TEMPLATE_STR \
|
||||
"usage: %s oldfile newfile patchfile [old-extents new-extents]\n" \
|
||||
"with extents taking the form \"off_1:len_1,...,off_n:len_n\"\n"
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int bspatch(
|
||||
const char* old_filename, const char* new_filename,
|
||||
const char* patch_filename,
|
||||
const char* old_extents, const char* new_extents) {
|
||||
FILE * f, * cpf, * dpf, * epf;
|
||||
BZFILE * cpfbz2, * dpfbz2, * epfbz2;
|
||||
int cbz2err, dbz2err, ebz2err;
|
||||
@@ -95,19 +93,18 @@ int main(int argc,char * argv[])
|
||||
off_t lenread;
|
||||
off_t i, j;
|
||||
|
||||
if ((argc != 6) && (argc != 4)) errx(1, USAGE_TEMPLATE_STR, argv[0]);
|
||||
int using_extents = (argc == 6);
|
||||
int using_extents = (old_extents != NULL || new_extents != NULL);
|
||||
|
||||
/* Open patch file */
|
||||
if ((f = fopen(argv[3], "r")) == NULL)
|
||||
err(1, "fopen(%s)", argv[3]);
|
||||
if ((f = fopen(patch_filename, "r")) == NULL)
|
||||
err(1, "fopen(%s)", patch_filename);
|
||||
|
||||
/*
|
||||
File format:
|
||||
0 8 "BSDIFF40"
|
||||
8 8 X
|
||||
16 8 Y
|
||||
24 8 sizeof(newfile)
|
||||
24 8 sizeof(new_filename)
|
||||
32 X bzip2(control block)
|
||||
32+X Y bzip2(diff block)
|
||||
32+X+Y ??? bzip2(extra block)
|
||||
@@ -120,7 +117,7 @@ int main(int argc,char * argv[])
|
||||
if (fread(header, 1, 32, f) < 32) {
|
||||
if (feof(f))
|
||||
errx(1, "Corrupt patch\n");
|
||||
err(1, "fread(%s)", argv[3]);
|
||||
err(1, "fread(%s)", patch_filename);
|
||||
}
|
||||
|
||||
/* Check for appropriate magic */
|
||||
@@ -136,25 +133,25 @@ int main(int argc,char * argv[])
|
||||
|
||||
/* Close patch file and re-open it via libbzip2 at the right places */
|
||||
if (fclose(f))
|
||||
err(1, "fclose(%s)", argv[3]);
|
||||
if ((cpf = fopen(argv[3], "r")) == NULL)
|
||||
err(1, "fopen(%s)", argv[3]);
|
||||
err(1, "fclose(%s)", patch_filename);
|
||||
if ((cpf = fopen(patch_filename, "r")) == NULL)
|
||||
err(1, "fopen(%s)", patch_filename);
|
||||
if (fseeko(cpf, 32, SEEK_SET))
|
||||
err(1, "fseeko(%s, %lld)", argv[3],
|
||||
err(1, "fseeko(%s, %lld)", patch_filename,
|
||||
(long long)32);
|
||||
if ((cpfbz2 = BZ2_bzReadOpen(&cbz2err, cpf, 0, 0, NULL, 0)) == NULL)
|
||||
errx(1, "BZ2_bzReadOpen, bz2err = %d", cbz2err);
|
||||
if ((dpf = fopen(argv[3], "r")) == NULL)
|
||||
err(1, "fopen(%s)", argv[3]);
|
||||
if ((dpf = fopen(patch_filename, "r")) == NULL)
|
||||
err(1, "fopen(%s)", patch_filename);
|
||||
if (fseeko(dpf, 32 + bzctrllen, SEEK_SET))
|
||||
err(1, "fseeko(%s, %lld)", argv[3],
|
||||
err(1, "fseeko(%s, %lld)", patch_filename,
|
||||
(long long)(32 + bzctrllen));
|
||||
if ((dpfbz2 = BZ2_bzReadOpen(&dbz2err, dpf, 0, 0, NULL, 0)) == NULL)
|
||||
errx(1, "BZ2_bzReadOpen, bz2err = %d", dbz2err);
|
||||
if ((epf = fopen(argv[3], "r")) == NULL)
|
||||
err(1, "fopen(%s)", argv[3]);
|
||||
if ((epf = fopen(patch_filename, "r")) == NULL)
|
||||
err(1, "fopen(%s)", patch_filename);
|
||||
if (fseeko(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
|
||||
err(1, "fseeko(%s, %lld)", argv[3],
|
||||
err(1, "fseeko(%s, %lld)", patch_filename,
|
||||
(long long)(32 + bzctrllen + bzdatalen));
|
||||
if ((epfbz2 = BZ2_bzReadOpen(&ebz2err, epf, 0, 0, NULL, 0)) == NULL)
|
||||
errx(1, "BZ2_bzReadOpen, bz2err = %d", ebz2err);
|
||||
@@ -162,16 +159,17 @@ int main(int argc,char * argv[])
|
||||
/* Open input file for reading. */
|
||||
if (using_extents) {
|
||||
size_t ex_count = 0;
|
||||
ex_t *ex_arr = parse_extent_str(argv[4], &ex_count);
|
||||
old_file = exfile_fopen(argv[1], "r", ex_arr, ex_count, free);
|
||||
ex_t *ex_arr = parse_extent_str(old_extents, &ex_count);
|
||||
old_file = exfile_fopen(new_filename, "r", ex_arr, ex_count,
|
||||
free);
|
||||
} else {
|
||||
old_file = fopen(argv[1], "r");
|
||||
old_file = fopen(new_filename, "r");
|
||||
}
|
||||
if (!old_file ||
|
||||
fseek(old_file, 0, SEEK_END) != 0 ||
|
||||
(oldsize = ftell(old_file)) < 0 ||
|
||||
fseek(old_file, 0, SEEK_SET) != 0)
|
||||
err(1, "cannot obtain the size of %s", argv[1]);
|
||||
err(1, "cannot obtain the size of %s", new_filename);
|
||||
off_t old_file_pos = 0;
|
||||
|
||||
if((new=malloc(newsize+1))==NULL) err(1,NULL);
|
||||
@@ -249,20 +247,21 @@ int main(int argc,char * argv[])
|
||||
BZ2_bzReadClose(&dbz2err, dpfbz2);
|
||||
BZ2_bzReadClose(&ebz2err, epfbz2);
|
||||
if (fclose(cpf) || fclose(dpf) || fclose(epf))
|
||||
err(1, "fclose(%s)", argv[3]);
|
||||
err(1, "fclose(%s)", patch_filename);
|
||||
|
||||
/* Write the new file */
|
||||
if (using_extents) {
|
||||
size_t ex_count = 0;
|
||||
ex_t *ex_arr = parse_extent_str(argv[5], &ex_count);
|
||||
new_file = exfile_fopen(argv[2], "w", ex_arr, ex_count, free);
|
||||
ex_t *ex_arr = parse_extent_str(new_extents, &ex_count);
|
||||
new_file = exfile_fopen(new_filename, "w", ex_arr, ex_count,
|
||||
free);
|
||||
} else {
|
||||
new_file = fopen(argv[2], "w");
|
||||
new_file = fopen(new_filename, "w");
|
||||
}
|
||||
if (!new_file ||
|
||||
fwrite_unlocked(new, 1, newsize, new_file) != newsize ||
|
||||
fclose(new_file) == EOF)
|
||||
err(1,"%s",argv[2]);
|
||||
err(1,"%s",new_filename);
|
||||
|
||||
free(new);
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef _BSDIFF_BSPATCH_H_
|
||||
#define _BSDIFF_BSPATCH_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int bspatch(
|
||||
const char* old_filename, const char* new_filename,
|
||||
const char* patch_filename,
|
||||
const char* old_extents, const char* new_extents);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _BSDIFF_BSPATCH_H_ */
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bspatch.h"
|
||||
|
||||
#define USAGE_TEMPLATE_STR \
|
||||
"usage: %s oldfile newfile patchfile [old-extents new-extents]\n" \
|
||||
"with extents taking the form \"off_1:len_1,...,off_n:len_n\"\n"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
const char* old_extents = NULL;
|
||||
const char* new_extents = NULL;
|
||||
|
||||
if ((argc != 6) && (argc != 4))
|
||||
errx(1, USAGE_TEMPLATE_STR, argv[0]);
|
||||
|
||||
if (argc == 6) {
|
||||
old_extents = argv[4];
|
||||
new_extents = argv[5];
|
||||
}
|
||||
|
||||
return bspatch(argv[1], argv[2], argv[3], old_extents, new_extents);
|
||||
}
|
||||
@@ -1,15 +1,19 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "exfile.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "exfile.h"
|
||||
|
||||
/*
|
||||
* Extent files implementation. Some things worth noting:
|
||||
*
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#ifndef __EXFILE_H
|
||||
#define __EXFILE_H
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef _BSDIFF_EXFILE_H_
|
||||
#define _BSDIFF_EXFILE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -46,4 +50,4 @@ FILE *exfile_fopen(const char *path, const char *fopen_mode, ex_t *ex_arr,
|
||||
FILE *exfile_fdopen(int fd, const char *fopen_mode, ex_t *ex_arr,
|
||||
size_t ex_count, void (*ex_free)(void *));
|
||||
|
||||
#endif /* __EXFILE_H */
|
||||
#endif /* _BSDIFF_EXFILE_H_ */
|
||||
@@ -1,11 +1,15 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "extents.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "extents.h"
|
||||
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#ifndef __EXTENTS_H
|
||||
#define __EXTENTS_H
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef _BSDIFF_EXTENTS_H_
|
||||
#define _BSDIFF_EXTENTS_H_
|
||||
|
||||
#include "exfile.h"
|
||||
|
||||
@@ -19,4 +23,4 @@
|
||||
* deallocated with free(3). */
|
||||
ex_t *extents_parse(const char *ex_str, ex_t *ex_arr, size_t *ex_count_p);
|
||||
|
||||
#endif /* __EXTENTS_H */
|
||||
#endif /* _BSDIFF_EXTENTS_H_ */
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "test_utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
|
||||
// If |path| is absolute, or explicit relative to the current working directory,
|
||||
// leaves it as is. Otherwise, if TMPDIR is defined in the environment and is
|
||||
// non-empty, prepends it to |path|. Otherwise, prepends /tmp. Returns the
|
||||
// resulting path.
|
||||
const string PrependTmpdir(const string& path) {
|
||||
if (path[0] == '/')
|
||||
return path;
|
||||
|
||||
const char *tmpdir = getenv("TMPDIR");
|
||||
const string prefix = (tmpdir && *tmpdir ? tmpdir : "/tmp");
|
||||
return prefix + "/" + path;
|
||||
}
|
||||
|
||||
bool MakeTempFile(const string& base_filename_template,
|
||||
string* filename) {
|
||||
const string filename_template = PrependTmpdir(base_filename_template);
|
||||
vector<char> result(filename_template.size() + 1, '\0');
|
||||
memcpy(result.data(), filename_template.data(), filename_template.size());
|
||||
|
||||
int mkstemp_fd = mkstemp(result.data());
|
||||
if (mkstemp_fd < 0) {
|
||||
perror("mkstemp()");
|
||||
return false;
|
||||
}
|
||||
close(mkstemp_fd);
|
||||
|
||||
if (filename)
|
||||
*filename = result.data();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace test_utils {
|
||||
|
||||
bool ReadFile(const string& path, vector<uint8_t>* out) {
|
||||
FILE* fp = fopen(path.c_str(), "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
out->clear();
|
||||
|
||||
uint8_t buf[16 * 1024];
|
||||
while (true) {
|
||||
size_t bytes_read = fread(buf, 1, sizeof(buf), fp);
|
||||
if (!bytes_read)
|
||||
break;
|
||||
out->insert(out->end(), buf, buf + bytes_read);
|
||||
}
|
||||
bool result = !ferror(fp);
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WriteFile(const string& path, vector<uint8_t> contents) {
|
||||
FILE* fp = fopen(path.c_str(), "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
size_t written = fwrite(contents.data(), 1, contents.size(), fp);
|
||||
bool result = written == contents.size() && !ferror(fp);
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScopedTempFile::ScopedTempFile(const string& pattern) {
|
||||
EXPECT_TRUE(MakeTempFile(pattern, &filename_));
|
||||
}
|
||||
|
||||
ScopedTempFile::~ScopedTempFile() {
|
||||
if (!filename_.empty() && unlink(filename_.c_str()) < 0) {
|
||||
perror("Unable to remove temporary file");
|
||||
}
|
||||
}
|
||||
|
||||
bool BsdiffPatchFile::LoadFromFile(const string& filename) {
|
||||
vector<uint8_t> contents;
|
||||
if (!ReadFile(filename, &contents))
|
||||
return false;
|
||||
file_size = contents.size();
|
||||
// Check that the file includes at least the header.
|
||||
TEST_AND_RETURN_FALSE(contents.size() >= kHeaderSize);
|
||||
magic = string(contents.data(), contents.data() + 8);
|
||||
memcpy(&ctrl_len, contents.data() + 8, sizeof(ctrl_len));
|
||||
memcpy(&diff_len, contents.data() + 16, sizeof(diff_len));
|
||||
memcpy(&new_file_len, contents.data() + 24, sizeof(new_file_len));
|
||||
|
||||
TEST_AND_RETURN_FALSE(file_size >= kHeaderSize + ctrl_len + diff_len);
|
||||
extra_len = file_size - kHeaderSize - ctrl_len - diff_len;
|
||||
|
||||
// Sanity check before we attempt to parse the bz2 streams.
|
||||
TEST_AND_RETURN_FALSE(ctrl_len >= 0);
|
||||
TEST_AND_RETURN_FALSE(diff_len >= 0);
|
||||
|
||||
uint8_t* ptr = contents.data() + kHeaderSize;
|
||||
bz2_ctrl = vector<uint8_t>(ptr, ptr + ctrl_len);
|
||||
ptr += ctrl_len;
|
||||
bz2_diff = vector<uint8_t>(ptr, ptr + diff_len);
|
||||
ptr += diff_len;
|
||||
bz2_extra = vector<uint8_t>(ptr, ptr + extra_len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BsdiffPatchFile::IsValid() const {
|
||||
TEST_AND_RETURN_FALSE(ctrl_len >= 0);
|
||||
TEST_AND_RETURN_FALSE(diff_len >= 0);
|
||||
TEST_AND_RETURN_FALSE(new_file_len >= 0);
|
||||
|
||||
// TODO(deymo): Test that the length of the decompressed bz2 streams |diff|
|
||||
// plus |extra| are equal to the |new_file_len|.
|
||||
// TODO(deymo): Test that all the |bz2_ctrl| triplets (x, y, z) have a "x"
|
||||
// and "y" value >= 0 ("z" can be negative).
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace test_utils
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef _BSDIFF_TEST_UTILS_H_
|
||||
#define _BSDIFF_TEST_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define TEST_AND_RETURN_FALSE(_x) \
|
||||
do { \
|
||||
if (!static_cast<bool>(_x)) { \
|
||||
fprintf(stderr, "%s failed.", #_x); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace test_utils {
|
||||
|
||||
// Reads all the contents of the file |path| into |out|. Returns whether it
|
||||
// read up to the end of file.
|
||||
bool ReadFile(const std::string& path, std::vector<uint8_t>* out);
|
||||
|
||||
// Overrides the file |path| with the contents passed in |out|. Returns whether
|
||||
// the operation succeeded.
|
||||
bool WriteFile(const std::string& path, std::vector<uint8_t> contents);
|
||||
|
||||
// Utility class to create and delete a temp file.
|
||||
class ScopedTempFile {
|
||||
public:
|
||||
// Creates a temp file with the passed |pattern|. The pattern should end with
|
||||
// "XXXXXX", that will be replaced with a random string. The file will be
|
||||
// removed when this instance is destroyed.
|
||||
explicit ScopedTempFile(const std::string& pattern);
|
||||
~ScopedTempFile();
|
||||
|
||||
std::string filename() const { return filename_; }
|
||||
const char* c_str() const { return filename_.c_str(); }
|
||||
|
||||
// Releases the temporary file. It will not be deleted when this instance is
|
||||
// destroyed.
|
||||
void release() { filename_.clear(); }
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
};
|
||||
|
||||
// This struct representes a parsed BSDIFF40 file.
|
||||
struct BsdiffPatchFile {
|
||||
static const size_t kHeaderSize = 32;
|
||||
|
||||
// Parses a BSDIFF40 file and stores the contents in the local methods.
|
||||
bool LoadFromFile(const std::string& filename);
|
||||
|
||||
// Returns wheter the patch file is valid.
|
||||
bool IsValid() const;
|
||||
|
||||
// The magic string in the header file. Normally "BSDIFF40".
|
||||
std::string magic;
|
||||
|
||||
// The length of the first (ctrl) bzip2 stream. Negative values are invalid.
|
||||
int64_t ctrl_len = -1;
|
||||
|
||||
// The length of the first (diff) bzip2 stream. Negative values are invalid.
|
||||
int64_t diff_len = -1;
|
||||
|
||||
// The length of the first (diff) bzip2 stream. This value is not stored in
|
||||
// the file, but generated based on the |file_size|.
|
||||
uint64_t extra_len = 0;
|
||||
|
||||
// The length of the new file after applying the patch. Negative values are
|
||||
// invalid.
|
||||
int64_t new_file_len = -1;
|
||||
|
||||
// The three compressed streams.
|
||||
std::vector<uint8_t> bz2_ctrl;
|
||||
std::vector<uint8_t> bz2_diff;
|
||||
std::vector<uint8_t> bz2_extra;
|
||||
|
||||
uint64_t file_size = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace test_utils
|
||||
|
||||
|
||||
#endif // _BSDIFF_TEST_UTILS_H_
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in new issue
Block a user