diff --git a/test/e2e/README.md b/test/e2e/README.md index d46741dc..8c3c51ec 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -20,23 +20,37 @@ export KUBECONFIG=$HOME/.kube/config - Running all cases. ``` -ginkgo test/e2e/ +go test test/e2e/e2e_test.go -test.v ``` - Running all cases labeled `simplecase`. ``` -ginkgo --label-filter=simplecase test/e2e/ +go test test/e2e/e2e_test.go -test.v --ginkgo.label-filter=simplecase ``` - Skip the cases of describing information contains `Namespace`. ``` -ginkgo --skip "list namespace" test/e2e/ +go test test/e2e/e2e_test.go -test.v --ginkgo.skip="list namespace" ``` - Just run the description information contains `Namespace`'s cases. ``` -ginkgo --focus "list namespace" test/e2e/ +go test test/e2e/e2e_test.go -test.v --ginkgo.focus="list namespace" ``` + +### Supported args + +| Args | Default | Description | +| ------------------- | ------------------------------ | ----------------------------------------------------------- | +| kubernetes-host | "" | The kubernetes host, or apiserver, to connect to. | +| kubernetes-config | $KUBECONFIG | Path to config containing embedded authinfo for kubernetes. | +| kubernetes-context | current-context | config context to use for kuberentes. | +| log-dir-prefix | "" | Prefix of the log directory. | +| chart-path | ../../charts/mysql-operator | The chart name or path for mysql operator. | +| operator-image-path | radondb/mysql-operator:v2.2.0 | Image path of mysql operator. | +| sidecar-image-path | radondb/mysql57-sidecar:v2.2.0 | Image full path of mysql sidecar. | +| pod-wait-timeout | 1200 | Timeout to wait for a pod to be ready. | +| dump-logs | false | Dump logs when test case failed. | \ No newline at end of file diff --git a/test/e2e/cluster/cluster.go b/test/e2e/cluster/cluster.go new file mode 100644 index 00000000..b12af685 --- /dev/null +++ b/test/e2e/cluster/cluster.go @@ -0,0 +1,117 @@ +/* +Copyright 2021 RadonDB. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cluster + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + ginkgoTypes "github.com/onsi/ginkgo/v2/types" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/types" + + apiv1alpha1 "github.com/radondb/radondb-mysql-kubernetes/api/v1alpha1" + "github.com/radondb/radondb-mysql-kubernetes/test/e2e/framework" +) + +var _ = Describe("MySQL Cluster E2E Tests", Label("Cluster"), func() { + var ( + f *framework.Framework + cluster *apiv1alpha1.MysqlCluster + clusterKey *types.NamespacedName + two, three, five int32 = 2, 3, 5 + ) + + BeforeEach(func() { + // Init framework. + if f == nil { + By("Init framework") + f = &framework.Framework{ + BaseName: "mysqlcluster-e2e", + Log: framework.Log, + } + f.BeforeEach() + } + Expect(f).ShouldNot(BeNil(), "failed to init framework") + }) + + ReportAfterEach(func(report SpecReport) { + if report.State == ginkgoTypes.SpecStatePassed { + GinkgoWriter.Printf("%s : %s\n", report.LeafNodeText, report.RunTime) + } else { + GinkgoWriter.Printf("%s : %s\n", report.LeafNodeText, report.State) + } + }) + + // Run the full scale in/out test with label filter: Scale. + // Run only scale out(2 -> 3 -> 5): Scale out. + // Run only scale in(5 -> 3 -> 2): Scale in. + When("Test cluster scale", Label("Scale"), Ordered, func() { + // Init a cluster or get an exist cluster. + BeforeAll(func() { + clusterKey = f.InitOrGetCluster(two) + cluster = &apiv1alpha1.MysqlCluster{} + Expect(f.Client.Get(context.TODO(), *clusterKey, cluster)).To(Succeed(), "failed to get cluster %s", cluster.Name) + + By("Testing the cluster readiness") + f.WaitClusterReadiness(clusterKey) + }) + + Context("Init", Label("Init"), func() { + // BeforeAll will init a cluster or get an exist cluster. + // this container need do no thing and just for record init time. + Specify("Replicas: 0 -> 2", func() { + }) + }) + + Context("Scale out", Label("Scale out"), Ordered, func() { + // Guarantee the initial replicas is 2. + BeforeAll(func() { + f.UpdateClusterReplicas(cluster, two) + f.WaitClusterReadiness(clusterKey) + }) + + Specify("Replicas: 2 -> 3", func() { + cluster.Spec.Replicas = &three + f.UpdateClusterReplicas(cluster, three) + f.WaitClusterReadiness(clusterKey) + }) + + Specify("Replicas: 3 -> 5", func() { + f.UpdateClusterReplicas(cluster, five) + f.WaitClusterReadiness(clusterKey) + }) + }) + + Context("Scale in", Label("Scale In"), Ordered, func() { + // Guarantee the initial replicas is 5. + BeforeAll(func() { + f.UpdateClusterReplicas(cluster, five) + f.WaitClusterReadiness(clusterKey) + }) + + Specify("Replicas: 5 -> 3", func() { + f.UpdateClusterReplicas(cluster, three) + f.WaitClusterReadiness(clusterKey) + }) + Specify("Replicas: 3 -> 2", func() { + f.UpdateClusterReplicas(cluster, two) + f.WaitClusterReadiness(clusterKey) + }) + }) + }) +}) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 35795203..ae90a53e 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -27,11 +27,9 @@ import ( "testing" . "github.com/onsi/ginkgo/v2" - "github.com/onsi/ginkgo/v2/types" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtimeutils "k8s.io/apimachinery/pkg/util/runtime" clientset "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client" @@ -41,6 +39,7 @@ import ( // Test case source. // Comment out the package that you don't want to run. + _ "github.com/radondb/radondb-mysql-kubernetes/test/e2e/cluster" _ "github.com/radondb/radondb-mysql-kubernetes/test/e2e/simplecase" ) @@ -77,13 +76,13 @@ var _ = SynchronizedBeforeSuite(func() []byte { if framework.TestContext.DumpLogs { By("Create log dir") - os.MkdirAll(fmt.Sprintf("%s_%d", framework.TestContext.ReportDirPrefix, GinkgoRandomSeed()), 0777) + os.MkdirAll(fmt.Sprintf("%s_%d", framework.TestContext.LogDirPrefix, GinkgoRandomSeed()), 0777) } - By("Install RadonDB MySQL Operator") + By("Install operator") framework.HelmInstallChart(framework.OperatorReleaseName, framework.RadondbMysqlE2eNamespace) return nil -}, func(data []byte) { +}, func(_ []byte) { framework.Logf("Running BeforeSuite actions on all node") }) @@ -102,7 +101,7 @@ var _ = SynchronizedAfterSuite(func() { client, err := clientset.NewForConfig(kubeCfg) Expect(err).NotTo(HaveOccurred()) - By("Remove operator release") + By("Remove operator") framework.HelmPurgeRelease(framework.OperatorReleaseName, framework.RadondbMysqlE2eNamespace) By("Delete test namespace") @@ -115,40 +114,18 @@ var _ = SynchronizedAfterSuite(func() { var _ = ReportAfterSuite("Collect log", func(report Report) { if framework.TestContext.DumpLogs { - f, err := os.OpenFile(path.Join(fmt.Sprintf("%s_%d", framework.TestContext.ReportDirPrefix, GinkgoRandomSeed()), "overview.txt"), os.O_RDWR|os.O_CREATE, 0644) + f, err := os.OpenFile(path.Join(fmt.Sprintf("%s_%d", framework.TestContext.LogDirPrefix, GinkgoRandomSeed()), "overview.txt"), os.O_RDWR|os.O_CREATE, 0644) if err != nil { fmt.Println(err) return } - // Get the kubernetes client. - kubeCfg, err := framework.LoadConfig() - if err != nil { - fmt.Println("Failed to get kubeconfig!") - return - } - client, err := clientset.NewForConfig(kubeCfg) - if err != nil { - fmt.Println("Failed to create k8s client!") - return - } for _, specReport := range report.SpecReports { - // Collect the summary of all cases. - fmt.Fprintf(f, "%s | %s\n", specReport.FullText(), specReport.State) - // Collect the POD log of failure cases. - if specReport.State.Is(types.SpecStateFailed) { - fileName := fmt.Sprintf("%v.txt", specReport.ContainerHierarchyTexts[len(specReport.ContainerHierarchyTexts)-1]) - logFile, err := os.OpenFile(path.Join(fmt.Sprintf("%s_%d", framework.TestContext.ReportDirPrefix, GinkgoRandomSeed()), fileName), os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - fmt.Printf("Failed to open file: %s with error: %s\n", fileName, err) - continue - } - fmt.Fprintf(logFile, "## Start test: %v\n", specReport.ContainerHierarchyTexts) - - framework.LogPodsWithLabels(client, framework.RadondbMysqlE2eNamespace, nil, time.Since(specReport.EndTime.Add(-1*time.Minute)), logFile) - - fmt.Fprintf(logFile, "## END test\n") - logFile.Close() + + if specReport.FullText() != "" { + // Collect the summary of all cases. + fmt.Fprintf(f, "%s | %s | %v\n", specReport.FullText(), specReport.State, specReport.RunTime) } + // TODO: Collect the POD log of failure cases. } f.Close() } @@ -160,8 +137,6 @@ var _ = ReportAfterSuite("Collect log", func(report Report) { // generated in this directory, and cluster logs will also be saved. // This function is called on each Ginkgo node in parallel mode. func RunE2ETests(t *testing.T) { - runtimeutils.ReallyCrash = true - RegisterFailHandler(ginkgowrapper.Fail) // Fetch the current config. @@ -170,15 +145,13 @@ func RunE2ETests(t *testing.T) { reporterConfig.FullTrace = true // Whether printing more detail. reporterConfig.Verbose = true - // Whether to display information of GinkgoWriter. - reporterConfig.AlwaysEmitGinkgoWriter = true if framework.TestContext.DumpLogs { - if framework.TestContext.ReportDirPrefix == "" { + if framework.TestContext.LogDirPrefix == "" { now := time.Now() - framework.TestContext.ReportDirPrefix = fmt.Sprintf("logs_%d%d_%d%d", now.Month(), now.Day(), now.Hour(), now.Minute()) + framework.TestContext.LogDirPrefix = fmt.Sprintf("logs_%d%d_%d%d", now.Month(), now.Day(), now.Hour(), now.Minute()) } // Path of JUnitReport. - reporterConfig.JUnitReport = path.Join(fmt.Sprintf("%s_%d", framework.TestContext.ReportDirPrefix, GinkgoRandomSeed()), "junit.xml") + reporterConfig.JUnitReport = path.Join(fmt.Sprintf("%s_%d", framework.TestContext.LogDirPrefix, GinkgoRandomSeed()), "junit.xml") } RunSpecs(t, "MySQL Operator E2E Suite", Label("MySQL Operator"), suiteConfig, reporterConfig) diff --git a/test/e2e/framework/cluster_util.go b/test/e2e/framework/cluster_util.go new file mode 100644 index 00000000..763cd6a9 --- /dev/null +++ b/test/e2e/framework/cluster_util.go @@ -0,0 +1,178 @@ +/* +Copyright 2018 Pressinfra SRL + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package framework + +import ( + "context" + "fmt" + "math/rand" + "time" + + _ "github.com/go-sql-driver/mysql" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + apiv1alpha1 "github.com/radondb/radondb-mysql-kubernetes/api/v1alpha1" + "github.com/radondb/radondb-mysql-kubernetes/utils" +) + +func newCluster(name, ns string, replicas int32) *apiv1alpha1.MysqlCluster { + return &apiv1alpha1.MysqlCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: apiv1alpha1.MysqlClusterSpec{ + Replicas: &replicas, + MysqlVersion: TestContext.MysqlVersion, + PodPolicy: apiv1alpha1.PodPolicy{ + SidecarImage: TestContext.SidecarImagePath, + }, + }, + } +} + +func (f *Framework) InitAClusterForTesting(replicas int32) string { + // Be careful, mysql allowed hostname lenght is <63. + cluster := newCluster(fmt.Sprintf("cl-%d", rand.Int31()/1000), f.Namespace.Name, replicas) + if err := f.Client.Create(context.TODO(), cluster); err != nil { + return "" + } + return cluster.Name +} + +func (f *Framework) getExistCluster() *apiv1alpha1.MysqlCluster { + existClusters := &apiv1alpha1.MysqlClusterList{} + Expect(f.Client.List(context.TODO(), existClusters, &client.ListOptions{ + Namespace: RadondbMysqlE2eNamespace, + })).To(Succeed(), "failed to list clusters") + + if len(existClusters.Items) > 0 { + return &existClusters.Items[0] + } + return &apiv1alpha1.MysqlCluster{} +} + +func (f *Framework) InitOrGetCluster(replicas int32) *types.NamespacedName { + clusterKey := &types.NamespacedName{Namespace: RadondbMysqlE2eNamespace} + if cluster := f.getExistCluster(); cluster.Name != "" { + clusterKey.Name = cluster.Name + cluster.Spec.Replicas = &replicas + Expect(f.Client.Update(context.TODO(), cluster)).To(Succeed(), "failed to update clusters") + } else { + clusterKey.Name = f.InitAClusterForTesting(replicas) + } + return clusterKey +} + +func (f *Framework) UpdateClusterReplicas(cluster *apiv1alpha1.MysqlCluster, replicas int32) { + Expect(f.Client.Get(context.TODO(), client.ObjectKeyFromObject(cluster), cluster)).To(Succeed(), "failed to get cluster %s", cluster.Name) + cluster.Spec.Replicas = &replicas + Expect(f.Client.Update(context.TODO(), cluster)).To(Succeed()) +} + +// WaitClusterReadiness determine whether the cluster is ready. +func (f *Framework) WaitClusterReadiness(clusterKey *types.NamespacedName) { + cluster := &apiv1alpha1.MysqlCluster{} + if err := f.Client.Get(context.TODO(), *clusterKey, cluster); err != nil { + Failf(fmt.Sprintf("Failed to get cluster %s", clusterKey.String())) + } + timeout := f.Timeout + if *cluster.Spec.Replicas > 0 { + timeout = time.Duration(*cluster.Spec.Replicas) * f.Timeout + } + // Wait for pods to be ready. + f.ClusterEventuallyReplicas(cluster, timeout) + // Wait for xenon to be ready. + f.ClusterEventuallyRaftStatus(cluster) +} + +func (f *Framework) ClusterEventuallyReplicas(cluster *apiv1alpha1.MysqlCluster, timeout time.Duration) { + Eventually(func() int { + cl := &apiv1alpha1.MysqlCluster{} + f.Client.Get(context.TODO(), types.NamespacedName{Name: cluster.Name, Namespace: cluster.Namespace}, cl) + return cl.Status.ReadyNodes + }, timeout, POLLING).Should(Equal(int(*cluster.Spec.Replicas)), "Not ready replicas of cluster '%s'", cluster.Name) +} + +func (f *Framework) ClusterEventuallyRaftStatus(cluster *apiv1alpha1.MysqlCluster) { + Eventually(func() bool { + cl := &apiv1alpha1.MysqlCluster{} + f.Client.Get(context.TODO(), types.NamespacedName{Name: cluster.Name, Namespace: cluster.Namespace}, cl) + return isXenonReadiness(cl) + }, TIMEOUT, POLLING).Should(BeTrue(), "Not ready xenon of cluster '%s'", cluster.Name) +} + +// isXenonReadiness determine whether the role of the cluster is normal. +// 1. Cluster must have Leader node. +// 2. Cluster can only have a Leader node. +func isXenonReadiness(cluster *apiv1alpha1.MysqlCluster) bool { + leader := "" + invalidCount := 0 + for _, node := range cluster.Status.Nodes { + switch node.RaftStatus.Role { + case string(utils.Leader): + if leader != "" { + return false + } + leader = node.Name + case string(utils.Follower): + default: + invalidCount++ + } + } + + return invalidCount == 0 && leader != "" +} + +// GetClusterLabels returns labels.Set for the given cluster. +func GetClusterLabels(cluster *apiv1alpha1.MysqlCluster) labels.Set { + labels := labels.Set{ + "mysql.radondb.com/cluster": cluster.Name, + "app.kubernetes.io/name": "mysql", + } + + return labels +} + +func (f *Framework) GetClusterPVCsFn(cluster *apiv1alpha1.MysqlCluster) func() []corev1.PersistentVolumeClaim { + return func() []corev1.PersistentVolumeClaim { + pvcList := &corev1.PersistentVolumeClaimList{} + lo := &client.ListOptions{ + Namespace: cluster.Namespace, + LabelSelector: labels.SelectorFromSet(GetClusterLabels(cluster)), + } + f.Client.List(context.TODO(), pvcList, lo) + return pvcList.Items + } +} + +func (f *Framework) GetClusterPods(cluster *apiv1alpha1.MysqlCluster) func() []corev1.Pod { + return func() []corev1.Pod { + podList := &corev1.PodList{} + lo := &client.ListOptions{ + Namespace: cluster.Namespace, + LabelSelector: labels.SelectorFromSet(GetClusterLabels(cluster)), + } + f.Client.List(context.TODO(), podList, lo) + return podList.Items + } +} diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index e98d3899..7f1a23a9 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -59,7 +59,7 @@ func (f *Framework) BeforeEach() { // https://github.com/onsi/ginkgo/issues/222 f.Timeout = time.Duration(TestContext.TimeoutSeconds) * time.Second - By("creating a kubernetes client") + By("Creating a kubernetes client") cfg, err := LoadConfig() Expect(err).NotTo(HaveOccurred()) diff --git a/test/e2e/framework/helm.go b/test/e2e/framework/helm.go index 2ae02282..09b4fea0 100644 --- a/test/e2e/framework/helm.go +++ b/test/e2e/framework/helm.go @@ -20,18 +20,23 @@ import ( "fmt" "os" "os/exec" + "strings" . "github.com/onsi/gomega" ) func HelmInstallChart(release, ns string) { + strs := strings.Split(TestContext.OperatorImagePath, ":") + if len(strs) != 2 { + Failf(fmt.Sprintf("Invalid operator image path: %s", TestContext.OperatorImagePath)) + } args := []string{ "install", release, "./" + TestContext.ChartPath, "--namespace", ns, "--values", TestContext.ChartValues, "--wait", "--kube-context", TestContext.KubeContext, - "--set", fmt.Sprintf("manager.image=%s", TestContext.OperatorImagePath), - "--set", fmt.Sprintf("manager.tag=%s", TestContext.OperatorImageTag), + "--set", fmt.Sprintf("manager.image=%s", strs[0]), + "--set", fmt.Sprintf("manager.tag=%s", strs[1]), } cmd := exec.Command("helm", args...) diff --git a/test/e2e/framework/test_context.go b/test/e2e/framework/test_context.go index d9b95d4a..c6326669 100644 --- a/test/e2e/framework/test_context.go +++ b/test/e2e/framework/test_context.go @@ -27,25 +27,25 @@ import ( var Log = logf.Log.WithName("framework.util") +// Default values of the test config. const ( - // The namespace where the resource created by E2E. - RadondbMysqlE2eNamespace = "radondb-mysql-e2e" - // The name of the Operator to create. - OperatorReleaseName = "e2e-test" // Export POD logs and test overview. DumpLogs = true // Optional directory to store junit and pod logs output in. // If not specified, it will beset to the current date. - ReportDirPrefix = "" + LogDirPrefix = "" // Specify the directory that Helm Install will be executed. ChartPath = "../../charts/mysql-operator" // Image path of mysql operator. - OperatorImagePath = "radondb/mysql-operator" - // Image tag of mysql operator. - OperatorImageTag = "latest" + OperatorImagePath = "radondb/mysql-operator:v2.2.0-alpha.1" // Image path for mysql sidecar. - SidecarImage = "radondb/mysql-sidecar:latest" + SidecarImagePath = "radondb/mysql57-sidecar:v2.2.0-alpha.1" + + // The namespace where the resource created by E2E. + RadondbMysqlE2eNamespace = "radondb-mysql-e2e" + // The name of the Operator to create. + OperatorReleaseName = "e2e-test" // How often to Poll pods, nodes and claims. Poll = 2 * time.Second @@ -64,14 +64,15 @@ type TestContextType struct { KubeConfig string KubeContext string - ReportDirPrefix string + LogDirPrefix string ChartPath string ChartValues string OperatorImagePath string - OperatorImageTag string - SidecarImage string + SidecarImagePath string + + MysqlVersion string TimeoutSeconds int DumpLogs bool @@ -85,15 +86,16 @@ func RegisterCommonFlags() { flag.StringVar(&TestContext.KubeConfig, "kubernetes-config", os.Getenv(clientcmd.RecommendedConfigPathEnvVar), "Path to config containing embedded authinfo for kubernetes. Default value is from environment variable "+clientcmd.RecommendedConfigPathEnvVar) flag.StringVar(&TestContext.KubeContext, "kubernetes-context", "", "config context to use for kuberentes. If unset, will use value from 'current-context'") - flag.StringVar(&TestContext.ReportDirPrefix, "report-dir", ReportDirPrefix, "Optional directory to store logs output in.") + flag.StringVar(&TestContext.LogDirPrefix, "log-dir-prefix", LogDirPrefix, "Prefix of the log directory.") + + flag.StringVar(&TestContext.ChartPath, "chart-path", ChartPath, "The chart name or path for mysql operator") + flag.StringVar(&TestContext.OperatorImagePath, "operator-image-path", OperatorImagePath, "Image path of mysql operator.") + flag.StringVar(&TestContext.SidecarImagePath, "sidecar-image-path", SidecarImagePath, "Image path of mysql sidecar.") - flag.StringVar(&TestContext.ChartPath, "operator-chart-path", ChartPath, "The chart name or path for mysql operator") - flag.StringVar(&TestContext.OperatorImagePath, "operator-image-path", OperatorImagePath, "Image tag of mysql operator.") - flag.StringVar(&TestContext.OperatorImageTag, "operator-image-tag", OperatorImageTag, "Image tag of mysql operator.") - flag.StringVar(&TestContext.SidecarImage, "sidecar-image", SidecarImage, "Image path of mysql sidecar.") + flag.StringVar(&TestContext.MysqlVersion, "mysql-version", "5.7", "The version of mysql to be installed.") flag.IntVar(&TestContext.TimeoutSeconds, "pod-wait-timeout", 1200, "Timeout to wait for a pod to be ready.") - flag.BoolVar(&TestContext.DumpLogs, "dump-logs-on-failure", DumpLogs, "Dump logs.") + flag.BoolVar(&TestContext.DumpLogs, "dump-logs", false, "Dump logs when test case failed.") } func RegisterParseFlags() {