Repository: kubernetes-sigs/gateway-api

Issue:
When Mesh Resource for populating Supported Features becomes available, update Conformance Suite.
**What would you like to be added**:
After Mesh has support for populating supported features in its [CRD](https://github.com/kubernetes-sigs/gateway-api/issues/3949), like GatewayClass does in it's Status struct, we need to modify conformance test suite to instead of ignoring mesh features for conformance report testing to running the tests based on populated features.

**Why this is needed**:
To simplify and automate conformance reporting for Mesh features.


Discussion:
@bexxmodd: /assign

Solution patch:
diff --git a/conformance/conformance.go b/conformance/conformance.go
index 39213105..642a6eb2 100644
--- a/conformance/conformance.go
+++ b/conformance/conformance.go
@@ -93,6 +93,7 @@ func DefaultOptions(t *testing.T) suite.ConformanceOptions {
 		ExemptFeatures:             exemptFeatures,
 		ManifestFS:                 []fs.FS{&Manifests},
 		GatewayClassName:           *flags.GatewayClassName,
+		MeshName:                   *flags.MeshName,
 		Implementation:             implementation,
 		Mode:                       *flags.Mode,
 		NamespaceAnnotations:       namespaceAnnotations,
diff --git a/conformance/utils/flags/flags.go b/conformance/utils/flags/flags.go
index 95da9eaa..1465df68 100644
--- a/conformance/utils/flags/flags.go
+++ b/conformance/utils/flags/flags.go
@@ -30,6 +30,7 @@ const (
 
 var (
 	GatewayClassName           = flag.String("gateway-class", "gateway-conformance", "Name of GatewayClass to use for tests")
+	MeshName                   = flag.String("mesh-name", "", "Name of Mesh to use for tests")
 	ShowDebug                  = flag.Bool("debug", false, "Whether to print debug logs")
 	CleanupBaseResources       = flag.Bool("cleanup-base-resources", true, "Whether to cleanup base test resources after the run")
 	SupportedFeatures          = flag.String("supported-features", "", "Supported features included in conformance tests suites")
diff --git a/conformance/utils/suite/conformance.go b/conformance/utils/suite/conformance.go
index 436b3ed2..12db14d6 100644
--- a/conformance/utils/suite/conformance.go
+++ b/conformance/utils/suite/conformance.go
@@ -68,7 +68,7 @@ func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) {
 
 	for _, manifestLocation := range test.Manifests {
 		tlog.Logf(t, "Applying %s", manifestLocation)
-		suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, suite.Cleanup)
+		suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, true)
 	}
 
 	if featuresInfo != "" {
diff --git a/conformance/utils/suite/suite.go b/conformance/utils/suite/suite.go
index 31db06c5..8dcd009e 100644
--- a/conformance/utils/suite/suite.go
+++ b/conformance/utils/suite/suite.go
@@ -39,6 +39,7 @@ import (
 
 	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
 	"sigs.k8s.io/gateway-api/apis/v1beta1"
+	xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
 	confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
 	"sigs.k8s.io/gateway-api/conformance/utils/config"
 	"sigs.k8s.io/gateway-api/conformance/utils/flags"
@@ -65,6 +66,7 @@ type ConformanceTestSuite struct {
 	RoundTripper             roundtripper.RoundTripper
 	GRPCClient               grpc.Client
 	GatewayClassName         string
+	MeshName                 string
 	ControllerName           string
 	Debug                    bool
 	Cleanup                  bool
@@ -136,6 +138,7 @@ type ConformanceOptions struct {
 	Clientset            clientset.Interface
 	RestConfig           *rest.Config
 	GatewayClassName     string
+	MeshName             string
 	AddressType          string
 	Debug                bool
 	RoundTripper         roundtripper.RoundTripper
@@ -206,20 +209,23 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite,
 		supportedFeatures = features.SetsToNamesSet(features.AllFeatures)
 	} else if shouldInferSupportedFeatures(&options) {
 		var err error
-		supportedFeatures, err = fetchSupportedFeatures(options.Client, options.GatewayClassName)
-		if err != nil {
-			return nil, fmt.Errorf("cannot infer supported features: %w", err)
+		if options.GatewayClassName != "" {
+			supportedFeatures, err = fetchGatewayClassSupportedFeatures(options.Client, options.GatewayClassName)
+			if err != nil {
+				return nil, fmt.Errorf("cannot infer supported features from GWC: %w", err)
+			}
 		}
 
-		// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
-		meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
-		for _, f := range supportedFeatures.UnsortedList() {
-			if meshFeatureNames.Has(f) {
-				supportedFeatures.Delete(f)
-				fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
+		supportedMeshFeatures := FeaturesSet{}
+		if options.MeshName != "" {
+			supportedMeshFeatures, err = fetchMeshSupportedFeatures(options.Client, options.MeshName)
+			if err != nil {
+				return nil, fmt.Errorf("cannot infer supported features from XMesh: %w", err)
 			}
 		}
+
 		source = supportedFeaturesSourceInferred
+		supportedFeatures = supportedFeatures.Union(supportedMeshFeatures)
 	}
 
 	// If features were not inferred from Status, it's a GWC issue.
@@ -606,24 +612,59 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
 	return res
 }
 
-func fetchSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
+func fetchGatewayClassSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
 	if gatewayClassName == "" {
 		return nil, fmt.Errorf("GatewayClass name must be provided to fetch supported features")
 	}
 	gwc := &gatewayv1.GatewayClass{}
 	err := client.Get(context.TODO(), types.NamespacedName{Name: gatewayClassName}, gwc)
 	if err != nil {
-		return nil, fmt.Errorf("fetchSupportedFeatures(): %w", err)
+		return nil, fmt.Errorf("fetchGatewayClassSupportedFeatures(): %w", err)
 	}
 
 	fs := FeaturesSet{}
 	for _, feature := range gwc.Status.SupportedFeatures {
 		fs.Insert(features.FeatureName(feature.Name))
 	}
+
+	// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
+	meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
+	for _, f := range fs.UnsortedList() {
+		if meshFeatureNames.Has(f) {
+			fs.Delete(f)
+			fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
+		}
+	}
 	fmt.Printf("Supported features for GatewayClass %s: %v\n", gatewayClassName, fs.UnsortedList())
 	return fs, nil
 }
 
+func fetchMeshSupportedFeatures(client client.Client, meshName string) (FeaturesSet, error) {
+	if meshName == "" {
+		return nil, fmt.Errorf("mesh name must be provided to fetch supported features")
+	}
+	xmesh := &xmeshv1alpha1.XMesh{}
+	err := client.Get(context.TODO(), types.NamespacedName{Name: meshName}, xmesh)
+	if err != nil {
+		return nil, fmt.Errorf("fetchMeshSupportedFeatures(): %w", err)
+	}
+
+	fs := FeaturesSet{}
+	for _, feature := range xmesh.Status.SupportedFeatures {
+		fs.Insert(features.FeatureName(feature.Name))
+	}
+
+	gwcFeatureNames := features.SetsToNamesSet(features.GatewayCoreFeatures, features.GatewayExtendedFeatures)
+	for _, f := range fs.UnsortedList() {
+		if gwcFeatureNames.Has(f) {
+			fs.Delete(f)
+			fmt.Printf("WARNING: Mesh feature %q should not be populated in XMesh.Status, skipping...", f)
+		}
+	}
+	fmt.Printf("Supported features for XMesh%s: %v\n", meshName, fs.UnsortedList())
+	return fs, nil
+}
+
 // shouldInferSupportedFeatures checks if any flags were supplied for manually
 // picking what to test. Inferred supported features are only used when no flags
 // are set.
diff --git a/geps/gep-696/index.md b/geps/gep-696/index.md
index 6082b309..da58798f 100644
--- a/geps/gep-696/index.md
+++ b/geps/gep-696/index.md
@@ -13,16 +13,27 @@
 
 (Primary goals of this proposal.)
 
+## Longer Term Goals (optional)
+
+(goals that are not covered initially on this proposal but may be considered long term)
+
 ## Non-Goals
 
 (What is out of scope for this proposal.)
 
-## Introduction
+## Introduction/Overview
 
 (Can link to external doc -- but we should bias towards copying
 the content into the GEP as online documents are easier to lose
 -- e.g. owner messes up the permissions, accidental deletion)
 
+Write here "What" we want to do. What is the proposal aiming to do?
+
+## Purpose (Why and Who)
+
+Write here "Why" we want to do it. What problems are being solved? What personas are
+the target of this proposal, and why will this proposal will make their lives better?
+
 ## API
 (... details, can point to PR with changes)
 
@@ -32,6 +43,17 @@ the content into the GEP as online documents are easier to lose
 ### Gateway For Mesh (East/West)
 (Include East/West API considerations, examples, and if different - APIs)
 
+## Request flow
+Example on the usage flow of this proposal/enhancement. It is suggested to contain
+at least one manifest as example.
+
+Example of a flow description:
+
+* A client makes a request to https://foo.example.com.
+* DNS resolves the name to a `Gateway` address.
+* The reverse proxy receives the request on a `Listener` and does something with it
+* The reverse proxy passes the request through `XPTORoute` modifying the headers to contain `XYZ`
+
 ## Conformance Details
 
 (from https://github.com/kubernetes-sigs/gateway-api/blob/main/geps/gep-2162/index.md#standardize-features-and-conformance-tests-names)
@@ -60,6 +82,11 @@ In any case, the conformance tests file names should be meaningful and easy to u
 
 (Make sure to also include conformance tests that cover mesh)
 
+When describing the new feature, write down some conformance test scenarios the feature should manage,
+to guarantee that future implementors understand what "Conformance" means and what will be tested.
+
+At least _some_ tests should be added at each phase, starting with Provisional.
+
 ## Alternatives
 
 (List other design alternatives and why we did not go in that
diff --git a/go.mod b/go.mod
index 070b21f6..8842e5f9 100644
--- a/go.mod
+++ b/go.mod
@@ -11,14 +11,14 @@ require (
 	google.golang.org/grpc v1.75.0
 	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
 	google.golang.org/protobuf v1.36.8
-	k8s.io/api v0.34.0
-	k8s.io/apiextensions-apiserver v0.34.0
-	k8s.io/apimachinery v0.34.0
-	k8s.io/client-go v0.34.0
-	k8s.io/code-generator v0.34.0
+	k8s.io/api v0.34.1
+	k8s.io/apiextensions-apiserver v0.34.1
+	k8s.io/apimachinery v0.34.1
+	k8s.io/client-go v0.34.1
+	k8s.io/code-generator v0.34.1
 	k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3
 	k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
-	sigs.k8s.io/controller-runtime v0.21.0
+	sigs.k8s.io/controller-runtime v0.22.1
 	sigs.k8s.io/controller-tools v0.18.0
 	sigs.k8s.io/structured-merge-diff/v6 v6.3.0
 	sigs.k8s.io/yaml v1.6.0
@@ -28,7 +28,8 @@ require (
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver v1.5.0 // indirect
 	github.com/Masterminds/sprig v2.22.0+incompatible // indirect
-	github.com/blang/semver/v4 v4.0.0 // indirect
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/emicklei/go-restful/v3 v3.13.0 // indirect
 	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
@@ -63,6 +64,8 @@ require (
 	github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/prometheus/client_golang v1.23.0 // indirect
+	github.com/prometheus/client_model v0.6.2 // indirect
+	github.com/prometheus/common v0.65.0 // indirect
 	github.com/prometheus/procfs v0.17.0 // indirect
 	github.com/spf13/cobra v1.9.1 // indirect
 	github.com/spf13/pflag v1.0.7 // indirect
diff --git a/go.sum b/go.sum
index 6abcd96c..496ee74b 100644
--- a/go.sum
+++ b/go.sum
@@ -8,8 +8,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
-github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -245,16 +243,16 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
-k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
-k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc=
-k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0=
-k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
-k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
-k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo=
-k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY=
-k8s.io/code-generator v0.34.0 h1:Ze2i1QsvUprIlX3oHiGv09BFQRLCz+StA8qKwwFzees=
-k8s.io/code-generator v0.34.0/go.mod h1:Py2+4w2HXItL8CGhks8uI/wS3Y93wPKO/9mBQUYNua0=
+k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
+k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
+k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
+k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
+k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
+k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
+k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/code-generator v0.34.1 h1:WpphT26E+j7tEgIUfFr5WfbJrktCGzB3JoJH9149xYc=
+k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg=
 k8s.io/gengo/v2 v2.0.0-20250820003526-c297c0c1eb9d h1:qUrYOinhdAUL0xxhA4gPqogPBaS9nIq2l2kTb6pmeB0=
 k8s.io/gengo/v2 v2.0.0-20250820003526-c297c0c1eb9d/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
@@ -263,8 +261,8 @@ k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 h1:liMHz39T5dJO1aOKHLvwaC
 k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
-sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
+sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg=
+sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
 sigs.k8s.io/controller-tools v0.18.0 h1:rGxGZCZTV2wJreeRgqVoWab/mfcumTMmSwKzoM9xrsE=
 sigs.k8s.io/controller-tools v0.18.0/go.mod h1:gLKoiGBriyNh+x1rWtUQnakUYEujErjXs9pf+x/8n1U=
 sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=



Test patch:
diff --git a/conformance/utils/suite/suite_test.go b/conformance/utils/suite/suite_test.go
index 778afa71..674e7480 100644
--- a/conformance/utils/suite/suite_test.go
+++ b/conformance/utils/suite/suite_test.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+	xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
 	confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
 	"sigs.k8s.io/gateway-api/pkg/consts"
 	"sigs.k8s.io/gateway-api/pkg/features"
@@ -411,7 +412,7 @@ func TestSuiteReport(t *testing.T) {
 	}
 }
 
-var statusFeatureNames = []string{
+var gwcStatusFeatureNames = []string{
 	"Gateway",
 	"GatewayPort8080",
 	"HTTPRoute",
@@ -423,7 +424,7 @@ var statusFeatureNames = []string{
 	"ReferenceGrant",
 }
 
-func TestInferSupportedFeatures(t *testing.T) {
+func TestInferGWCSupportedFeatures(t *testing.T) {
 	testCases := []struct {
 		name               string
 		allowAllFeatures   bool
@@ -435,7 +436,7 @@ func TestInferSupportedFeatures(t *testing.T) {
 	}{
 		{
 			name:             "properly infer supported features",
-			expectedFeatures: namesToFeatureSet(statusFeatureNames),
+			expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
 			expectedSource:   supportedFeaturesSourceInferred,
 		},
 		{
@@ -460,7 +461,7 @@ func TestInferSupportedFeatures(t *testing.T) {
 		{
 			name:               "supports conformance profile - core",
 			ConformanceProfile: sets.New(GatewayHTTPConformanceProfileName),
-			expectedFeatures:   namesToFeatureSet(statusFeatureNames),
+			expectedFeatures:   namesToFeatureSet(gwcStatusFeatureNames),
 			expectedSource:     supportedFeaturesSourceInferred,
 		},
 	}
@@ -482,7 +483,7 @@ func TestInferSupportedFeatures(t *testing.T) {
 					Message: "GatewayClass is accepted and ready for use",
 				},
 			},
-			SupportedFeatures: featureNamesToSet(statusFeatureNames),
+			SupportedFeatures: featureNamesToSet(gwcStatusFeatureNames),
 		},
 	}
 	scheme := runtime.NewScheme()
@@ -524,6 +525,113 @@ func TestInferSupportedFeatures(t *testing.T) {
 	}
 }
 
+var meshStatusFeatureNames = []string{
+	"Mesh",
+	"MeshClusterIPMatching",
+	"MeshNamespaceSelector",
+	"MeshServiceAccountSelector",
+	"MeshTLS",
+	"MeshTLSClientCert",
+	"MeshTrafficSplit",
+	"MeshAccessControl",
+	"HTTPRoute",
+}
+
+func TestXMeshInferSupportedFeatures(t *testing.T) {
+	testCases := []struct {
+		name               string
+		allowAllFeatures   bool
+		supportedFeatures  FeaturesSet
+		exemptFeatures     FeaturesSet
+		ConformanceProfile sets.Set[ConformanceProfileName]
+		expectedFeatures   FeaturesSet
+		expectedSource     supportedFeaturesSource
+	}{
+		{
+			name:             "properly infer mesh supported features",
+			expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
+			expectedSource:   supportedFeaturesSourceInferred,
+		},
+		{
+			name:              "no features",
+			supportedFeatures: sets.New[features.FeatureName]("Mesh"),
+			expectedFeatures:  sets.New[features.FeatureName]("Mesh"),
+			expectedSource:    supportedFeaturesSourceManual,
+		},
+		{
+			name:              "remove exempt features",
+			supportedFeatures: sets.New[features.FeatureName]("MeshTLS", "MeshAccessControl"),
+			exemptFeatures:    sets.New[features.FeatureName]("MeshAccessControl"),
+			expectedFeatures:  sets.New[features.FeatureName]("MeshTLS"),
+			expectedSource:    supportedFeaturesSourceManual,
+		},
+		{
+			name:               "supports conformance profile - core",
+			ConformanceProfile: sets.New(MeshHTTPConformanceProfileName),
+			expectedFeatures:   namesToFeatureSet(meshStatusFeatureNames),
+			expectedSource:     supportedFeaturesSourceInferred,
+		},
+	}
+
+	meshName := "xochopintre"
+	xmesh := &xmeshv1alpha1.XMesh{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: meshName,
+		},
+		Spec: xmeshv1alpha1.MeshSpec{
+			ControllerName: "example.com/mesh-controller",
+		},
+		Status: xmeshv1alpha1.MeshStatus{
+			Conditions: []metav1.Condition{
+				{
+					Type:    string(xmeshv1alpha1.MeshConditionAccepted),
+					Status:  metav1.ConditionTrue,
+					Reason:  "Accepted",
+					Message: "XMesh is accepted and ready for use",
+				},
+			},
+			SupportedFeatures: featureNamesToSet(meshStatusFeatureNames),
+		},
+	}
+	scheme := runtime.NewScheme()
+	scheme.AddKnownTypes(xmeshv1alpha1.SchemeGroupVersion, &xmeshv1alpha1.XMesh{})
+	fakeClient := fake.NewClientBuilder().
+		WithScheme(scheme).
+		WithObjects(xmesh).
+		WithLists(&apiextensionsv1.CustomResourceDefinitionList{}).
+		Build()
+
+	xmeshv1alpha1.Install(fakeClient.Scheme())
+	apiextensionsv1.AddToScheme(fakeClient.Scheme())
+
+	for _, tc := range testCases {
+		options := ConformanceOptions{
+			AllowCRDsMismatch:          true,
+			MeshName:                   meshName,
+			EnableAllSupportedFeatures: tc.allowAllFeatures,
+			SupportedFeatures:          tc.supportedFeatures,
+			ExemptFeatures:             tc.exemptFeatures,
+			ConformanceProfiles:        tc.ConformanceProfile,
+			Client:                     fakeClient,
+		}
+
+		t.Run(tc.name, func(t *testing.T) {
+			cSuite, err := NewConformanceTestSuite(options)
+			if err != nil {
+				t.Fatalf("error initializing conformance suite: %v", err)
+			}
+
+			if cSuite.supportedFeaturesSource != tc.expectedSource {
+				t.Errorf("InferredSupportedFeatures mismatch: got %v, want %v", cSuite.supportedFeaturesSource, tc.expectedSource)
+			}
+
+			if equal := cSuite.SupportedFeatures.Equal(tc.expectedFeatures); !equal {
+				t.Errorf("SupportedFeatures mismatch: got %v, want %v", cSuite.SupportedFeatures.UnsortedList(), tc.expectedFeatures.UnsortedList())
+			}
+		})
+	}
+}
+
 func TestGWCPublishedMeshFeatures(t *testing.T) {
 	gwcName := "ochopintre"
 	gwc := &gatewayv1.GatewayClass{