FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

test(cli): cover app id derivation from project names by Adebowale-Morakinyo · Pull Request #79 · pythonnative/pythonnative · GitHub

test(cli): cover app id derivation from project names - #79

Merged
owenthcarey merged 1 commit into
pythonnative:mainfrom
Adebowale-Morakinyo:test/cli-app-id-derivation
Sep 8, 2026
Merged

owenthcarey merged 1 commit into
pythonnative:mainfrom
Adebowale-Morakinyo:test/cli-app-id-derivation

Conversation

Copy link
Copy Markdown
Contributor

What

A Google-style docstring on _app_id_from_name and direct unit tests. No behavior change — the
function body's AST is identical to main.

Closes #62.

The reachability split, which corrects the issue

_NAME_RE (^[a-z][a-z0-9_-]*$) guarantees a leading letter, so slug is never empty and
slug[0].isalpha() is always true for a typed name. The app-prefix branch is unreachable
from pn init <name>.

The issue names 3d_viewer → com.example.app3d_viewer as the headline untested case, but
pn init 3d_viewer exits 1 on the name guard and never calls this function. A test written that
way would pass for the wrong reason. Same for the empty-string and uppercase cases.

So the tests are split into typed-reachable and derived-only groups, each asserting its own
premise against _NAME_RE so the split can't silently rot, and the prefix branch is covered end
to end through the no-name path from a 3d_viewer/ directory. Hyphen removal is the only
substitution a typed name can trigger, since - is the one _NAME_RE character outside
[a-z0-9_].

The suggested invariant can't fail, and I kept it anyway

^com\.example\.[a-z][a-z0-9_]*$ holds for every possible input — fuzzed over 2,173,777 during
orientation (exhaustive ASCII to length 3, every Unicode character whose .lower() changes
length, 60k random control characters and non-ASCII), and structurally guaranteed since the
substitution leaves only [a-z0-9_] and the prefix supplies a leading letter.

It earns its place for a narrower reason, recorded in a comment: it catches a change that admits
a character outside the segment grammar — under a keep-hyphens mutation it kills 4 nodes, since
com.example.my-app doesn't match — but not a changed prefix. The exact-value cases carry the
real weight.

What the mutation checks establish

Before this PR, the two behaviors the issue exists to cover were pinned by nothing:

Mutation Before After
keep hyphens [^a-z0-9_-] 0 9
prefix "x" not "app" 0 6
strip underscores [^a-z0-9] 4 8
drop .lower() 1 3

Every existing fixture used my_app, widgets, or MyProject — no hyphens, nothing digit- or
underscore-leading. What the six downstream assertions pinned was underscore preservation and
lowercasing, incidentally, and nothing the issue was about.

Two claims in the docstring took correcting during review

Recording them because they're the kind of thing that survives into documentation otherwise.

Hyphens and the platform grammars. I first wrote that neither a Java package segment nor an
iOS bundle identifier admits a hyphen. The second half is wrong here: config.py:444 calls
_validate_app_id(self.ios.bundle_id, allow_hyphen=True), and :613 normalizes - to _ before
matching, so an explicit [ios].bundle_id may contain one. The real constraint is stronger and
simpler — _app_id_from_name fills app.id, which is the default for both the Android
application id and the iOS bundle id, so it has to satisfy the stricter of the two grammars.

Non-injectivity. I first wrote that mapping - to _ would be "valid and injective." It
wouldn't. _NAME_RE permits both characters, so my-app would collide with my_app instead of
with myapp. Across every typed-legal name up to length 4, both schemes lose the same 423 names
to collision — deletion produces 129 multi-source ids, the mapping produces 309. Same information
loss, redistributed. Injectivity would need an escaping scheme, not a substitution.

A test now pins my-app and my_app as distinct, so the alternative can't be introduced silently
and the comment can't go stale.

Flagged, not fixed

pn init class exits 0 and writes id = "com.example.class", which _validate_app_id then
rejects — all 53 entries in config._JAVA_KEYWORDS pass _NAME_RE. The scaffold is dead on
arrival. Opened as #78; out of scope here since this issue forbids behavior changes and the fix
is a product decision. The docstring records that the result satisfies _APP_ID_SEGMENT without
being guaranteed to pass _validate_app_id.

Testing

32 new test nodes, 111 in tests/test_cli.py. ./scripts/check.sh passes.

Behavior identity was verified two ways: the function body's AST compared against main with the
docstring stripped, and 10,011 fuzz inputs run through both versions with outputs compared. Every
parameterized expected value was computed independently from main's source rather than from the
implementation under test.

_app_id_from_name is private, so mkdocstrings' filters: ["!^_"] excludes it — the docstring
doesn't render anywhere and mkdocs build --strict is unaffected.

_app_id_from_name had no direct tests and no docstring, and the two
behaviors that matter were pinned by nothing: mutating it to keep
hyphens or to use a different prefix broke zero tests, because every
existing fixture used my_app, widgets, or MyProject. What the six
downstream assertions pinned was underscore preservation and
lowercasing, incidentally.

The tests are split by reachability. _NAME_RE guarantees a leading
letter, so the "app" prefix branch is unreachable from pn init <name>;
it is live only via the derived directory name when pn init is given
none. Each group asserts its own premise against _NAME_RE so the split
can't silently rot, and one end-to-end test drives the prefix branch
through the no-name path.

The docstring records why hyphens are deleted rather than mapped, that
the derivation is therefore non-injective, and that the result satisfies
_APP_ID_SEGMENT without being guaranteed to pass _validate_app_id --
reserved words like "class" pass through here.

No behavior change; the function body's AST is identical to main.

Closes pythonnative#62
owenthcarey merged commit 5b1d965 into pythonnative:main Sep 8, 2026
18 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add unit tests and a docstring for _app_id_from_name in the pn CLI

2 participants


Back | FazBrowse Home | New Git URL