Esempio n. 1
0
func getMockCluster(t *testing.T) models.Cluster {
	mockData, err := mocks.GetMockCluster()
	assert.NoErr(t, err)
	mockCluster, err := ParseJSONCluster(mockData)
	assert.NoErr(t, err)
	return mockCluster
}
Esempio n. 2
0
func TestTarFiles(t *testing.T) {
	dir, err := tests.DataDir()
	assert.NoErr(t, err)
	rdr, err := tarFiles(dir, tests.ExpectedDataSlice())
	assert.NoErr(t, err)
	tr := tar.NewReader(rdr)
	set := tests.ExpectedDataSet()
	numFound := 0
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			t.Errorf("reading next record in archive (%s)", err)
			continue
		}
		_, found := set[hdr.Name]
		if !found {
			t.Errorf("unknown file %s", hdr.Name)
			continue
		}
		numFound++
	}
	if numFound != len(set) {
		t.Fatalf("found only %d of %d files in the testdata dir", len(tests.ExpectedDataSlice()), numFound)
	}

}
Esempio n. 3
0
func TestNewestSemVer(t *testing.T) {
	// Verify that NewestSemVer returns correct semver string for larger major, minor, and patch substrings
	const v1Lower = "2.0.0"
	v2s := [3]string{"3.0.0", "2.1.0", "2.0.1"}
	for _, v2 := range v2s {
		newest, err := NewestSemVer(v1Lower, v2)
		assert.NoErr(t, err)
		if newest != v2 {
			fmt.Printf("expected %s to be greater than %s\n", v2, v1Lower)
			t.Fatal("semver comparison failure")
		}
	}
	// Verify that NewestSemVer returns correct semver string for smaller major, minor, and patch substrings
	const v1Higher = "2.4.5"
	v2s = [3]string{"1.99.23", "2.3.99", "2.4.4"}
	for _, v2 := range v2s {
		newest, err := NewestSemVer(v1Higher, v2)
		assert.NoErr(t, err)
		if newest != v1Higher {
			fmt.Printf("expected %s to be greater than %s\n", v1Higher, v2)
			t.Fatal("semver comparison failure")
		}
	}
	// Verify that NewestSemVer returns correct semver string for comparing equal strings
	const v1Equal = "1.0.0"
	v2 := v1Equal
	newest, err := NewestSemVer(v1Equal, v2)
	assert.NoErr(t, err)
	if newest != v1Equal && newest != v2 {
		fmt.Printf("expected %s to be equal to %s and %s\n", newest, v1Equal, v2)
		t.Fatal("semver comparison failure")
	}
}
Esempio n. 4
0
// TestPushInvalidArgsLength tests trying to do a push with only the command, not the repo
func TestPushInvalidArgsLength(t *testing.T) {
	const testingServerAddr = "127.0.0.1:2252"
	key, err := sshTestingHostKey()
	assert.NoErr(t, err)

	cfg, err := serverConfigure()
	assert.NoErr(t, err)
	cfg.AddHostKey(key)

	c := NewCircuit()
	pushLock := NewInMemoryRepositoryLock(0)
	runServer(cfg, c, pushLock, testingServerAddr, 0*time.Second, t)

	// Give server time to initialize.
	time.Sleep(200 * time.Millisecond)

	assert.Equal(t, c.State(), ClosedState, "circuit state")

	// Connect to the server and issue env var set. This should return true.
	client, err := ssh.Dial("tcp", testingServerAddr, clientConfig())
	assert.NoErr(t, err)

	// check for invalid length of arguments
	sess, err := client.NewSession()
	assert.NoErr(t, err)
	defer sess.Close()
	if out, err := sess.Output("git-upload-pack"); err == nil {
		t.Errorf("Expected an error but '%s' was received", out)
	} else if string(out) != "" {
		t.Errorf("Expected , got '%s'", out)
	}
}
func TestRefreshAvailableVersions(t *testing.T) {
	desc := "this is test1"
	updAvail := "nothing"
	expectedCompVsns := operations.GetComponentsByLatestReleaseOKBodyBody{
		Data: []*models.ComponentVersion{
			&models.ComponentVersion{
				Component:       &models.Component{Name: "test1", Description: &desc},
				Version:         &models.Version{Train: "testTrain"},
				UpdateAvailable: &updAvail,
			},
		},
	}
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		if err := json.NewEncoder(w).Encode(expectedCompVsns); err != nil {
			http.Error(w, "error encoding JSON", http.StatusInternalServerError)
			return
		}
	}))
	defer ts.Close()
	apiclient, err := config.GetSwaggerClient(ts.URL)
	assert.NoErr(t, err)
	vsns := availableVersionsFromAPI{
		rwm:             new(sync.RWMutex),
		baseVersionsURL: ts.URL,
		apiClient:       apiclient,
	}
	retCompVsns, err := vsns.Refresh(models.Cluster{})
	assert.NoErr(t, err)
	assert.Equal(t, len(retCompVsns), len(expectedCompVsns.Data), "number of component versions")
}
Esempio n. 6
0
func TestIDHandler(t *testing.T) {
	idHandler := IDHandler(&mockClusterID{})
	resp, err := getTestHandlerResponse(idHandler)
	assert.NoErr(t, err)
	assert200(t, resp)
	respData, err := ioutil.ReadAll(resp.Body)
	assert.NoErr(t, err)
	assert.Equal(t, string(respData), mockID, "ID value")
}
Esempio n. 7
0
// Calls GetID twice, the first time we expect our passed-in struct w/ Get() method
// to be invoked, the 2nd time we expect to receive the same value back (cached in memory)
// and for the passed-in Get() method to be ignored
func TestGetID(t *testing.T) {
	cid := &testClusterID{}
	id, err := GetID(cid)
	assert.NoErr(t, err)
	assert.Equal(t, id, mockClusterID, "cluster ID value")
	cid.cache = "something else"
	id, err = GetID(cid)
	assert.NoErr(t, err)
	assert.Equal(t, id, "something else", "cluster ID value")
}
Esempio n. 8
0
func TestCreatePreReceiveHook(t *testing.T) {
	const gitHome = "TestGitHome"
	gopath := os.Getenv("GOPATH")
	repoPath := filepath.Join(gopath, "src", "github.com", "deis", "builder", "testdata")
	assert.NoErr(t, createPreReceiveHook(gitHome, repoPath))
	hookBytes, err := ioutil.ReadFile(filepath.Join(repoPath, "hooks", "pre-receive"))
	assert.NoErr(t, err)
	hookStr := string(hookBytes)
	gitHomeIdx := strings.Index(hookStr, fmt.Sprintf("GIT_HOME=%s", gitHome))
	assert.False(t, gitHomeIdx == -1, "GIT_HOME was not found")
}
// Calls GetAvailableVersions twice, the first time we expect our passed-in struct w/ Refresh() method
// to be invoked, the 2nd time we expect to receive the same value back (cached in memory)
// and for the passed-in Refresh() method to be ignored
func TestGetAvailableVersions(t *testing.T) {
	mock := getMockComponentVersions()
	var mockVersions []models.ComponentVersion
	assert.NoErr(t, json.Unmarshal(mock, &mockVersions))
	versions, err := GetAvailableVersions(testAvailableVersions{}, models.Cluster{})
	assert.NoErr(t, err)
	assert.Equal(t, versions, mockVersions, "component versions data")
	versions, err = GetAvailableVersions(shouldBypassAvailableVersions{}, models.Cluster{})
	assert.NoErr(t, err)
	assert.Equal(t, versions, mockVersions, "component versions data")
}
Esempio n. 10
0
func TestWritePlainText(t *testing.T) {
	const text = "foo"
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		writePlainText(text, w)
	})
	resp, err := getTestHandlerResponse(handler)
	assert.NoErr(t, err)
	assert.Equal(t, resp.Header.Get("Content-Type"), "text/plain", "Content-Type value")
	respData, err := ioutil.ReadAll(resp.Body)
	assert.NoErr(t, err)
	assert.Equal(t, string(respData), text, "text response")
}
Esempio n. 11
0
// TestServer tests the SSH server.
//
// This listens on the non-standard port 2244 of localhost. This will generate
// an entry in your known_hosts file, and will tie that to the testing key
// used here. It's not recommended that you try to start another SSH server on
// the same port (at a later time) or else you will have key issues that you
// must manually resolve.
func TestReceive(t *testing.T) {
	const testingServerAddr = "127.0.0.1:2244"
	key, err := sshTestingHostKey()
	assert.NoErr(t, err)

	cfg, err := serverConfigure()
	assert.NoErr(t, err)
	cfg.AddHostKey(key)

	c := NewCircuit()
	pushLock := NewInMemoryRepositoryLock(0)
	runServer(cfg, c, pushLock, testingServerAddr, time.Duration(0), t)

	// Give server time to initialize.
	time.Sleep(200 * time.Millisecond)
	assert.Equal(t, c.State(), ClosedState, "circuit state")

	// Connect to the server and issue env var set. This should return true.
	client, err := ssh.Dial("tcp", testingServerAddr, clientConfig())
	if err != nil {
		t.Fatalf("Failed to connect client to local server: %s", err)
	}
	sess, err := client.NewSession()
	if err != nil {
		t.Fatalf("Failed to create client session: %s", err)
	}
	defer sess.Close()

	if err := sess.Setenv("HELLO", "world"); err != nil {
		t.Fatal(err)
	}

	if out, err := sess.Output("ping"); err != nil {
		t.Errorf("Output '%s' Error %s", out, err)
	} else if string(out) != "pong" {
		t.Errorf("Expected 'pong', got '%s'", out)
	}

	// Create a new session because the success of the last one closed the
	// connection.
	sess, err = client.NewSession()
	if err != nil {
		t.Fatalf("Failed to create client session: %s", err)
	}
	if err := sess.Run("illegal"); err == nil {
		t.Fatalf("expected a failed run with command 'illegal'")
	}
	if err := sess.Run("illegal command"); err == nil {
		t.Fatalf("expected a failed run with command 'illegal command'")
	}

}
Esempio n. 12
0
func TestGetID(t *testing.T) {
	const idRoute = "/id"
	resp, apiServer, err := testGet(idRoute)
	if apiServer != nil {
		apiServer.Close()
	}
	assert.NoErr(t, err)
	assert200(t, resp)
	respData, err := ioutil.ReadAll(resp.Body)
	assert.NoErr(t, err)
	mockData, err := mocks.GetMockClusterID()
	assert.NoErr(t, err)
	assert.Equal(t, string(respData), mockData, "id data response")
}
Esempio n. 13
0
func TestHTTPQueueOperations(t *testing.T) {
	srv := testsrv.StartServer(makeQHandler())
	defer srv.Close()
	urlStrSplit := strings.Split(strings.TrimPrefix(srv.URLStr(), "http://"), ":")
	assert.Equal(t, 2, len(urlStrSplit), "number of elements in the URL string")
	host := urlStrSplit[0]
	port, err := strconv.Atoi(urlStrSplit[1])
	assert.NoErr(t, err)
	if port > 65535 {
		t.Fatalf("port [%d] not a uint16", port)
	}
	cl := NewHTTPClient(SchemeHTTP, host, uint16(port))
	assert.NoErr(t, qOperations(cl))
}
Esempio n. 14
0
// TestConcurrentPushSameRepo tests many concurrent pushes, each to the same repo
func TestConcurrentPushSameRepo(t *testing.T) {
	const testingServerAddr = "127.0.0.1:2245"
	key, err := sshTestingHostKey()
	assert.NoErr(t, err)

	cfg, err := serverConfigure()
	assert.NoErr(t, err)
	cfg.AddHostKey(key)

	c := NewCircuit()
	pushLock := NewInMemoryRepositoryLock(500 * time.Millisecond)
	runServer(cfg, c, pushLock, testingServerAddr, 2*time.Second, t)

	// Give server time to initialize.
	time.Sleep(200 * time.Millisecond)

	assert.Equal(t, c.State(), ClosedState, "circuit state")

	// Connect to the server and issue env var set. This should return true.
	client, err := ssh.Dial("tcp", testingServerAddr, clientConfig())
	assert.NoErr(t, err)

	const numPushers = 4
	outCh := make(chan *sshSessionOutput, numPushers)
	for i := 0; i < numPushers; i++ {
		go func() {
			sess, newSessErr := client.NewSession()
			assert.NoErr(t, newSessErr)
			defer sess.Close()
			out, outErr := sess.Output("git-upload-pack /demo.git")
			outCh <- &sshSessionOutput{outStr: string(out), err: outErr}
		}()
	}

	// ensure at least 1 output was successful
	foundOK := false
	to := 1 * time.Second
	for i := 0; i < numPushers; i++ {
		select {
		case sessOut := <-outCh:
			if sessOut.outStr == "OK" {
				foundOK = true
			}
		case <-time.After(to):
			t.Fatalf("didn't receive an output within %s", to)
		}
	}
	assert.True(t, foundOK, "no SSH requests were successful")
}
Esempio n. 15
0
func TestHTTPDo(t *testing.T) {
	hndl := func(http.ResponseWriter, *http.Request) {}
	srv := testsrv.StartServer(http.HandlerFunc(hndl))
	defer srv.Close()
	transport := &http.Transport{}
	client := &http.Client{Transport: transport}
	req, err := http.NewRequest("GET", srv.URLStr(), strings.NewReader(""))
	assert.NoErr(t, err)
	err = HTTPDo(context.Background(), client, transport, req, func(*http.Response, error) error {
		return nil
	})
	assert.NoErr(t, err)
	recv := srv.AcceptN(1, 100*time.Millisecond)
	assert.Equal(t, 1, len(recv), "number of received requests")
}
Esempio n. 16
0
func TestClientAcquireLock(t *testing.T) {
	mut := new(sync.Mutex)
	lockID := NewLockID()
	router := mux.NewRouter()
	registerLockHandler(router, mut, lockID)
	registerUnlockHandler(router, mut, lockID)
	srv := httptest.NewServer(router)
	defer srv.Close()

	baseURL := srv.URL
	lid, err := AcquireLock(http.DefaultClient, baseURL)
	assert.NoErr(t, err)
	assert.Equal(t, lockID.id, lid, "resulting lock IDs")
	assert.NoErr(t, ReleaseLock(http.DefaultClient, baseURL, lid))
}
Esempio n. 17
0
func TestGetFiles(t *testing.T) {
	dir, err := tests.DataDir()
	assert.NoErr(t, err)
	files, err := getFiles(dir)
	assert.NoErr(t, err)
	expected := tests.ExpectedDataSlice()
	set := map[string]struct{}{}
	for _, file := range files {
		set[file] = struct{}{}
	}
	for _, ex := range expected {
		_, ok := set[ex]
		assert.True(t, ok, "file %s was not found", ex)
	}
}
Esempio n. 18
0
func TestParse(t *testing.T) {
	//TODO: use gogenerate to generate valid html templates
	//http://godoc.org/github.com/arschles/gogenerate

	tmpl := `
    <html>
      <head>
        <title>hello {{.name}}</title>
      </head>
      <body>
        {{.greeting}}
      </body>
    </html>
  `
	fileName := "mytmpl.tmpl"
	tmplBytes := []byte(tmpl)
	expectedErr := errors.New("template not found")
	assetFunc := createValidAssetFunc(fileName, tmplBytes, expectedErr)

	tmpl1, err1 := New("test", assetFunc).Parse(fileName)
	assert.NoErr(t, err1)
	assert.False(t, tmpl1 == nil, "tmpl1 was nil when it should not have been")
	tmpl2, err2 := New("test1", assetFunc).Parse(fileName + fileName)
	assert.Err(t, err2, expectedErr)
	assert.True(t, tmpl2 == nil, "tmpl2 was not nil when it should have been")

	//TODO: check actual template output
	name := "Aaron"
	tmplData := map[string]string{
		"name": name,
	}
	buf1 := bytes.NewBuffer([]byte{})
	executeErr1 := tmpl1.Execute(buf1, tmplData)
	stdTmpl, stdTmplParseErr := template.New("referenceTest").Parse(tmpl)
	assert.NoErr(t, stdTmplParseErr)
	buf2 := bytes.NewBuffer([]byte{})
	executeErr2 := stdTmpl.Execute(buf2, tmplData)

	assert.NoErr(t, executeErr1)
	assert.NoErr(t, executeErr2)

	bytes1 := buf1.Bytes()
	bytes2 := buf2.Bytes()

	assert.True(t,
		string(bytes1) == string(bytes2),
		"actual template output %s is not equal expected %s", string(bytes1), string(bytes2))
}
Esempio n. 19
0
func TestCreateTplCtx(t *testing.T) {
	tplDir := "rootfs/templates"
	ctx := createTplCtx(tplDir, funcs)
	tpl, err := ctx.Prepare(tpl.NewFiles("index.html"))
	assert.NoErr(t, err)
	assert.NotNil(t, tpl, "returned template")
}
Esempio n. 20
0
func TestGetDetailsFromDockerConfigSecretSuccess(t *testing.T) {
	encToken := base64.StdEncoding.EncodeToString([]byte("testuser:testpassword"))
	auth := []byte(`
    {
    "auths": {
              "https://test.io": {
                  "auth": "` + encToken + `",
                  "email": "*****@*****.**"
              }
          }
    }
`)
	expectedData := map[string]string{"DEIS_REGISTRY_USERNAME": "******", "DEIS_REGISTRY_PASSWORD": "******", "DEIS_REGISTRY_HOSTNAME": "https://test.io"}
	data := make(map[string][]byte)
	data[api.DockerConfigJsonKey] = auth
	secret := api.Secret{Data: data}
	getter := &k8s.FakeSecret{
		FnGet: func(string) (*api.Secret, error) {
			return &secret, nil
		},
	}
	regData, err := getDetailsFromDockerConfigSecret(getter, testSecret)
	assert.NoErr(t, err)
	assert.Equal(t, expectedData, regData, "registry details")

}
Esempio n. 21
0
func TestGetInstalled(t *testing.T) {
	cluster, err := GetInstalled(mockInstalledComponents{})
	assert.NoErr(t, err)
	assert.Equal(t, cluster.Components[0].Component.Name, mockComponentName, "Name value")
	assert.Equal(t, *cluster.Components[0].Component.Description, mockComponentDescription, "Description value")
	assert.Equal(t, cluster.Components[0].Version.Version, mockComponentVersion, "Version value")
}
Esempio n. 22
0
func TestParseJSONCluster(t *testing.T) {
	const name = "component"
	const description = "test component"
	const version = "1.0.0"
	raw := []byte(fmt.Sprintf(`{
	  "id": "%s",
	  "components": [
	    {
	      "component": {
	        "name": "%s",
	        "description": "%s"
	      },
	      "version": {
	        "version": "%s"
	      }
	    }
	  ]
	}`, mockClusterID, name, description, version))
	cluster, err := ParseJSONCluster(raw)
	assert.NoErr(t, err)

	assert.Equal(t, cluster.ID, mockClusterID, "ID value")
	assert.Equal(t, cluster.Components[0].Component.Name, name, "Name value")
	assert.Equal(t, *cluster.Components[0].Component.Description, description, "Description value")
	assert.Equal(t, cluster.Components[0].Version.Version, version, "Version value")
}
Esempio n. 23
0
func TestMsgs(t *testing.T) {
	const numSends = 20
	srv := StartServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))
	defer srv.Close()
	ch := make(chan []*ReceivedRequest)
	waitTime := 10 * time.Millisecond

	for i := 0; i < numSends; i++ {
		resp, err := http.Get(srv.URLStr())
		assert.NoErr(t, err)
		assert.Equal(t, http.StatusOK, resp.StatusCode, "status code")
	}

	go func() {
		ch <- srv.AcceptN(numSends, waitTime)
	}()
	select {
	case r := <-ch:
		assert.Equal(t, len(r), numSends, "number of recevied messages")
	case <-time.After(recvWaitTime):
		t.Errorf("AcceptN didn't return after [%+v]", recvWaitTime)
	}
}
Esempio n. 24
0
func TestErrors(t *testing.T) {
	err1 := errors.New("this is an error")
	var err2 error = nil
	assert.Err(t, err1, errors.New("this is an error"))
	assert.NoErr(t, err2)
	assert.ExistsErr(t, err1, "valid error")
}
Esempio n. 25
0
func TestNew(t *testing.T) {
	tmpDir, err := ioutil.TempDir("", "tmpdir")
	if err != nil {
		t.Fatalf("error creating temp directory (%s)", err)
	}

	defer func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			t.Fatalf("failed to remove builder-key from %s (%s)", tmpDir, err)
		}
	}()

	builderconf.BuilderKeyLocation = filepath.Join(tmpDir, "builder-key")
	data := []byte("testbuilderkey")
	if err := ioutil.WriteFile(builderconf.BuilderKeyLocation, data, 0644); err != nil {
		t.Fatalf("error creating %s (%s)", builderconf.BuilderKeyLocation, err)
	}

	host := "127.0.0.1"
	port := "80"
	cli, err := New(host, port)
	assert.NoErr(t, err)
	assert.Equal(t, cli.ControllerURL.String(), fmt.Sprintf("http://%s:%s/", host, port), "data")
	assert.Equal(t, cli.HooksToken, string(data), "data")
	assert.Equal(t, cli.UserAgent, "deis-builder", "user-agent")

	port = "invalid-port-number"
	if _, err = New(host, port); err == nil {
		t.Errorf("expected error with invalid port number, got nil")
	}
}
Esempio n. 26
0
func TestCollectEnv(t *testing.T) {
	envMap := collectEnv()
	pwd, ok := envMap["PWD"]
	assert.True(t, ok, "'PWD' not found in the env")
	wd, err := os.Getwd()
	assert.NoErr(t, err)
	assert.Equal(t, pwd, wd, "working dir")
}
Esempio n. 27
0
func TestAddUpdateData(t *testing.T) {
	mockCluster := getMockCluster(t)
	// AddUpdateData should add an "UpdateAvailable" field to any components whose versions are out-of-date
	err := AddUpdateData(&mockCluster, mocks.LatestMockData{})
	assert.NoErr(t, err)
	//TODO: when newestVersion is implemented, actually test for the addition of "UpdateAvailable" fields.
	// tracked in https://github.com/deis/workflow-manager/issues/52
}
Esempio n. 28
0
func TestPackagePath(t *testing.T) {
	gopath := "/go"
	full := "/go/src/github.com/arschles/godo"

	pkg, err := packagePath(gopath, full)
	assert.NoErr(t, err)
	assert.Equal(t, pkg, "github.com/arschles/godo", "package")
}
Esempio n. 29
0
func TestLocalDirs(t *testing.T) {
	wd, err := os.Getwd()
	assert.NoErr(t, err)
	pkgDir, err := filepath.Abs(wd + "/..")
	assert.NoErr(t, err)
	lDirs, err := localDirs(pkgDir, func(dir string) bool {
		// no directories with any dots in them
		return len(strings.Split(dir, ".")) == 1
	})
	assert.NoErr(t, err)

	expectedPackages := map[string]int{
		"cleaner":    1,
		"conf":       1,
		"controller": 1,
		"git":        1,
		"gitreceive": 1,
		"healthsrv":  1,
		"k8s":        1,
		"sshd":       1,
		"storage":    1,
		"sys":        1,
	}

	actualPackages := map[string]int{}
	for _, lDir := range lDirs {
		actualPackages[lDir]++
	}
	assert.Equal(t, len(actualPackages), len(expectedPackages), "number of packages")
	for actualPackageName, actualNum := range actualPackages {
		if actualNum != 1 {
			t.Errorf("found %d %s packages", actualNum, actualPackageName)
			continue
		}
		expectedNum, ok := expectedPackages[actualPackageName]
		if !ok {
			t.Errorf("found unexpected package %s", actualPackageName)
			continue
		}
		if actualNum != expectedNum {
			t.Errorf("found %d %s packages, expected %d", actualNum, actualPackageName, expectedNum)
			continue
		}
	}
}
Esempio n. 30
0
func TestRunningK8sDataReplicationControllers(t *testing.T) {
	c := getK8sClientForReplicationControllers(t)
	deisK8sResources := NewResourceInterfaceNamespaced(c, namespace)
	runningK8sData := NewRunningK8sData(deisK8sResources)
	rcs, err := runningK8sData.ReplicationControllers()
	assert.NoErr(t, err)
	assert.True(t, len(rcs) == 2, "rc response slice was not the expected length")
	assert.True(t, rcs[0].Data != rcs[1].Data, "rcs should not be identical")
}