aboutsummaryrefslogtreecommitdiff
path: root/server_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'server_test.go')
-rw-r--r--server_test.go806
1 files changed, 806 insertions, 0 deletions
diff --git a/server_test.go b/server_test.go
new file mode 100644
index 0000000..2e88202
--- /dev/null
+++ b/server_test.go
@@ -0,0 +1,806 @@
+package hotline
+
+//
+//import (
+// "bytes"
+// "fmt"
+// "github.com/google/go-cmp/cmp"
+// "io/ioutil"
+// "math/big"
+// "net"
+// "strings"
+// "sync"
+// "testing"
+//)
+//
+//type transactionTest struct {
+// description string // Human understandable description
+// account Account // Account struct for a user that will test transaction will execute under
+// request Transaction // transaction that will be sent by the client to the server
+// want Transaction // transaction that the client expects to receive in response
+// setup func() // Optional setup required for the test scenario
+// teardown func() // Optional teardown for test scenario
+//}
+//
+//func (tt *transactionTest) Setup(srv *Server) error {
+// if err := srv.NewUser(tt.account.Login, tt.account.Name, NegatedUserString([]byte(tt.account.Password)), tt.account.Access); err != nil {
+// return err
+// }
+//
+// if tt.setup != nil {
+// tt.setup()
+// }
+//
+// return nil
+//}
+//
+//func (tt *transactionTest) Teardown(srv *Server) error {
+// if err := srv.DeleteUser(tt.account.Login); err != nil {
+// return err
+// }
+//
+// if tt.teardown != nil {
+// tt.teardown()
+// }
+//
+// return nil
+//}
+//
+//// StartTestServer
+//func StartTestServer() (srv *Server, lnPort int) {
+// hotlineServer, _ := NewServer("test/config/")
+// ln, err := net.Listen("tcp", ":0")
+//
+// if err != nil {
+// panic(err)
+// }
+// go func() {
+// for {
+// conn, _ := ln.Accept()
+// go hotlineServer.HandleConnection(conn)
+// }
+// }()
+// return hotlineServer, ln.Addr().(*net.TCPAddr).Port
+//}
+//
+//func StartTestClient(serverPort int, login, passwd string) (*Client, error) {
+// c := NewClient("")
+//
+// err := c.JoinServer(fmt.Sprintf(":%v", serverPort), login, passwd)
+// if err != nil {
+// return nil, err
+// }
+//
+// return c, nil
+//}
+//
+//func StartTestServerWithClients(clientCount int) ([]*Client, int) {
+// _, serverPort := StartTestServer()
+//
+// var clients []*Client
+// for i := 0; i < clientCount; i++ {
+// client, err := StartTestClient(serverPort, "admin", "")
+// if err != nil {
+// panic(err)
+// }
+// clients = append(clients, client)
+// }
+// clients[0].ReadN(2)
+//
+// return clients, serverPort
+//}
+//
+
+
+////func TestHandleTranAgreed(t *testing.T) {
+//// clients, _ := StartTestServerWithClients(2)
+////
+//// chatMsg := "Test Chat"
+////
+//// // Assert that both clients should receive the user join notification
+//// var wg sync.WaitGroup
+//// for _, client := range clients {
+//// wg.Add(1)
+//// go func(wg *sync.WaitGroup, c *Client) {
+//// defer wg.Done()
+////
+//// receivedMsg := c.ReadTransactions()[0].GetField(fieldData).Data
+////
+//// want := []byte(fmt.Sprintf("test: %s\r", chatMsg))
+//// if bytes.Compare(receivedMsg, want) != 0 {
+//// t.Errorf("%q, want %q", receivedMsg, want)
+//// }
+//// }(&wg, client)
+//// }
+////
+//// trans := clients[1].ReadTransactions()
+//// spew.Dump(trans)
+////
+//// // Send the agreement
+//// clients[1].Connection.Write(
+//// NewTransaction(
+//// tranAgreed, 0,
+//// []Field{
+//// NewField(fieldUserName, []byte("testUser")),
+//// NewField(fieldUserIconID, []byte{0x00,0x07}),
+//// },
+//// ).Payload(),
+//// )
+////
+//// wg.Wait()
+////}
+//
+//func TestChatSend(t *testing.T) {
+// //srvPort := StartTestServer()
+// //
+// //senderClient := NewClient("senderClient")
+// //senderClient.JoinServer(fmt.Sprintf(":%v", srvPort), "", "")
+// //
+// //receiverClient := NewClient("receiverClient")
+// //receiverClient.JoinServer(fmt.Sprintf(":%v", srvPort), "", "")
+//
+// clients, _ := StartTestServerWithClients(2)
+//
+// chatMsg := "Test Chat"
+//
+// // Both clients should receive the chatMsg
+// var wg sync.WaitGroup
+// for _, client := range clients {
+// wg.Add(1)
+// go func(wg *sync.WaitGroup, c *Client) {
+// defer wg.Done()
+//
+// receivedMsg := c.ReadTransactions()[0].GetField(fieldData).Data
+//
+// want := []byte(fmt.Sprintf(" test: %s\r", chatMsg))
+// if bytes.Compare(receivedMsg, want) != 0 {
+// t.Errorf("%q, want %q", receivedMsg, want)
+// }
+// }(&wg, client)
+// }
+//
+// // Send the chatMsg
+// clients[1].Send(
+// NewTransaction(
+// tranChatSend, 0,
+// []Field{
+// NewField(fieldData, []byte(chatMsg)),
+// },
+// ),
+// )
+//
+// wg.Wait()
+//}
+//
+//func TestSetClientUserInfo(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+//
+// newIcon := []byte{0x00, 0x01}
+// newUserName := "newName"
+//
+// // Both clients should receive the chatMsg
+// var wg sync.WaitGroup
+// for _, client := range clients {
+// wg.Add(1)
+// go func(wg *sync.WaitGroup, c *Client) {
+// defer wg.Done()
+//
+// tran := c.ReadTransactions()[0]
+//
+// want := []byte(newUserName)
+// got := tran.GetField(fieldUserName).Data
+// if bytes.Compare(got, want) != 0 {
+// t.Errorf("%q, want %q", got, want)
+// }
+// }(&wg, client)
+// }
+//
+// _, err := clients[1].Connection.Write(
+// NewTransaction(
+// tranSetClientUserInfo, 0,
+// []Field{
+// NewField(fieldUserIconID, newIcon),
+// NewField(fieldUserName, []byte(newUserName)),
+// },
+// ).Payload(),
+// )
+// if err != nil {
+// t.Errorf("%v", err)
+// }
+//
+// wg.Wait()
+//}
+//
+//// TestSendInstantMsg tests that client A can send an instant message to client B
+////
+//func TestSendInstantMsg(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+//
+// instantMsg := "Test IM"
+//
+// var wg sync.WaitGroup
+// wg.Add(1)
+// go func(wg *sync.WaitGroup, c *Client) {
+// defer wg.Done()
+//
+// tran := c.WaitForTransaction(tranServerMsg)
+//
+// receivedMsg := tran.GetField(fieldData).Data
+// want := []byte(fmt.Sprintf("%s", instantMsg))
+// if bytes.Compare(receivedMsg, want) != 0 {
+// t.Errorf("%q, want %q", receivedMsg, want)
+// }
+// }(&wg, clients[0])
+//
+// _ = clients[1].Send(
+// NewTransaction(tranGetUserNameList, 0, []Field{}),
+// )
+// //connectedUsersTran := clients[1].ReadTransactions()[0]
+// ////connectedUsers := connectedUsersTran.Fields[0].Data[0:2]
+// //spew.Dump(connectedUsersTran.Fields)
+// //firstUserID := connectedUsersTran.Fields[0].Data[0:2]
+// //
+// //spew.Dump(firstUserID)
+//
+// // Send the IM
+// err := clients[1].Send(
+// NewTransaction(
+// tranSendInstantMsg, 0,
+// []Field{
+// NewField(fieldData, []byte(instantMsg)),
+// NewField(fieldUserName, clients[1].UserName),
+// NewField(fieldUserID, []byte{0, 2}),
+// NewField(fieldOptions, []byte{0, 1}),
+// },
+// ),
+// )
+// if err != nil {
+// t.Error(err)
+// }
+//
+// wg.Wait()
+//}
+//
+//func TestOldPostNews(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+//
+// newsPost := "Test News Post"
+//
+// var wg sync.WaitGroup
+// wg.Add(1)
+// go func(wg *sync.WaitGroup, c *Client) {
+// defer wg.Done()
+//
+// receivedMsg := c.ReadTransactions()[0].GetField(fieldData).Data
+//
+// if strings.Contains(string(receivedMsg), newsPost) == false {
+// t.Errorf("news post missing")
+// }
+// }(&wg, clients[0])
+//
+// clients[1].Connection.Write(
+// NewTransaction(
+// tranOldPostNews, 0,
+// []Field{
+// NewField(fieldData, []byte(newsPost)),
+// },
+// ).Payload(),
+// )
+//
+// wg.Wait()
+//}
+//
+//// TODO: Fixme
+////func TestGetFileNameList(t *testing.T) {
+//// clients, _ := StartTestServerWithClients(2)
+////
+//// clients[0].Connection.Write(
+//// NewTransaction(
+//// tranGetFileNameList, 0,
+//// []Field{},
+//// ).Payload(),
+//// )
+////
+//// ts := clients[0].ReadTransactions()
+//// testfileSit := ReadFileNameWithInfo(ts[0].Fields[1].Data)
+////
+//// want := "testfile.sit"
+//// got := testfileSit.Name
+//// diff := cmp.Diff(want, got)
+//// if diff != "" {
+//// t.Fatalf(diff)
+//// }
+//// if testfileSit.Name != "testfile.sit" {
+//// t.Errorf("news post missing")
+//// t.Errorf("%q, want %q", testfileSit.Name, "testfile.sit")
+//// }
+////}
+//
+//func TestNewsCategoryList(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+// client := clients[0]
+//
+// client.Send(
+// NewTransaction(
+// tranGetNewsCatNameList, 0,
+// []Field{},
+// ),
+// )
+//
+// ts := client.ReadTransactions()
+// cats := ts[0].GetFields(fieldNewsCatListData15)
+//
+// newsCat := ReadNewsCategoryListData(cats[0].Data)
+// want := "TestBundle"
+// got := newsCat.Name
+// diff := cmp.Diff(want, got)
+// if diff != "" {
+// t.Fatalf(diff)
+// }
+//
+// newsBundle := ReadNewsCategoryListData(cats[1].Data)
+// want = "TestCat"
+// got = newsBundle.Name
+// diff = cmp.Diff(want, got)
+// if diff != "" {
+// t.Fatalf(diff)
+// }
+//}
+//
+//func TestNestedNewsCategoryList(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+// client := clients[0]
+// newsPath := NewsPath{
+// []string{
+// "TestBundle",
+// "NestedBundle",
+// },
+// }
+//
+// _, err := client.Connection.Write(
+// NewTransaction(
+// tranGetNewsCatNameList, 0,
+// []Field{
+// NewField(
+// fieldNewsPath,
+// newsPath.Payload(),
+// ),
+// },
+// ).Payload(),
+// )
+// if err != nil {
+// t.Errorf("%v", err)
+// }
+//
+// ts := client.ReadTransactions()
+// cats := ts[0].GetFields(fieldNewsCatListData15)
+//
+// newsCat := ReadNewsCategoryListData(cats[0].Data)
+// want := "NestedCat"
+// got := newsCat.Name
+// diff := cmp.Diff(want, got)
+// if diff != "" {
+// t.Fatalf(diff)
+// }
+//}
+//
+//func TestFileDownload(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+// client := clients[0]
+//
+// type want struct {
+// fileSize []byte
+// transferSize []byte
+// waitingCount []byte
+// refNum []byte
+// }
+// var tests = []struct {
+// fileName string
+// want want
+// }{
+// {
+// fileName: "testfile.sit",
+// want: want{
+// fileSize: []byte{0x0, 0x0, 0x0, 0x13},
+// transferSize: []byte{0x0, 0x0, 0x0, 0xa1},
+// },
+// },
+// {
+// fileName: "testfile.txt",
+// want: want{
+// fileSize: []byte{0x0, 0x0, 0x0, 0x17},
+// transferSize: []byte{0x0, 0x0, 0x0, 0xa5},
+// },
+// },
+// }
+//
+// for _, test := range tests {
+// _, err := client.Connection.Write(
+// NewTransaction(
+// tranDownloadFile, 0,
+// []Field{
+// NewField(fieldFileName, []byte(test.fileName)),
+// NewField(fieldFilePath, []byte("")),
+// },
+// ).Payload(),
+// )
+// if err != nil {
+// t.Errorf("%v", err)
+// }
+// tran := client.ReadTransactions()[0]
+//
+// if got := tran.GetField(fieldFileSize).Data; bytes.Compare(got, test.want.fileSize) != 0 {
+// t.Errorf("TestFileDownload: fileSize got %#v, want %#v", got, test.want.fileSize)
+// }
+//
+// if got := tran.GetField(fieldTransferSize).Data; bytes.Compare(got, test.want.transferSize) != 0 {
+// t.Errorf("TestFileDownload: fieldTransferSize: %s: got %#v, want %#v", test.fileName, got, test.want.transferSize)
+// }
+// }
+//}
+//
+//func TestFileUpload(t *testing.T) {
+// clients, _ := StartTestServerWithClients(2)
+// client := clients[0]
+//
+// var tests = []struct {
+// fileName string
+// want Transaction
+// }{
+// {
+// fileName: "testfile.sit",
+// want: Transaction{
+// Fields: []Field{
+// NewField(fieldRefNum, []byte{0x16, 0x3f, 0x5f, 0xf}),
+// },
+// },
+// },
+// }
+//
+// for _, test := range tests {
+// err := client.Send(
+// NewTransaction(
+// tranUploadFile, 0,
+// []Field{
+// NewField(fieldFileName, []byte(test.fileName)),
+// NewField(fieldFilePath, []byte("")),
+// },
+// ),
+// )
+// if err != nil {
+// t.Errorf("%v", err)
+// }
+// tran := client.ReadTransactions()[0]
+//
+// for _, f := range test.want.Fields {
+// got := tran.GetField(f.Uint16ID()).Data
+// want := test.want.GetField(fieldRefNum).Data
+// if bytes.Compare(got, want) != 0 {
+// t.Errorf("xxx: yyy got %#v, want %#v", got, want)
+// }
+// }
+// }
+//}
+//
+//// TODO: Make canonical
+//func TestNewUser(t *testing.T) {
+// srv, port := StartTestServer()
+//
+// var tests = []struct {
+// description string
+// setup func()
+// teardown func()
+// account Account
+// request Transaction
+// want Transaction
+// }{
+// {
+// description: "a valid new account",
+// teardown: func() {
+// _ = srv.DeleteUser("testUser")
+// },
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{255, 255, 255, 255, 255, 255, 255, 255},
+// },
+// request: NewTransaction(
+// tranNewUser, 0,
+// []Field{
+// NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
+// NewField(fieldUserName, []byte("testUserName")),
+// NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
+// NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{},
+// },
+// },
+// {
+// description: "a newUser request from a user without the required access",
+// teardown: func() {
+// _ = srv.DeleteUser("testUser")
+// },
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+// },
+// request: NewTransaction(
+// tranNewUser, 0,
+// []Field{
+// NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
+// NewField(fieldUserName, []byte("testUserName")),
+// NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
+// NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{
+// NewField(fieldError, []byte("You are not allowed to create new accounts.")),
+// },
+// },
+// },
+// {
+// description: "a request to create a user that already exists",
+// teardown: func() {
+// _ = srv.DeleteUser("testUser")
+// },
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{255, 255, 255, 255, 255, 255, 255, 255},
+// },
+// request: NewTransaction(
+// tranNewUser, 0,
+// []Field{
+// NewField(fieldUserLogin, []byte(NegatedUserString([]byte("guest")))),
+// NewField(fieldUserName, []byte("testUserName")),
+// NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
+// NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{
+// NewField(fieldError, []byte("Cannot create account guest because there is already an account with that login.")),
+// },
+// },
+// },
+// }
+//
+// for _, test := range tests {
+// if test.setup != nil {
+// test.setup()
+// }
+//
+// if err := srv.NewUser(test.account.Login, test.account.Name, NegatedUserString([]byte(test.account.Password)), test.account.Access); err != nil {
+// t.Errorf("%v", err)
+// }
+//
+// c := NewClient("")
+// err := c.JoinServer(fmt.Sprintf(":%v", port), test.account.Login, test.account.Password)
+// if err != nil {
+// t.Errorf("login failed: %v", err)
+// }
+//
+// if err := c.Send(test.request); err != nil {
+// t.Errorf("%v", err)
+// }
+//
+// tran := c.ReadTransactions()[0]
+// for _, want := range test.want.Fields {
+// got := tran.GetField(want.Uint16ID())
+// if bytes.Compare(got.Data, want.Data) != 0 {
+// t.Errorf("%v: field mismatch: want: %#v got: %#v", test.description, want.Data, got.Data)
+// }
+// }
+//
+// srv.DeleteUser(test.account.Login)
+//
+// if test.teardown != nil {
+// test.teardown()
+// }
+// }
+//}
+//
+//func TestDeleteUser(t *testing.T) {
+// srv, port := StartTestServer()
+//
+// var tests = []transactionTest{
+// {
+// description: "a deleteUser request from a user without the required access",
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+// },
+// request: NewTransaction(
+// tranDeleteUser, 0,
+// []Field{
+// NewField(fieldUserLogin, []byte(NegatedUserString([]byte("foo")))),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{
+// NewField(fieldError, []byte("You are not allowed to delete accounts.")),
+// },
+// },
+// },
+// {
+// description: "a valid deleteUser request",
+// setup: func() {
+// _ = srv.NewUser("foo", "foo", "foo", []byte{0, 0, 0, 0, 0, 0, 0, 0})
+// },
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{255, 255, 255, 255, 255, 255, 255, 255},
+// },
+// request: NewTransaction(
+// tranDeleteUser, 0,
+// []Field{
+// NewField(fieldUserLogin, []byte(NegatedUserString([]byte("foo")))),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{},
+// },
+// },
+// }
+//
+// for _, test := range tests {
+// test.Setup(srv)
+//
+// c := NewClient("")
+// err := c.JoinServer(fmt.Sprintf(":%v", port), test.account.Login, test.account.Password)
+// if err != nil {
+// t.Errorf("login failed: %v", err)
+// }
+//
+// if err := c.Send(test.request); err != nil {
+// t.Errorf("%v", err)
+// }
+//
+// tran := c.ReadTransactions()[0]
+// for _, want := range test.want.Fields {
+// got := tran.GetField(want.Uint16ID())
+// if bytes.Compare(got.Data, want.Data) != 0 {
+// t.Errorf("%v: field mismatch: want: %#v got: %#v", test.description, want.Data, got.Data)
+// }
+// }
+//
+// test.Teardown(srv)
+// }
+//}
+//
+//func TestDeleteFile(t *testing.T) {
+// srv, port := StartTestServer()
+//
+// var tests = []transactionTest{
+// {
+// description: "a request without the required access",
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{0, 0, 0, 0, 0, 0, 0, 0},
+// },
+// request: NewTransaction(
+// tranDeleteFile, 0,
+// []Field{
+// NewField(fieldFileName, []byte("testFile")),
+// NewField(fieldFilePath, []byte("")),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{},
+// },
+// },
+// {
+// description: "a valid deleteFile request",
+// setup: func() {
+// _ = ioutil.WriteFile(srv.Config.FileRoot+"testFile", []byte{0x00}, 0666)
+// },
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{255, 255, 255, 255, 255, 255, 255, 255},
+// },
+// request: NewTransaction(
+// tranDeleteFile, 0,
+// []Field{
+// NewField(fieldFileName, []byte("testFile")),
+// NewField(fieldFilePath, []byte("")),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{},
+// },
+// },
+// {
+// description: "an invalid request for a file that does not exist",
+// account: Account{
+// Login: "test",
+// Name: "unnamed",
+// Password: "test",
+// Access: []byte{255, 255, 255, 255, 255, 255, 255, 255},
+// },
+// request: NewTransaction(
+// tranDeleteFile, 0,
+// []Field{
+// NewField(fieldFileName, []byte("testFile")),
+// NewField(fieldFilePath, []byte("")),
+// },
+// ),
+// want: Transaction{
+// Fields: []Field{
+// NewField(fieldError, []byte("Cannot delete file testFile because it does not exist or cannot be found.")),
+// },
+// },
+// },
+// }
+//
+// for _, test := range tests {
+// test.Setup(srv)
+//
+// c := NewClient("")
+//
+// if err := c.JoinServer(fmt.Sprintf(":%v", port), test.account.Login, test.account.Password); err != nil {
+// t.Errorf("login failed: %v", err)
+// }
+//
+// if err := c.Send(test.request); err != nil {
+// t.Errorf("%v", err)
+// }
+//
+// tran := c.ReadTransactions()[0]
+// for _, want := range test.want.Fields {
+// got := tran.GetField(want.Uint16ID())
+// if bytes.Compare(got.Data, want.Data) != 0 {
+// t.Errorf("%v: field mismatch: want: %#v got: %#v", test.description, want.Data, got.Data)
+// }
+// }
+//
+// test.Teardown(srv)
+// }
+//}
+//
+//func Test_authorize(t *testing.T) {
+// accessBitmap := big.NewInt(int64(0))
+// accessBitmap.SetBit(accessBitmap, accessCreateFolder, 1)
+// fmt.Printf("%v %b %x\n", accessBitmap, accessBitmap, accessBitmap)
+// fmt.Printf("%b\n", 0b10000)
+//
+// type args struct {
+// access *[]byte
+// reqAccess int
+// }
+// tests := []struct {
+// name string
+// args args
+// want bool
+// }{
+// {
+// name: "fooz",
+// args: args{
+// access: &[]byte{4, 0, 0, 0, 0, 0, 0, 0x02},
+// reqAccess: accessDownloadFile,
+// },
+// want: true,
+// },
+// }
+// for _, tt := range tests {
+// t.Run(tt.name, func(t *testing.T) {
+// if got := authorize(tt.args.access, tt.args.reqAccess); got != tt.want {
+// t.Errorf("authorize() = %v, want %v", got, tt.want)
+// }
+// })
+// }
+//}