Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test start #2718

Closed
wants to merge 3 commits into from
Closed

Test start #2718

wants to merge 3 commits into from

Conversation

aadcg
Copy link
Member

@aadcg aadcg commented Dec 22, 2022

Fixes #2710

Discussion

Work in progress. I'd like you to take a look at the strategy I'm taking. @Ambrevar @aartaka @jmercouris

Checklist:

Everything in this checklist is required for each PR. Please do not approve a PR that does not have all of these items.

  • I have pulled from master before submitting this PR
  • There are no merge conflicts.
  • I've added the new dependencies as:
    • ASDF dependencies,
    • Git submodules,
      cd /path/to/nyxt/checkout
      git submodule add https://gitlab.common-lisp.net/nyxt/py-configparser _build/py-configparser
    • and Guix dependencies.
  • My code follows the style guidelines for Common Lisp code. See:
  • I have performed a self-review of my own code.
  • My code has been reviewed by at least one peer. (The peer review to approve a PR counts. The reviewer must download and test the code.)
  • Documentation:
    • All my code has docstrings and :documentations written in the aforementioned style. (It's OK to skip the docstring for really trivial parts.)
    • I have updated the existing documentation to match my changes.
    • I have commented my code in hard-to-understand areas.
    • I have updated the changelog.lisp with my changes if it's anything user-facing (new features, important bug fix, compatibility breakage).
    • I have added a migration.lisp entry for all compatibility-breaking changes.
    • (If this changes something about the features showcased on Nyxt website) I have these changes described in the new/existing article at Nyxt website or will notify one of maintainters to do so.
  • Compilation and tests:
    • My changes generate no new warnings.
    • I have added tests that prove my fix is effective or that my feature works. (If possible.)
    • New and existing unit tests pass locally with my changes.

Copy link
Member

@Ambrevar Ambrevar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far!

source/start.lisp Outdated Show resolved Hide resolved
tests/offline/start.lisp Outdated Show resolved Hide resolved
tests/offline/start.lisp Outdated Show resolved Hide resolved
(define-test stateless-headless-argument ()
(nyxt:start :headless t :failsafe t)
;; TODO find a way to avoid these calls to sleep.
(sleep 4)
Copy link
Member

@Ambrevar Ambrevar Dec 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the only proper way is via the after-startup-hook.

But clearly it's not convenient for testing. A few options:

  • Add a callback option to nyxt:start which is executed when after-startup-hook is run. This callback could either run the test code or fulfill a promise which the test code is waiting on.
  • Add an (unexported?) promise slot to browser, and have it fulfilled when after-startup-hook is run.
    This way the caller can force the promise (which means "wait for the browser to be started").
  • Make nyxt:start synchronous, which means we would put all the promise/force logic inside nyxt:start itself and only return from the function once the renderer is started.
    The drawback is that nyxt:start would now be blocking. But... considering there is nothing to be done in the meantime, is this really a drawback?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Finally move wait-on(-handler) to nhooks? not exactly a solution, but makes tests much prettier.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not fully satisfied with the wait-on approach: it's still a cumbersome approach that forces nesting over nesting...

The CPS approach is more interesting in this regard: #2054 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or promises/futures and lparallel:plet?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best and cleaner solution would be to make nyxt:start synchronous.

The drawback is that nyxt:start would now be blocking. But... considering there is nothing to be done in the meantime, is this really a drawback?

No. That's why I think this is the best solution.

This change would also gracefully handle (progn (nyxt:start) (nyxt:quit)).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ambrevar are you interested in working on this or should I do it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think you have a clear idea of how to implement it, please go ahead :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best and cleaner solution would be to make nyxt:start synchronous.

The drawback is that nyxt:start would now be blocking. But... considering there is nothing to be done in the meantime, is this really a drawback?

No. That's why I think this is the best solution.

This change would also gracefully handle (progn (nyxt:start) (nyxt:quit)).

Okay, this one feels quite contradictory.

  • I understood that you want to make nyxt:start to happen synchronously. The only clear option I see for that is bt:join-thread on the thread that nyxt:start spawns.
    • An alternative solution would be waiting on some after-startup-hook (or some alternative) in nyxt:start, but that is architecturally impossible, because *browser* is not yet initialized when we run nyxt:start.
  • Given that we join threads, one cannot simply do (progn (nyxt:start) (nyxt:quit)), because nyxt:start does not terminate until nyxt:quit is called. Which is a vicious circle.
    • If we wait on some event (which is quite bad for an startup routine), then we'd be basically baking the waiting logic from the tests into the nyxt:start. And it does not belong there.
  • All in all, I don't see much use in making nyxt:start synchronous, because it's either
    • Creating inconveniences for the otherwise trivial use-case (bt:join-thread I can do myslef), or
    • Introducing backwards references to the extremely fragile startup sequence.

I must be missing something here. I mean, I'd like to make our startup more reproducible, but not in those two ways I'm imagining, and not necessarily by making nyxt:start synchronous/blocking, so there should be a better way you're conjuring, @aadcg.

Copy link
Member Author

@aadcg aadcg Jan 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I don't want to work on the architecture of start.lisp. There is too much historical background that I lack, while @aartaka and @Ambrevar know it quite well.

@Ambrevar suggestion on making nyxt:start synchronous seems sane to me, because I don't see the point in having the ability to do something while the renderer is loading (and @Ambrevar seems to agree).

With regards to the semantics of (progn (nyxt:start) (nyxt:quit) I think it should mean: "Start Nyxt, block it while the renderer loads and then, when nyxt:start exists, proceed and run nyxt:quit". I'm sure this is possible to do. But I understand that it would require re-thinking a lot of the architecture.

I can help with testing and making sense of the start process of course.

To answer @Ambrevar's comment:

If you think you have a clear idea of how to implement it, please go ahead :)

No, I have no clear idea. I just have a clear idea of how things ought to be :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now solved.

@aadcg aadcg self-assigned this Jan 18, 2023
@aartaka
Copy link
Contributor

aartaka commented Jan 24, 2023

@Ambrevar, wait, should this be closed? @aadcg, there was something in addition to #2740 in this PR, right?

@aadcg aadcg reopened this Jan 24, 2023
@Ambrevar
Copy link
Member

GitHub did this for me, so yes, this should remain open.

@aadcg
Copy link
Member Author

aadcg commented Jan 24, 2023

Yes, there is lots of work to be done on this front. Not on testing itself, but on making a proper start implementation. And the implementation specification is dictated by the test I write tests (that ought to pass).

@aadcg aadcg mentioned this pull request Jan 24, 2023
@aadcg
Copy link
Member Author

aadcg commented Feb 13, 2023

(nyxt:start) returns NIL and so does (nyxt:quit) after Nyxt started.

What should be return value from running (nyxt:quit) when there's no browser instance? NIL?

CC @Ambrevar @aartaka

@Ambrevar
Copy link
Member

Makes sense if nyxt:start returns *browser*.
(nyxt:quit) should probably return nothing / NIL.

@aadcg
Copy link
Member Author

aadcg commented Feb 13, 2023

Makes sense if nyxt:start returns browser.

Currently it returns NIL, but I agree that it should return *browser*.

;; (define-test start-quit-cycle ()
;; (assert-false (progn (nyxt:start :failsafe t) (nyxt:quit))))

;; Fails.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ambrevar @aartaka @jmercouris

How critical do you think that this is?

For the user that starts Nyxt via the OS shell, it doesn't matter much.

For the developer that starts Nyxt from the REPL it's critical.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, fell under my radar.

It's not critical production-wise, as you pointed out.
Still, this highlights that our startup sequence is not as functional as we would hope, which indicates we wrote poor code :(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

I'll soon come back to this PR by adding all tests that would capture the invariants of a good startup sequence. If some are not met, then I'll leave them commented out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pierre covered it!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean @jmercouris?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean I agree with Pierre's conclusions

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it!

@aadcg aadcg mentioned this pull request May 28, 2023
@jmercouris
Copy link
Member

Please reopen when ready.

@jmercouris jmercouris closed this Oct 28, 2023
@aadcg aadcg deleted the test-start branch October 30, 2023 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Test start.lisp
4 participants