Repository: dragonflyoss/Dragonfly2 (go)

Issue:
Write wrong task piece info
### Bug report:
When the length of the file to be downloaded happens to be an integer multiple of the shard, the shard information stored is incorrectly written
<!-- Please describe what is actually happening -->
when file size 
### Expected behavior:

<!-- Please describe what you expect to happen -->

### How to reproduce it:
eg:  when file length is 33554432 and piece length is 4194304, there should not be a piece numbered 8
![image](https://user-images.githubusercontent.com/16535906/123781397-c69ad880-d906-11eb-9277-84c96f379a3d.png)

<!-- How can a maintainer reproduce this issue (please be detailed) -->

### Environment:
- Dragonfly version:
- OS:
- Kernel (e.g. `uname -a`):
- Others:


Pull request description:
Repair wrong piece info
<!--- Provide a general summary of your changes in the Title above -->

## Description
When the length of the file to be downloaded happens to be an integer multiple of the shard, the shard information stored is incorrectly written
<!--- Describe your changes in detail -->

## Related Issue
fix #381 
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate):

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] Code compiles correctly.
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.


Patch:
diff --git a/cdnsystem/daemon/cdn/cache_detector.go b/cdnsystem/daemon/cdn/cache_detector.go
index c4b7258e..aff489de 100644
--- a/cdnsystem/daemon/cdn/cache_detector.go
+++ b/cdnsystem/daemon/cdn/cache_detector.go
@@ -89,7 +89,7 @@ func (cd *cacheDetector) doDetect(task *types.SeedTask, fileMd5 hash.Hash) (resu
 		return nil, errors.Wrapf(err, "read file meta data of task %s", task.TaskID)
 	}
 	if err := checkSameFile(task, fileMetaData); err != nil {
-		return nil, errors.Wrapf(err, "task does not match meta information of task file")
+		return nil, errors.Wrapf(err, "check same file")
 	}
 	ctx, expireCancel := context.WithTimeout(context.Background(), 4*time.Second)
 	defer expireCancel()
@@ -100,8 +100,7 @@ func (cd *cacheDetector) doDetect(task *types.SeedTask, fileMd5 hash.Hash) (resu
 	}
 	logger.WithTaskID(task.TaskID).Debugf("task expired result: %t", expired)
 	if expired {
-		return nil, errors.Wrapf(cdnerrors.ErrResourceExpired, "url:%s, expireInfo:%+v", task.URL,
-			fileMetaData.ExpireInfo)
+		return nil, cdnerrors.ErrResourceExpired{URL: task.URL}
 	}
 	// not expired
 	if fileMetaData.Finish {
@@ -117,7 +116,7 @@ func (cd *cacheDetector) doDetect(task *types.SeedTask, fileMd5 hash.Hash) (resu
 		return nil, errors.Wrapf(err, "check if url(%s) supports range request", task.URL)
 	}
 	if !supportRange {
-		return nil, errors.Wrapf(cdnerrors.ErrResourceNotSupportRangeRequest, "url:%s", task.URL)
+		return nil, cdnerrors.ErrResourceNotSupportRangeRequest{URL: task.URL}
 	}
 	return cd.parseByReadFile(task.TaskID, fileMetaData, fileMd5)
 }
@@ -125,15 +124,15 @@ func (cd *cacheDetector) doDetect(task *types.SeedTask, fileMd5 hash.Hash) (resu
 // parseByReadMetaFile detect cache by read meta and pieceMeta files of task
 func (cd *cacheDetector) parseByReadMetaFile(taskID string, fileMetaData *storage.FileMetaData) (*cacheResult, error) {
 	if !fileMetaData.Success {
-		return nil, errors.Wrapf(cdnerrors.ErrDownloadFail, "success flag of download is false")
+		return nil, fmt.Errorf("success flag of taskID %s is false", taskID)
 	}
 	pieceMetaRecords, err := cd.cacheDataManager.readAndCheckPieceMetaRecords(taskID, fileMetaData.PieceMd5Sign)
 	if err != nil {
 		return nil, errors.Wrapf(err, "check piece meta integrity")
 	}
 	if fileMetaData.TotalPieceCount > 0 && len(pieceMetaRecords) != int(fileMetaData.TotalPieceCount) {
-		return nil, errors.Wrapf(cdnerrors.ErrPieceCountNotEqual, "piece file piece count(%d), "+
-			"meta file piece count(%d)", len(pieceMetaRecords), fileMetaData.TotalPieceCount)
+		err := cdnerrors.ErrInconsistentValues{Expected: fileMetaData.TotalPieceCount, Actual: len(pieceMetaRecords)}
+		return nil, errors.Wrapf(err, "compare file piece count")
 	}
 	storageInfo, err := cd.cacheDataManager.statDownloadFile(taskID)
 	if err != nil {
@@ -141,8 +140,11 @@ func (cd *cacheDetector) parseByReadMetaFile(taskID string, fileMetaData *storag
 	}
 	// check file data integrity by file size
 	if fileMetaData.CdnFileLength != storageInfo.Size {
-		return nil, errors.Wrapf(cdnerrors.ErrFileLengthNotEqual, "meta size %d, storage size %d",
-			fileMetaData.CdnFileLength, storageInfo.Size)
+		err := cdnerrors.ErrInconsistentValues{
+			Expected: fileMetaData.CdnFileLength,
+			Actual:   storageInfo.Size,
+		}
+		return nil, errors.Wrapf(err, "compare file cdn file length")
 	}
 	return &cacheResult{
 		breakPoint:       -1,
@@ -249,13 +251,16 @@ func checkPieceContent(reader io.Reader, pieceRecord *storage.PieceMetaRecord, f
 	pieceMd5 := md5.New()
 	tee := io.TeeReader(io.TeeReader(io.LimitReader(reader, int64(pieceRecord.PieceLen)), pieceMd5), fileMd5)
 	if n, err := io.Copy(ioutil.Discard, tee); n != int64(pieceRecord.PieceLen) || err != nil {
-		return fmt.Errorf("read piece content: %v", err)
+		return errors.Wrap(err, "read piece content")
 	}
 	realPieceMd5 := digestutils.ToHashString(pieceMd5)
 	// check piece content
 	if realPieceMd5 != pieceRecord.Md5 {
-		return errors.Wrapf(cdnerrors.ErrPieceMd5NotMatch, "realPieceMd5 md5 (%s), expected md5 (%s)",
-			realPieceMd5, pieceRecord.Md5)
+		err := cdnerrors.ErrInconsistentValues{
+			Expected: pieceRecord.Md5,
+			Actual:   realPieceMd5,
+		}
+		return errors.Wrap(err, "compare piece md5")
 	}
 	return nil
 }
diff --git a/cdnsystem/daemon/cdn/cache_writer.go b/cdnsystem/daemon/cdn/cache_writer.go
index 60eed651..6b44ae7f 100644
--- a/cdnsystem/daemon/cdn/cache_writer.go
+++ b/cdnsystem/daemon/cdn/cache_writer.go
@@ -75,12 +75,12 @@ func (cw *cacheWriter) startWriter(reader io.Reader, task *types.SeedTask, detec
 	}
 	storageInfo, err := cw.cacheDataManager.statDownloadFile(task.TaskID)
 	if err != nil {
-		return &downloadMetadata{backSourceLength: backSourceFileLength}, fmt.Errorf("stat cdn download file: %v", storageInfo)
+		return &downloadMetadata{backSourceLength: backSourceFileLength}, fmt.Errorf("stat cdn download file: %v", err)
 	}
 	// todo Try getting it from the ProgressManager first
 	pieceMd5Sign, _, err := cw.cacheDataManager.getPieceMd5Sign(task.TaskID)
 	if err != nil {
-		return &downloadMetadata{backSourceLength: backSourceFileLength}, fmt.Errorf("get piece md5 sign: %v", pieceMd5Sign)
+		return &downloadMetadata{backSourceLength: backSourceFileLength}, fmt.Errorf("get piece md5 sign: %v", err)
 	}
 	return &downloadMetadata{
 		backSourceLength:     backSourceFileLength,
@@ -111,6 +111,9 @@ func (cw *cacheWriter) doWrite(reader io.Reader, task *types.SeedTask, routineCo
 			close(jobCh)
 			return backSourceFileLength, 0, fmt.Errorf("read source taskID %s pieceNum %d piece: %v", task.TaskID, curPieceNum, err)
 		}
+		if n == 0 {
+			break
+		}
 		backSourceFileLength = backSourceFileLength + n
 
 		jobCh <- &piece{
diff --git a/cdnsystem/daemon/task/manager_util.go b/cdnsystem/daemon/task/manager_util.go
index 82debd94..a4a1395e 100644
--- a/cdnsystem/daemon/task/manager_util.go
+++ b/cdnsystem/daemon/task/manager_util.go
@@ -48,8 +48,8 @@ func (tm *Manager) addOrUpdateTask(ctx context.Context, request *types.TaskRegis
 	if key, err := tm.taskURLUnReachableStore.Get(taskID); err == nil {
 		if unReachableStartTime, ok := key.(time.Time); ok &&
 			time.Since(unReachableStartTime) < tm.cfg.FailAccessInterval {
-			return nil, cdnerrors.ErrURLNotReachable{URL: request.URL, Cause: fmt.Errorf("task hit unReachable cache and interval less than %d, url: %s",
-				tm.cfg.FailAccessInterval, request.URL)}
+			return nil, errors.Wrapf(cdnerrors.ErrURLNotReachable{URL: request.URL}, "task hit unReachable cache and interval less than %d, "+
+				"url: %s", tm.cfg.FailAccessInterval, request.URL)
 		}
 		tm.taskURLUnReachableStore.Delete(taskID)
 		logger.Debugf("delete taskID:%s from url unReachable store", taskID)
diff --git a/cdnsystem/errors/errors.go b/cdnsystem/errors/errors.go
index fe08e282..0de97f0e 100644
--- a/cdnsystem/errors/errors.go
+++ b/cdnsystem/errors/errors.go
@@ -24,12 +24,11 @@ import (
 
 // ErrURLNotReachable represents the url is a not reachable.
 type ErrURLNotReachable struct {
-	URL   string
-	Cause error
+	URL string
 }
 
 func (e ErrURLNotReachable) Error() string {
-	return fmt.Sprintf("url %s not reachable: %v", e.URL, e.Cause)
+	return fmt.Sprintf("url %s not reachable", e.URL)
 }
 
 // ErrTaskIDDuplicate represents the task id is in conflict.
@@ -42,42 +41,57 @@ func (e ErrTaskIDDuplicate) Error() string {
 	return fmt.Sprintf("taskId %s conflict: %v", e.TaskID, e.Cause)
 }
 
-var (
-	// ErrSystemError represents the error is a system error.
-	ErrSystemError = errors.New("system error")
+type ErrInconsistentValues struct {
+	Expected interface{}
+	Actual   interface{}
+}
 
-	// ErrPieceCountNotEqual represents the number of pieces downloaded does not match the amount of meta information
-	ErrPieceCountNotEqual = errors.New("inconsistent number of pieces")
+func (e ErrInconsistentValues) Error() string {
+	return fmt.Sprintf("inconsistent number of pieces, expected %s, actual: %s", e.Expected, e.Actual)
+}
 
-	// ErrFileLengthNotEqual represents the file length of downloaded dose not match the length of meta information
-	ErrFileLengthNotEqual = errors.New("inconsistent file length")
+// ErrResourceExpired represents the downloaded resource has expired
+type ErrResourceExpired struct {
+	URL string
+}
 
-	// ErrDownloadFail represents an exception was encountered while downloading the file
-	ErrDownloadFail = errors.New("resource download failed")
+func (e ErrResourceExpired) Error() string {
+	return fmt.Sprintf("url %s expired", e.URL)
+}
 
-	// ErrResourceExpired represents the downloaded resource has expired
-	ErrResourceExpired = errors.New("resource expired")
+// ErrResourceNotSupportRangeRequest represents the downloaded resource does not support Range downloads
+type ErrResourceNotSupportRangeRequest struct {
+	URL string
+}
 
-	// ErrResourceNotSupportRangeRequest represents the downloaded resource does not support Range downloads
-	ErrResourceNotSupportRangeRequest = errors.New("resource does not support range request")
+func (e ErrResourceNotSupportRangeRequest) Error() string {
+	return fmt.Sprintf("url %s does not support range request", e.URL)
+}
+
+// ErrFileNotExist represents the file is not exists
+type ErrFileNotExist struct {
+	File string
+}
+
+func (e ErrFileNotExist) Error() string {
+	return fmt.Sprintf("file or dir %s not exist", e.File)
+}
+
+var (
+	// ErrSystemError represents the error is a system error.
+	ErrSystemError = errors.New("system error")
 
-	// ErrPieceMd5NotMatch represents the MD5 value of the download file is inconsistent with the meta information
-	ErrPieceMd5NotMatch = errors.New("piece md5 check fail")
+	// ErrTaskDownloadFail represents an exception was encountered while downloading the file
+	ErrTaskDownloadFail = errors.New("resource download failed")
 
 	// ErrDataNotFound represents the data cannot be found.
 	ErrDataNotFound = errors.New("data not found")
 
-	// ErrFileNotExist represents the file is not exists
-	ErrFileNotExist = errors.New("file or directory not exist")
-
 	// ErrInvalidValue represents the value is invalid.
 	ErrInvalidValue = errors.New("invalid value")
 
 	// ErrConvertFailed represents failed to convert.
 	ErrConvertFailed = errors.New("convert failed")
-
-	// ErrRangeNotSatisfiable represents the length of file is insufficient.
-	ErrRangeNotSatisfiable = errors.New("range not satisfiable")
 )
 
 // IsSystemError checks the error is a system error or not.
@@ -99,28 +113,26 @@ func IsTaskIDDuplicate(err error) bool {
 	return ok
 }
 
-func IsPieceCountNotEqual(err error) bool {
-	return errors.Cause(err) == ErrPieceCountNotEqual
-}
-
-func IsFileLengthNotEqual(err error) bool {
-	return errors.Cause(err) == ErrFileLengthNotEqual
+func IsInconsistentValues(err error) bool {
+	err = errors.Cause(err)
+	_, ok := err.(ErrInconsistentValues)
+	return ok
 }
 
 func IsDownloadFail(err error) bool {
-	return errors.Cause(err) == ErrDownloadFail
+	return errors.Cause(err) == ErrTaskDownloadFail
 }
 
 func IsResourceExpired(err error) bool {
-	return errors.Cause(err) == ErrResourceExpired
+	err = errors.Cause(err)
+	_, ok := err.(ErrResourceExpired)
+	return ok
 }
 
 func IsResourceNotSupportRangeRequest(err error) bool {
-	return errors.Cause(err) == ErrResourceNotSupportRangeRequest
-}
-
-func IsPieceMd5NotMatch(err error) bool {
-	return errors.Cause(err) == ErrPieceMd5NotMatch
+	err = errors.Cause(err)
+	_, ok := err.(ErrResourceNotSupportRangeRequest)
+	return ok
 }
 
 func IsDataNotFound(err error) bool {
@@ -135,10 +147,8 @@ func IsConvertFailed(err error) bool {
 	return errors.Cause(err) == ErrConvertFailed
 }
 
-func IsRangeNotSatisfiable(err error) bool {
-	return errors.Cause(err) == ErrRangeNotSatisfiable
-}
-
 func IsFileNotExist(err error) bool {
-	return errors.Cause(err) == ErrFileNotExist
+	err = errors.Cause(err)
+	_, ok := err.(ErrFileNotExist)
+	return ok
 }
diff --git a/cdnsystem/storedriver/local/local_driver.go b/cdnsystem/storedriver/local/local_driver.go
index 9955422d..ea65930e 100644
--- a/cdnsystem/storedriver/local/local_driver.go
+++ b/cdnsystem/storedriver/local/local_driver.go
@@ -30,7 +30,6 @@ import (
 	"d7y.io/dragonfly/v2/pkg/unit"
 	"d7y.io/dragonfly/v2/pkg/util/fileutils"
 	"d7y.io/dragonfly/v2/pkg/util/statutils"
-	"github.com/pkg/errors"
 )
 
 // Ensure driver implements the storedriver.Driver interface
@@ -368,7 +367,7 @@ func (ds *driver) statPath(bucket, key string) (string, os.FileInfo, error) {
 	f, err := os.Stat(filePath)
 	if err != nil {
 		if os.IsNotExist(err) {
-			return "", nil, errors.Wrapf(cdnerrors.ErrFileNotExist, "no such file or directory:%s exists", filePath)
+			return "", nil, cdnerrors.ErrFileNotExist{File: "filePath"}
 		}
 		return "", nil, err
 	}
diff --git a/pkg/source/httpprotocol/http_source_client.go b/pkg/source/httpprotocol/http_source_client.go
index c74da521..485e0e08 100644
--- a/pkg/source/httpprotocol/http_source_client.go
+++ b/pkg/source/httpprotocol/http_source_client.go
@@ -24,7 +24,6 @@ import (
 	"net/http"
 	"time"
 
-	cdnerrors "d7y.io/dragonfly/v2/cdnsystem/errors"
 	"d7y.io/dragonfly/v2/pkg/source"
 	"d7y.io/dragonfly/v2/pkg/structure/maputils"
 	"d7y.io/dragonfly/v2/pkg/util/stringutils"
@@ -81,13 +80,13 @@ func WithHTTPClient(client *http.Client) HTTPSourceClientOption {
 func (client *httpSourceClient) GetContentLength(ctx context.Context, url string, header source.RequestHeader) (int64, error) {
 	resp, err := client.doRequest(ctx, http.MethodGet, url, header)
 	if err != nil {
-		return -1, cdnerrors.ErrURLNotReachable{URL: url, Cause: err}
+		return -1, err
 	}
 	resp.Body.Close()
 	// todo Here if other status codes should be added to ErrURLNotReachable, if not, it will be downloaded frequently for 404 or 403
 	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
 		// todo Whether this situation should be distinguished from the err situation, similar to proposing another error type to indicate that this  error can interact with the URL, but the status code does not meet expectations
-		return -1, cdnerrors.ErrURLNotReachable{URL: url, Cause: fmt.Errorf("get http resource length failed, unexpected code: %d", resp.StatusCode)}
+		return -1, fmt.Errorf("get http resource length failed, unexpected code: %d", resp.StatusCode)
 	}
 	return resp.ContentLength, nil
 }
diff --git a/pkg/source/ossprotocol/oss_source_client.go b/pkg/source/ossprotocol/oss_source_client.go
index 20a77093..5775a63f 100644
--- a/pkg/source/ossprotocol/oss_source_client.go
+++ b/pkg/source/ossprotocol/oss_source_client.go
@@ -178,19 +178,19 @@ func (osc *ossSourceClient) getMeta(ctx context.Context, url string, header map[
 	}
 	ossObject, err := parseOssObject(url)
 	if err != nil {
-		return nil, cdnerrors.ErrURLNotReachable{URL: url, Cause: fmt.Errorf("parse oss object: %v", err)}
+		return nil, errors.Wrapf(err, "parse oss object")
 	}
 
 	bucket, err := client.Bucket(ossObject.bucket)
 	if err != nil {
-		return nil, cdnerrors.ErrURLNotReachable{URL: url, Cause: fmt.Errorf("get bucket:%s: %v", ossObject.bucket, err)}
+		return nil, errors.Wrapf(err, "get bucket:%s", ossObject.bucket)
 	}
 	isExist, err := bucket.IsObjectExist(ossObject.object)
 	if err != nil {
-		return nil, cdnerrors.ErrURLNotReachable{URL: url, Cause: fmt.Errorf("prob object:%s exist: %v", ossObject.object, err)}
+		return nil, errors.Wrapf(err, "prob object:%s if exist", ossObject.object)
 	}
 	if !isExist {
-		return nil, cdnerrors.ErrURLNotReachable{URL: url, Cause: fmt.Errorf("oss object:%s does not exist", ossObject.object)}
+		return nil, fmt.Errorf("oss object:%s does not exist", ossObject.object)
 	}
 	return bucket.GetObjectMeta(ossObject.object, getOptions(header)...)
 }


Test patch:
diff --git a/cdnsystem/daemon/cdn/cache_detector_test.go b/cdnsystem/daemon/cdn/cache_detector_test.go
index 1f02878a..24ef6bcf 100644
--- a/cdnsystem/daemon/cdn/cache_detector_test.go
+++ b/cdnsystem/daemon/cdn/cache_detector_test.go
@@ -59,7 +59,7 @@ func (suite *CacheDetectorTestSuite) SetupSuite() {
 	storageMgr.EXPECT().ReadFileMetaData(fullNoExpiredCache.taskID).Return(fullNoExpiredCache.fileMeta, nil).AnyTimes()
 	storageMgr.EXPECT().ReadFileMetaData(partialNotSupportRangeCache.taskID).Return(partialNotSupportRangeCache.fileMeta, nil).AnyTimes()
 	storageMgr.EXPECT().ReadFileMetaData(partialSupportRangeCache.taskID).Return(partialSupportRangeCache.fileMeta, nil).AnyTimes()
-	storageMgr.EXPECT().ReadFileMetaData(noCache.taskID).Return(noCache.fileMeta, cdnerrors.ErrFileNotExist).AnyTimes()
+	storageMgr.EXPECT().ReadFileMetaData(noCache.taskID).Return(noCache.fileMeta, cdnerrors.ErrFileNotExist{}).AnyTimes()
 	storageMgr.EXPECT().ReadDownloadFile(fullNoExpiredCache.taskID).DoAndReturn(
 		func(taskID string) (io.ReadCloser, error) {
 			content, err := ioutil.ReadFile("../../testdata/cdn/go.html")
@@ -78,11 +78,11 @@ func (suite *CacheDetectorTestSuite) SetupSuite() {
 			suite.Nil(err)
 			return ioutil.NopCloser(strings.NewReader(string(content))), nil
 		}).AnyTimes()
-	storageMgr.EXPECT().ReadDownloadFile(noCache.taskID).Return(nil, cdnerrors.ErrFileNotExist).AnyTimes()
+	storageMgr.EXPECT().ReadDownloadFile(noCache.taskID).Return(nil, cdnerrors.ErrFileNotExist{}).AnyTimes()
 	storageMgr.EXPECT().ReadPieceMetaRecords(fullNoExpiredCache.taskID).Return(fullNoExpiredCache.pieces, nil).AnyTimes()
 	storageMgr.EXPECT().ReadPieceMetaRecords(partialNotSupportRangeCache.taskID).Return(partialNotSupportRangeCache.pieces, nil).AnyTimes()
 	storageMgr.EXPECT().ReadPieceMetaRecords(partialSupportRangeCache.taskID).Return(partialSupportRangeCache.pieces, nil).AnyTimes()
-	storageMgr.EXPECT().ReadPieceMetaRecords(noCache.taskID).Return(nil, cdnerrors.ErrFileNotExist).AnyTimes()
+	storageMgr.EXPECT().ReadPieceMetaRecords(noCache.taskID).Return(nil, cdnerrors.ErrFileNotExist{}).AnyTimes()
 	storageMgr.EXPECT().StatDownloadFile(fullNoExpiredCache.taskID).Return(&storedriver.StorageInfo{
 		Path:       "",
 		Size:       9789,
diff --git a/cdnsystem/errors/errors_test.go b/cdnsystem/errors/errors_test.go
index fb6a14b7..aa888868 100644
--- a/cdnsystem/errors/errors_test.go
+++ b/cdnsystem/errors/errors_test.go
@@ -116,13 +116,13 @@ func (s *ErrorTestSuite) TestIsDownloadFail() {
 		{
 			name: "equal",
 			args: args{
-				err: ErrDownloadFail,
+				err: ErrTaskDownloadFail,
 			},
 			want: true,
 		}, {
 			name: "wrap",
 			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrDownloadFail, "wrap err"), "wapp err"),
+				err: errors.Wrapf(errors.Wrapf(ErrTaskDownloadFail, "wrap err"), "wapp err"),
 			},
 			want: true,
 		}, {
@@ -140,42 +140,6 @@ func (s *ErrorTestSuite) TestIsDownloadFail() {
 	}
 }
 
-func (s *ErrorTestSuite) TestIsFileLengthNotEqual() {
-	type args struct {
-		err error
-	}
-	tests := []struct {
-		name string
-		args args
-		want bool
-	}{
-		{
-			name: "equal",
-			args: args{
-				err: ErrFileLengthNotEqual,
-			},
-			want: true,
-		}, {
-			name: "wrap",
-			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrFileLengthNotEqual, "wrap err"), "wapp err"),
-			},
-			want: true,
-		}, {
-			name: "notEqual",
-			args: args{
-				err: errors.Wrapf(ErrInvalidValue, "invaid"),
-			},
-			want: false,
-		},
-	}
-	for _, tt := range tests {
-		s.Run(tt.name, func() {
-			s.Equal(tt.want, IsFileLengthNotEqual(tt.args.err))
-		})
-	}
-}
-
 func (s *ErrorTestSuite) TestIsFileNotExist() {
 	type args struct {
 		err error
@@ -188,13 +152,13 @@ func (s *ErrorTestSuite) TestIsFileNotExist() {
 		{
 			name: "equal",
 			args: args{
-				err: ErrFileNotExist,
+				err: ErrFileNotExist{},
 			},
 			want: true,
 		}, {
 			name: "wrap",
 			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrFileNotExist, "wrap err"), "wapp err"),
+				err: errors.Wrapf(errors.Wrapf(ErrFileNotExist{}, "wrap err"), "wapp err"),
 			},
 			want: true,
 		}, {
@@ -248,79 +212,7 @@ func (s *ErrorTestSuite) TestIsInvalidValue() {
 	}
 }
 
-func (s *ErrorTestSuite) TestIsPieceCountNotEqual() {
-	type args struct {
-		err error
-	}
-	tests := []struct {
-		name string
-		args args
-		want bool
-	}{
-		{
-			name: "equal",
-			args: args{
-				err: ErrPieceCountNotEqual,
-			},
-			want: true,
-		}, {
-			name: "wrap",
-			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrPieceCountNotEqual, "wrap err"), "wapp err"),
-			},
-			want: true,
-		}, {
-			name: "notEqual",
-			args: args{
-				err: errors.Wrapf(ErrInvalidValue, "invaid"),
-			},
-			want: false,
-		},
-	}
-	for _, tt := range tests {
-		s.Run(tt.name, func() {
-			s.Equal(tt.want, IsPieceCountNotEqual(tt.args.err))
-		})
-	}
-}
-
-func (s *ErrorTestSuite) TestIsPieceMd5NotMatch() {
-	type args struct {
-		err error
-	}
-	tests := []struct {
-		name string
-		args args
-		want bool
-	}{
-		{
-			name: "equal",
-			args: args{
-				err: ErrPieceMd5NotMatch,
-			},
-			want: true,
-		}, {
-			name: "wrap",
-			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrPieceMd5NotMatch, "wrap err"), "wapp err"),
-			},
-			want: true,
-		}, {
-			name: "notEqual",
-			args: args{
-				err: errors.Wrapf(ErrInvalidValue, "invaid"),
-			},
-			want: false,
-		},
-	}
-	for _, tt := range tests {
-		s.Run(tt.name, func() {
-			s.Equal(tt.want, IsPieceMd5NotMatch(tt.args.err))
-		})
-	}
-}
-
-func (s *ErrorTestSuite) TestIsRangeNotSatisfiable() {
+func (s *ErrorTestSuite) TestIsInconsistentValues() {
 	type args struct {
 		err error
 	}
@@ -332,13 +224,13 @@ func (s *ErrorTestSuite) TestIsRangeNotSatisfiable() {
 		{
 			name: "equal",
 			args: args{
-				err: ErrRangeNotSatisfiable,
+				err: ErrInconsistentValues{},
 			},
 			want: true,
 		}, {
 			name: "wrap",
 			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrRangeNotSatisfiable, "wrap err"), "wapp err"),
+				err: errors.Wrapf(errors.Wrapf(ErrInconsistentValues{}, "wrap err"), "wapp err"),
 			},
 			want: true,
 		}, {
@@ -351,7 +243,7 @@ func (s *ErrorTestSuite) TestIsRangeNotSatisfiable() {
 	}
 	for _, tt := range tests {
 		s.Run(tt.name, func() {
-			s.Equal(tt.want, IsRangeNotSatisfiable(tt.args.err))
+			s.Equal(tt.want, IsInconsistentValues(tt.args.err))
 		})
 	}
 }
@@ -368,13 +260,13 @@ func (s *ErrorTestSuite) TestIsResourceExpired() {
 		{
 			name: "equal",
 			args: args{
-				err: ErrResourceExpired,
+				err: ErrResourceExpired{},
 			},
 			want: true,
 		}, {
 			name: "wrap",
 			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrResourceExpired, "wrap err"), "wapp err"),
+				err: errors.Wrapf(errors.Wrapf(ErrResourceExpired{}, "wrap err"), "wapp err"),
 			},
 			want: true,
 		}, {
@@ -404,13 +296,13 @@ func (s *ErrorTestSuite) TestIsResourceNotSupportRangeRequest() {
 		{
 			name: "equal",
 			args: args{
-				err: ErrResourceNotSupportRangeRequest,
+				err: ErrResourceNotSupportRangeRequest{},
 			},
 			want: true,
 		}, {
 			name: "wrap",
 			args: args{
-				err: errors.Wrapf(errors.Wrapf(ErrResourceNotSupportRangeRequest, "wrap err"), "wapp err"),
+				err: errors.Wrapf(errors.Wrapf(ErrResourceNotSupportRangeRequest{}, "wrap err"), "wapp err"),
 			},
 			want: true,
 		}, {
@@ -518,8 +410,7 @@ func (s *ErrorTestSuite) TestIsURLNotReachable() {
 			name: "equal",
 			args: args{
 				err: ErrURLNotReachable{
-					URL:   "test",
-					Cause: fmt.Errorf("test"),
+					URL: "test",
 				},
 			},
 			want: true,
@@ -527,8 +418,7 @@ func (s *ErrorTestSuite) TestIsURLNotReachable() {
 			name: "wrap",
 			args: args{
 				err: errors.Wrapf(ErrURLNotReachable{
-					URL:   "test",
-					Cause: fmt.Errorf("test"),
+					URL: "test",
 				}, "wapp err"),
 			},
 			want: true,