From 329a79fd9ac4d2292802a6d3fed1c64dda78bf59 Mon Sep 17 00:00:00 2001 From: Thomas Osterbind Date: Tue, 16 Jan 2018 13:31:03 -0500 Subject: [PATCH] feat: initial commit --- .circleci/config.yml | 31 ++++++++ .circleci/cover.test.sh | 9 +++ README.md | 8 ++ codecov.yml | 9 +++ datasetDiffer.go | 49 +++++++++++++ datasetDiffer_test.go | 73 +++++++++++++++++++ testdata/exampleData_blankData.json | 6 ++ testdata/exampleData_blankStructure.json | 6 ++ testdata/exampleData_newData.json | 6 ++ testdata/exampleData_newDataAndStructure.json | 6 ++ testdata/exampleData_newStructure.json | 6 ++ testdata/exampleData_orig.json | 6 ++ 12 files changed, 215 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .circleci/cover.test.sh create mode 100644 codecov.yml create mode 100644 datasetDiffer.go create mode 100644 datasetDiffer_test.go create mode 100644 testdata/exampleData_blankData.json create mode 100644 testdata/exampleData_blankStructure.json create mode 100644 testdata/exampleData_newData.json create mode 100644 testdata/exampleData_newDataAndStructure.json create mode 100644 testdata/exampleData_newStructure.json create mode 100644 testdata/exampleData_orig.json diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..bf89bfb --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +version: '2' +jobs: + build: + working_directory: /go/src/github.com/qri-io/datasetDiffer + docker: + - image: circleci/golang:1.9 + environment: + GOLANG_ENV: test + PORT: 3000 + environment: + TEST_RESULTS: /tmp/test-results + steps: + - checkout + - run: mkdir -p $TEST_RESULTS + - run: go get github.com/jstemmer/go-junit-report github.com/golang/lint/golint + - run: + name: Install deps + command: > + go get -v -d -u + github.com/jstemmer/go-junit-report + - run: + name: Run Lint Tests + command: golint ./... + - run: + name: Run Tests + command: | + trap "go-junit-report <${TEST_RESULTS}/go-test.out > ${TEST_RESULTS}/go-test-report.xml" EXIT + ./.circleci/cover.test.sh | tee ${TEST_RESULTS}/go-test.out + - run: + name: Publish coverage info to codecov.io + command: bash <(curl -s https://codecov.io/bash) diff --git a/.circleci/cover.test.sh b/.circleci/cover.test.sh new file mode 100644 index 0000000..e28c5de --- /dev/null +++ b/.circleci/cover.test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt +go test -v -race -coverprofile=profile.out -covermode=atomic github.com/qri-io/datasetDiffer +if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out +fi \ No newline at end of file diff --git a/README.md b/README.md index 41fe8d3..658e80e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ +[![Qri](https://img.shields.io/badge/made%20by-qri-magenta.svg?style=flat-square)](https://qri.io) +[![GoDoc](https://godoc.org/github.com/qri-io/datasetDiffer?status.svg)](http://godoc.org/github.com/qri-io/datasetDiffer) +[![License](https://img.shields.io/github/license/qri-io/datasetDiffer.svg?style=flat-square)](./LICENSE) +[![Codecov](https://img.shields.io/codecov/c/github/qri-io/datasetDiffer.svg?style=flat-square)](https://codecov.io/gh/qri-io/datasetDiffer) +[![CI](https://img.shields.io/circleci/project/github/qri-io/datasetDiffer.svg?style=flat-square)](https://circleci.com/gh/qri-io/datasetDiffer) +[![Go Report Card](https://goreportcard.com/badge/github.com/qri-io/datasetDiffer)](https://goreportcard.com/report/github.com/qri-io/datasetDiffer) + # datasetDiffer + Utility for Diffing Datasets, currently a very basic placeholder diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..1a90176 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +codecov: + ci: + - "ci/circle-ci" + notify: + require_ci_to_pass: no + after_n_builds: 2 +coverage: + range: "80...100" +comment: off \ No newline at end of file diff --git a/datasetDiffer.go b/datasetDiffer.go new file mode 100644 index 0000000..62291cb --- /dev/null +++ b/datasetDiffer.go @@ -0,0 +1,49 @@ +package datasetDiffer + +import ( + "fmt" + + "github.com/qri-io/dataset" +) + +// Diff contains a string description of a diff +type Diff string + +// DiffList contains a slice of diffs in order of descending scope +type DiffList struct { + diffs []Diff +} + +// String returns the first (largest scope) change as a string +func (diffList DiffList) String() string { + if len(diffList.diffs) > 0 { + return string(diffList.diffs[0]) + } + return "" +} + +// DiffDatasets calculates diffs between two datasets and returns a +// dataset. Differences are checked in order of descending scope +// - dataset.Dataset.path +// - dataset.Dataset.Structure.path +// - dataset.Dataset.Data.path +// TODO: make diffs non-trivial +func DiffDatasets(a, b *dataset.Dataset) (*DiffList, error) { + diffList := &DiffList{} + diffDescription := Diff("") + if len(a.Structure.Path().String()) <= 1 || len(b.Structure.Path().String()) <= 1 { + return nil, fmt.Errorf("error: structure path cannot be empty string") + } + if len(a.DataPath) <= 1 || len(b.DataPath) <= 1 { + return nil, fmt.Errorf("error: data path cannot be empty string") + } + if a.Structure.Path() != b.Structure.Path() { + diffDescription = Diff("Structure Changed.") + diffList.diffs = append(diffList.diffs, diffDescription) + } + if a.DataPath != b.DataPath { + diffDescription = Diff("Data Changed.") + diffList.diffs = append(diffList.diffs, diffDescription) + } + return diffList, nil +} diff --git a/datasetDiffer_test.go b/datasetDiffer_test.go new file mode 100644 index 0000000..6e8c1b1 --- /dev/null +++ b/datasetDiffer_test.go @@ -0,0 +1,73 @@ +package datasetDiffer + +import ( + "fmt" + "io/ioutil" + // "path/filepath" + "testing" + // "github.com/qri-io/qri" + // testrepo "github.com/qri-io/qri/repo/test" + "github.com/qri-io/dataset" +) + +func loadTestData(path string) (*dataset.Dataset, error) { + dataBytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + d := &dataset.Dataset{} + err = d.UnmarshalJSON(dataBytes) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal dataset: %s", err.Error()) + } + return d, nil +} + +func TestDiffDataset(t *testing.T) { + //setup + + //test cases + cases := []struct { + dsPathA string + dsPathB string + expected string + err string + }{ + {"testdata/exampleData_orig.json", "testdata/exampleData_orig.json", "", ""}, + {"testdata/exampleData_orig.json", "testdata/exampleData_newData.json", "Data Changed.", ""}, + {"testdata/exampleData_orig.json", "testdata/exampleData_newStructure.json", "Structure Changed.", ""}, + {"testdata/exampleData_orig.json", "testdata/exampleData_newDataAndStructure.json", "Structure Changed.", ""}, + {"testdata/exampleData_orig.json", "testdata/exampleData_blankStructure.json", "", "error: structure path cannot be empty string"}, + {"testdata/exampleData_orig.json", "testdata/exampleData_blankData.json", "", "error: data path cannot be empty string"}, + } + // load files and execute tests + for i, c := range cases { + pathA := c.dsPathA + pathB := c.dsPathB + //load data + dsA, err := loadTestData(pathA) + if err != nil { + t.Errorf("case %d error: error loading file '%s'", i, pathA) + return + } + dsB, err := loadTestData(pathB) + if err != nil { + t.Errorf("case %d error: error loading file '%s'", i, pathB) + return + } + got, err := DiffDatasets(dsA, dsB) + if err != nil { + if err.Error() == c.err { + continue + } else { + t.Errorf("case %d error mismatch: expected '%s', got '%s'", i, c.err, err.Error()) + return + } + + } + if got.String() != c.expected { + t.Errorf("case %d response mismatch. expected '%s', got '%s'", i, c.expected, got.String()) + continue + } + } +} diff --git a/testdata/exampleData_blankData.json b/testdata/exampleData_blankData.json new file mode 100644 index 0000000..9f76dc3 --- /dev/null +++ b/testdata/exampleData_blankData.json @@ -0,0 +1,6 @@ +{ + "kind": "qri:ds:0", + "DataPath": "", + "path": "123", + "structure": "abc" +} \ No newline at end of file diff --git a/testdata/exampleData_blankStructure.json b/testdata/exampleData_blankStructure.json new file mode 100644 index 0000000..7463d45 --- /dev/null +++ b/testdata/exampleData_blankStructure.json @@ -0,0 +1,6 @@ +{ + "kind": "qri:ds:0", + "DataPath": "def", + "path": "123", + "structure": "" +} \ No newline at end of file diff --git a/testdata/exampleData_newData.json b/testdata/exampleData_newData.json new file mode 100644 index 0000000..1fffee8 --- /dev/null +++ b/testdata/exampleData_newData.json @@ -0,0 +1,6 @@ +{ + "kind": "qri:ds:0", + "DataPath": "def", + "path": "123", + "structure": "abc" +} \ No newline at end of file diff --git a/testdata/exampleData_newDataAndStructure.json b/testdata/exampleData_newDataAndStructure.json new file mode 100644 index 0000000..b4edfc5 --- /dev/null +++ b/testdata/exampleData_newDataAndStructure.json @@ -0,0 +1,6 @@ +{ + "kind": "qri:ds:0", + "DataPath": "abc......", + "path": "123", + "structure": "abc......" +} \ No newline at end of file diff --git a/testdata/exampleData_newStructure.json b/testdata/exampleData_newStructure.json new file mode 100644 index 0000000..a37434e --- /dev/null +++ b/testdata/exampleData_newStructure.json @@ -0,0 +1,6 @@ +{ + "kind": "qri:ds:0", + "DataPath": "abc", + "path": "123", + "structure": "def" +} \ No newline at end of file diff --git a/testdata/exampleData_orig.json b/testdata/exampleData_orig.json new file mode 100644 index 0000000..b7c5622 --- /dev/null +++ b/testdata/exampleData_orig.json @@ -0,0 +1,6 @@ +{ + "kind": "qri:ds:0", + "DataPath": "abc", + "path": "123", + "structure": "abc" +}