You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ha...@apache.org on 2021/06/01 05:40:17 UTC

[skywalking-banyandb] 01/01: Introduce mock system

This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git

commit 649210057f14ac38bdbd114eacdc4e39449c2fe8
Author: Gao Hongtao <ha...@gmail.com>
AuthorDate: Tue Jun 1 10:43:49 2021 +0800

    Introduce mock system
    
    Signed-off-by: Gao Hongtao <ha...@gmail.com>
---
 .github/workflows/go.yml         |  18 +++++-
 .gitignore                       |   4 ++
 .licenserc.yaml                  |   3 +
 Makefile                         |   6 +-
 api/fbs/v1/database_generated.go |  28 ---------
 api/fbs/v1/query_generated.go    |  70 ----------------------
 api/fbs/v1/trace_generated.go    |   7 ---
 banyand/kv/kv.go                 |   1 +
 banyand/storage/database_test.go | 124 ++++++++++++++++++++++-----------------
 banyand/storage/storage.go       |   1 +
 go.mod                           |   1 +
 go.sum                           |   1 +
 scripts/build/lint.mk            |   2 +
 scripts/build/test.mk            |   7 ++-
 14 files changed, 110 insertions(+), 163 deletions(-)

diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 0ecc247..9efc48b 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -39,6 +39,8 @@ jobs:
         uses: apache/skywalking-eyes@5c90c9698806040f50671a172a7cac17974ecebd
       - name: Update dependencies
         run: GOPROXY=https://proxy.golang.org go mod download
+      - name: Install flatc
+        run: bash scripts/ci/install_fb.sh
       - name: Lint
         run: make lint
       - name: Check
@@ -59,9 +61,23 @@ jobs:
         run: make build
       - name: Build release
         run: make release
+  test:
+    name: Test
+    runs-on: ubuntu-20.04
+    steps:
+      - name: Install Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.16
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v2
+      - name: Update dependencies
+        run: GOPROXY=https://proxy.golang.org go mod download
+      - name: Test
+        run: make test
   checks:
     name: build
     runs-on: ubuntu-20.04
-    needs: [check, build]
+    needs: [check, build, test]
     steps:
       - run: echo 'success'
diff --git a/.gitignore b/.gitignore
index 6434c34..0bb2865 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,7 @@ build/release/
 .DS_Store
 .env
 .run
+
+# mock files
+*mock.go
+*mock_test.go
diff --git a/.licenserc.yaml b/.licenserc.yaml
index b545fed..25c0073 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -69,6 +69,9 @@ header: # `header` section is configurations for source codes license header.
     - '**/go.sum'
     - 'LICENSE'
     - 'NOTICE'
+    - '**/build/**'
+    - '**/*_mock.go'
+    - '**/*_mock_test.go'
 
   comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`.
 
diff --git a/Makefile b/Makefile
index 490aeb1..563a0f3 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,8 @@ clean: TARGET=clean test-clean
 clean: default  ## Clean artifacts in all projects
 
 generate: ## Generate API codes
+	go install github.com/golang/mock/mockgen@v1.5.0
+	go generate ./...
 	$(MAKE) -C api/fbs generate
 	$(MAKE) license-fix
 	$(MAKE) format
@@ -81,7 +83,7 @@ EXPECTED_GO_VERSION_PREFIX := "go version go$(CONFIGURED_GO_VERSION)"
 GO_VERSION := $(shell go version)
 
 ## Check that the status is consistent with CI.
-check: clean
+check: clean generate
 # case statement because /bin/sh cannot do prefix comparison, awk is awkward and assuming /bin/bash is brittle
 	@case "$(GO_VERSION)" in $(EXPECTED_GO_VERSION_PREFIX)* ) ;; * ) \
 		echo "Expected 'go version' to start with $(EXPECTED_GO_VERSION_PREFIX), but it didn't: $(GO_VERSION)"; \
@@ -98,7 +100,7 @@ check: clean
 		exit 1; \
 	fi
 	
-pre-push: lint license-check check ## Check source files before pushing to the remote repo
+pre-push: generate lint license-check check ## Check source files before pushing to the remote repo
 
 ##@ License targets
 
diff --git a/api/fbs/v1/database_generated.go b/api/fbs/v1/database_generated.go
index ffb9cb0..bd1a9f4 100644
--- a/api/fbs/v1/database_generated.go
+++ b/api/fbs/v1/database_generated.go
@@ -60,13 +60,6 @@ func GetRootAsNode(buf []byte, offset flatbuffers.UOffsetT) *Node {
 	return x
 }
 
-func GetSizePrefixedRootAsNode(buf []byte, offset flatbuffers.UOffsetT) *Node {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Node{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Node) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -146,13 +139,6 @@ func GetRootAsShard(buf []byte, offset flatbuffers.UOffsetT) *Shard {
 	return x
 }
 
-func GetSizePrefixedRootAsShard(buf []byte, offset flatbuffers.UOffsetT) *Shard {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Shard{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Shard) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -241,13 +227,6 @@ func GetRootAsShardEvent(buf []byte, offset flatbuffers.UOffsetT) *ShardEvent {
 	return x
 }
 
-func GetSizePrefixedRootAsShardEvent(buf []byte, offset flatbuffers.UOffsetT) *ShardEvent {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &ShardEvent{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *ShardEvent) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -321,13 +300,6 @@ func GetRootAsMetadata(buf []byte, offset flatbuffers.UOffsetT) *Metadata {
 	return x
 }
 
-func GetSizePrefixedRootAsMetadata(buf []byte, offset flatbuffers.UOffsetT) *Metadata {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Metadata{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Metadata) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
diff --git a/api/fbs/v1/query_generated.go b/api/fbs/v1/query_generated.go
index 6990dd0..6b5b5d7 100644
--- a/api/fbs/v1/query_generated.go
+++ b/api/fbs/v1/query_generated.go
@@ -129,13 +129,6 @@ func GetRootAsIntPair(buf []byte, offset flatbuffers.UOffsetT) *IntPair {
 	return x
 }
 
-func GetSizePrefixedRootAsIntPair(buf []byte, offset flatbuffers.UOffsetT) *IntPair {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &IntPair{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *IntPair) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -206,13 +199,6 @@ func GetRootAsStrPair(buf []byte, offset flatbuffers.UOffsetT) *StrPair {
 	return x
 }
 
-func GetSizePrefixedRootAsStrPair(buf []byte, offset flatbuffers.UOffsetT) *StrPair {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &StrPair{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *StrPair) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -274,13 +260,6 @@ func GetRootAsPair(buf []byte, offset flatbuffers.UOffsetT) *Pair {
 	return x
 }
 
-func GetSizePrefixedRootAsPair(buf []byte, offset flatbuffers.UOffsetT) *Pair {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Pair{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Pair) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -335,13 +314,6 @@ func GetRootAsPairQuery(buf []byte, offset flatbuffers.UOffsetT) *PairQuery {
 	return x
 }
 
-func GetSizePrefixedRootAsPairQuery(buf []byte, offset flatbuffers.UOffsetT) *PairQuery {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &PairQuery{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *PairQuery) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -400,13 +372,6 @@ func GetRootAsQueryOrder(buf []byte, offset flatbuffers.UOffsetT) *QueryOrder {
 	return x
 }
 
-func GetSizePrefixedRootAsQueryOrder(buf []byte, offset flatbuffers.UOffsetT) *QueryOrder {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &QueryOrder{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *QueryOrder) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -460,13 +425,6 @@ func GetRootAsEntity(buf []byte, offset flatbuffers.UOffsetT) *Entity {
 	return x
 }
 
-func GetSizePrefixedRootAsEntity(buf []byte, offset flatbuffers.UOffsetT) *Entity {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Entity{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Entity) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -586,13 +544,6 @@ func GetRootAsTracesResponse(buf []byte, offset flatbuffers.UOffsetT) *TracesRes
 	return x
 }
 
-func GetSizePrefixedRootAsTracesResponse(buf []byte, offset flatbuffers.UOffsetT) *TracesResponse {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &TracesResponse{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *TracesResponse) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -646,13 +597,6 @@ func GetRootAsProjection(buf []byte, offset flatbuffers.UOffsetT) *Projection {
 	return x
 }
 
-func GetSizePrefixedRootAsProjection(buf []byte, offset flatbuffers.UOffsetT) *Projection {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Projection{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Projection) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -703,13 +647,6 @@ func GetRootAsRangeQuery(buf []byte, offset flatbuffers.UOffsetT) *RangeQuery {
 	return x
 }
 
-func GetSizePrefixedRootAsRangeQuery(buf []byte, offset flatbuffers.UOffsetT) *RangeQuery {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &RangeQuery{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *RangeQuery) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
@@ -767,13 +704,6 @@ func GetRootAsEntityCriteria(buf []byte, offset flatbuffers.UOffsetT) *EntityCri
 	return x
 }
 
-func GetSizePrefixedRootAsEntityCriteria(buf []byte, offset flatbuffers.UOffsetT) *EntityCriteria {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &EntityCriteria{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *EntityCriteria) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
diff --git a/api/fbs/v1/trace_generated.go b/api/fbs/v1/trace_generated.go
index b2beef5..eb43af7 100644
--- a/api/fbs/v1/trace_generated.go
+++ b/api/fbs/v1/trace_generated.go
@@ -34,13 +34,6 @@ func GetRootAsTrace(buf []byte, offset flatbuffers.UOffsetT) *Trace {
 	return x
 }
 
-func GetSizePrefixedRootAsTrace(buf []byte, offset flatbuffers.UOffsetT) *Trace {
-	n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
-	x := &Trace{}
-	x.Init(buf, n+offset+flatbuffers.SizeUint32)
-	return x
-}
-
 func (rcv *Trace) Init(buf []byte, i flatbuffers.UOffsetT) {
 	rcv._tab.Bytes = buf
 	rcv._tab.Pos = i
diff --git a/banyand/kv/kv.go b/banyand/kv/kv.go
index 80efdb4..22dbc76 100644
--- a/banyand/kv/kv.go
+++ b/banyand/kv/kv.go
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//go:generate mockgen -destination=./kv_mock.go -package=kv . Hook
 package kv
 
 import (
diff --git a/banyand/storage/database_test.go b/banyand/storage/database_test.go
index a873420..d772173 100644
--- a/banyand/storage/database_test.go
+++ b/banyand/storage/database_test.go
@@ -27,6 +27,7 @@ import (
 	"time"
 
 	"github.com/dgraph-io/badger/v3/y"
+	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
@@ -47,13 +48,19 @@ func TestDB_Create_Directory(t *testing.T) {
 }
 
 func TestDB_Store(t *testing.T) {
-	p := new(mockPlugin)
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+	var ap AccessPoint
+	p := mockPlugin(ctrl, func(get GetAccessPoint) {
+		ap = get()
+	})
+
 	tempDir, db := setUp(t, p)
 	defer func() {
 		db.GracefulStop()
 		removeDir(tempDir)
 	}()
-	ap := p.ApFunc()
+
 	s := ap.Store("normal")
 	assert.NoError(t, s.Put([]byte("key1"), []byte{12}))
 	val, err := s.Get([]byte("key1"))
@@ -77,7 +84,68 @@ func TestDB_Store(t *testing.T) {
 	assert.Equal(t, [][]byte{{33}}, vals)
 }
 
-func setUp(t *testing.T, p *mockPlugin) (tempDir string, db Database) {
+func mockPlugin(ctrl *gomock.Controller, f func(get GetAccessPoint)) Plugin {
+	p := NewMockPlugin(ctrl)
+	p.
+		EXPECT().
+		ID().
+		Return("foo").
+		AnyTimes()
+	p.
+		EXPECT().
+		Init().
+		Return([]KVSpec{
+			{
+				Name: "normal",
+				Type: KVTypeNormal,
+			},
+			{
+				Name:       "auto-gen",
+				Type:       KVTypeNormal,
+				AutoGenKey: true,
+			},
+			{
+				Name:           "time-series",
+				Type:           KVTypeTimeSeries,
+				TimeSeriesHook: mockHook(ctrl),
+			},
+		}).
+		AnyTimes()
+	p.
+		EXPECT().
+		Start(gomock.Any()).
+		Do(f).
+		AnyTimes()
+	return p
+}
+
+func mockHook(ctrl *gomock.Controller) kv.Hook {
+	h := kv.NewMockHook(ctrl)
+	h.
+		EXPECT().
+		Reduce(gomock.Any(), gomock.Any()).
+		DoAndReturn(func(left bytes.Buffer, right y.ValueStruct) bytes.Buffer {
+			return left
+		}).
+		AnyTimes()
+	h.
+		EXPECT().
+		Extract(gomock.Any(), gomock.Any()).
+		DoAndReturn(func(raw []byte, ts uint64) ([]byte, error) {
+			return raw, nil
+		}).
+		AnyTimes()
+	h.
+		EXPECT().
+		Split(gomock.Any()).
+		DoAndReturn(func(raw []byte) ([][]byte, error) {
+			return [][]byte{raw}, nil
+		}).
+		AnyTimes()
+	return h
+}
+
+func setUp(t *testing.T, p Plugin) (tempDir string, db Database) {
 	require.NoError(t, logger.Init(logger.Logging{
 		Env:   "dev",
 		Level: "debug",
@@ -113,53 +181,3 @@ func removeDir(dir string) {
 		fmt.Printf("Error while removing dir: %v\n", err)
 	}
 }
-
-var _ Plugin = (*mockPlugin)(nil)
-
-type mockPlugin struct {
-	ApFunc GetAccessPoint
-}
-
-func (m *mockPlugin) ID() string {
-	return "foo"
-}
-
-func (m *mockPlugin) Init() []KVSpec {
-	return []KVSpec{
-		{
-			Name: "normal",
-			Type: KVTypeNormal,
-		},
-		{
-			Name:       "auto-gen",
-			Type:       KVTypeNormal,
-			AutoGenKey: true,
-		},
-		{
-			Name:           "time-series",
-			Type:           KVTypeTimeSeries,
-			TimeSeriesHook: &mockHook{},
-		},
-	}
-}
-
-func (m *mockPlugin) Start(point GetAccessPoint) {
-	m.ApFunc = point
-}
-
-var _ kv.Hook = (*mockHook)(nil)
-
-type mockHook struct {
-}
-
-func (m *mockHook) Reduce(left bytes.Buffer, right y.ValueStruct) bytes.Buffer {
-	return left
-}
-
-func (m *mockHook) Extract(raw []byte, ts uint64) ([]byte, error) {
-	return raw, nil
-}
-
-func (m *mockHook) Split(raw []byte) ([][]byte, error) {
-	return [][]byte{raw}, nil
-}
diff --git a/banyand/storage/storage.go b/banyand/storage/storage.go
index dfad909..15bffc0 100644
--- a/banyand/storage/storage.go
+++ b/banyand/storage/storage.go
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//go:generate mockgen -destination=./storage_mock.go -package=storage . Plugin
 package storage
 
 import (
diff --git a/go.mod b/go.mod
index 4c7e9c0..6c472d5 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.16
 
 require (
 	github.com/dgraph-io/badger/v3 v3.2011.1
+	github.com/golang/mock v1.3.1
 	github.com/google/flatbuffers v1.12.0
 	github.com/oklog/run v1.1.0
 	github.com/spf13/cobra v1.1.3
diff --git a/go.sum b/go.sum
index 9a41ac4..5fbfbd8 100644
--- a/go.sum
+++ b/go.sum
@@ -82,6 +82,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
diff --git a/scripts/build/lint.mk b/scripts/build/lint.mk
index 509e5b8..dad733d 100644
--- a/scripts/build/lint.mk
+++ b/scripts/build/lint.mk
@@ -32,4 +32,6 @@ $(LINTER):
 
 .PHONY: lint
 lint: $(LINTER) ## Run all the linters
+	go install github.com/golang/mock/mockgen@v1.5.0
+	go generate $(TEST_PKG_LIST) 
 	$(LINTER) --verbose run $(LINT_OPTS) --config $(root_dir)/golangci.yml
diff --git a/scripts/build/test.mk b/scripts/build/test.mk
index e28ad28..87d39e2 100644
--- a/scripts/build/test.mk
+++ b/scripts/build/test.mk
@@ -31,7 +31,9 @@ TEST_COVERAGE_EXTRA_OPTS ?=
 ##@ Test targets
 
 .PHONY: test
-test: ## Run all the unit tests
+test: $(MOCKGEN) ## Run all the unit tests
+	go install github.com/golang/mock/mockgen@v1.5.0
+	go generate $(TEST_PKG_LIST) 
 	go test $(TEST_OPTS) $(TEST_EXTRA_OPTS) -tags "$(TEST_TAGS)" $(TEST_PKG_LIST)
 
 .PHONY: test-race
@@ -47,4 +49,5 @@ test-coverage: ## Run all the unit tests with coverage analysis on
 
 .PHONY: test-clean
 test-clean::  ## Clean all test artifacts
-	rm -rf $(TEST_COVERAGE_DIR)
\ No newline at end of file
+	rm -rf $(TEST_COVERAGE_DIR)
+	
\ No newline at end of file