fix(gazelle): Do not create invalid py_test rules in `project` generation mode (#1809)

Since https://github.com/bazelbuild/rules_python/pull/1538, when using
`gazelle:python_generation_mode project`, a `py_test` rule is created
even when there are no test files in the project.
fb673ee47b3268a65a18a154edd574b6509c38c7 (first commit on the PR branch)
reproduces this issue - it shows that a `py_test` rule is created even
when there is no test entrypoint file (`__test__.py`) nor any test file
in the entire project. Additionally, test rules created on `project` or
`package` generation mode will always set `main = "__test__.py"`, even
when that file doesn't exist.

This PR fixes it by only generating a `py_test` rule if it can find some
test file - either an explicit `__test__.py`, or any other file prefixed
with `test_` or suffixed with `_test.py`. It also changes the generated
test rule to only add `main = "__test__.py"` if there is an actual
`__test__.py` file. Note that, in the case where a `__test__.py` file
doesn't exist, the generated `py_test` rule is likely invalid as-is due
to the lack of `main`; this can be fixed by mapping to some other
`py_test` implementation, and I believe the new behavior makes more
sense than pointing `main` to a non-existing file.

It may be useful to review per-commit (all tests pass on each commit):
- First commit reproduces the issue;
- Second commit fixes the issue and the corresponding tests;
- Third commit adds additional test cases.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b7ab25..af4c108 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,9 @@
 * (whl_library): Fix the experimental_target_platforms overriding for platform
   specific wheels when the wheels are for any python interpreter version. Fixes
   [#1810](https://github.com/bazelbuild/rules_python/issues/1810).
+* (gazelle) In `project` or `package` generation modes, do not generate `py_test`
+  rules when there are no test files and do not set `main = "__test__.py"` when
+  that file doesn't exist.
 
 ### Added
 
diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go
index 400c25e..673076d 100644
--- a/gazelle/python/generate.go
+++ b/gazelle/python/generate.go
@@ -393,20 +393,22 @@
 			// the file exists on disk.
 			pyTestFilenames.Add(pyTestEntrypointFilename)
 		}
-		pyTestTargetName := cfg.RenderTestName(packageName)
-		pyTestTarget := newPyTestTargetBuilder(pyTestFilenames, pyTestTargetName)
+		if (hasPyTestEntryPointTarget || !pyTestFilenames.Empty()) {
+			pyTestTargetName := cfg.RenderTestName(packageName)
+			pyTestTarget := newPyTestTargetBuilder(pyTestFilenames, pyTestTargetName)
 
-		if hasPyTestEntryPointTarget {
-			entrypointTarget := fmt.Sprintf(":%s", pyTestEntrypointTargetname)
-			main := fmt.Sprintf(":%s", pyTestEntrypointFilename)
-			pyTestTarget.
-				addSrc(entrypointTarget).
-				addResolvedDependency(entrypointTarget).
-				setMain(main)
-		} else {
-			pyTestTarget.setMain(pyTestEntrypointFilename)
+			if hasPyTestEntryPointTarget {
+				entrypointTarget := fmt.Sprintf(":%s", pyTestEntrypointTargetname)
+				main := fmt.Sprintf(":%s", pyTestEntrypointFilename)
+				pyTestTarget.
+					addSrc(entrypointTarget).
+					addResolvedDependency(entrypointTarget).
+					setMain(main)
+			} else if hasPyTestEntryPointFile {
+				pyTestTarget.setMain(pyTestEntrypointFilename)
+			}
+			pyTestTargets = append(pyTestTargets, pyTestTarget)
 		}
-		pyTestTargets = append(pyTestTargets, pyTestTarget)
 	} else {
 		// Create one py_test target per file
 		pyTestFilenames.Each(func(index int, testFile interface{}) {
diff --git a/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out
index 3a33111..af01460 100644
--- a/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out
+++ b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out
@@ -25,5 +25,4 @@
         "bar/bar_test.py",
         "foo/bar/bar_test.py",
     ],
-    main = "__test__.py",
 )
diff --git a/gazelle/python/testdata/project_generation_mode/BUILD.in b/gazelle/python/testdata/project_generation_mode/BUILD.in
new file mode 100644
index 0000000..130a625
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
diff --git a/gazelle/python/testdata/project_generation_mode/BUILD.out b/gazelle/python/testdata/project_generation_mode/BUILD.out
new file mode 100644
index 0000000..1f30b6d
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/BUILD.out
@@ -0,0 +1,14 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
+
+py_library(
+    name = "project_generation_mode",
+    srcs = [
+        "__init__.py",
+        "bar/bar.py",
+        "foo/foo.py",
+    ],
+    visibility = ["//:__subpackages__"],
+)
diff --git a/gazelle/python/testdata/project_generation_mode/README.md b/gazelle/python/testdata/project_generation_mode/README.md
new file mode 100644
index 0000000..6d8f138
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/README.md
@@ -0,0 +1,3 @@
+# Project generation mode
+
+Simple example using `gazelle:python_generation_mode project` in a project with no tests.
diff --git a/gazelle/python/testdata/project_generation_mode/WORKSPACE b/gazelle/python/testdata/project_generation_mode/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/project_generation_mode/__init__.py b/gazelle/python/testdata/project_generation_mode/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/__init__.py
diff --git a/gazelle/python/testdata/project_generation_mode/bar/bar.py b/gazelle/python/testdata/project_generation_mode/bar/bar.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/bar/bar.py
diff --git a/gazelle/python/testdata/project_generation_mode/foo/foo.py b/gazelle/python/testdata/project_generation_mode/foo/foo.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/foo/foo.py
diff --git a/gazelle/python/testdata/project_generation_mode/test.yaml b/gazelle/python/testdata/project_generation_mode/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/test.yaml
@@ -0,0 +1,15 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+---
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.in b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.in
new file mode 100644
index 0000000..130a625
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.out b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.out
new file mode 100644
index 0000000..05cf353
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.out
@@ -0,0 +1,19 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
+
+py_library(
+    name = "project_generation_mode_with_test_entrypoint",
+    srcs = ["__init__.py"],
+    visibility = ["//:__subpackages__"],
+)
+
+py_test(
+    name = "project_generation_mode_with_test_entrypoint_test",
+    srcs = [
+        "__test__.py",
+        "foo/foo_test.py",
+    ],
+    main = "__test__.py",
+)
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/README.md b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/README.md
new file mode 100644
index 0000000..8db5728
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/README.md
@@ -0,0 +1,3 @@
+# Project generation mode with test entrypoint
+
+Example using `gazelle:python_generation_mode project` in a project with tests that use an explicit `__test__.py` entrypoint.
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/WORKSPACE b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__init__.py b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__init__.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__test__.py b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__test__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__test__.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/foo/foo_test.py b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/foo/foo_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/foo/foo_test.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/test.yaml b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/test.yaml
@@ -0,0 +1,15 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+---
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.in b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.in
new file mode 100644
index 0000000..130a625
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.out b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.out
new file mode 100644
index 0000000..8756978
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.out
@@ -0,0 +1,15 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
+
+py_library(
+    name = "project_generation_mode_with_tests",
+    srcs = ["__init__.py"],
+    visibility = ["//:__subpackages__"],
+)
+
+py_test(
+    name = "project_generation_mode_with_tests_test",
+    srcs = ["foo/foo_test.py"],
+)
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/README.md b/gazelle/python/testdata/project_generation_mode_with_tests/README.md
new file mode 100644
index 0000000..4a5f012
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/README.md
@@ -0,0 +1,7 @@
+# Project generation mode with tests
+
+Example using `gazelle:python_generation_mode project` in a project with tests, but no `__test__.py` entrypoint.
+
+Note that, in this mode, the `py_test` rule will have no `main` set, which will fail to run with the standard
+`py_test` rule. However, this can be used in conjunction with `gazelle:map_kind` to use some other implementation
+of `py_test` that is able to handle this sitation (such as `rules_python_pytest`).
\ No newline at end of file
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/WORKSPACE b/gazelle/python/testdata/project_generation_mode_with_tests/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/__init__.py b/gazelle/python/testdata/project_generation_mode_with_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/__init__.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/foo/foo_test.py b/gazelle/python/testdata/project_generation_mode_with_tests/foo/foo_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/foo/foo_test.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/test.yaml b/gazelle/python/testdata/project_generation_mode_with_tests/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/test.yaml
@@ -0,0 +1,15 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+---