Need help writing a test

Hello,

I’m trying to write a test for fcitx and I can manage to get the exact python code pass in the interactive console. However, the same test fails in the vm. VM launches and does most of the things but the very crucial thing fails. This is my first test and I suspect with a bit of handholding, I’ll save a lot of time.

Would someone be able to help me, maybe a sync chat on IRC or discord?

Here is the code-

code
import ./make-test-python.nix ( 
	{
		pkgs, ...
	}: 
		let 
			fcitx_config_contents  = builtins.readFile "/code/zx/workspace/git/nixpkgs/nixos/tests/fcitx_config"  ;
			fcitx_profile_contents = builtins.readFile "/code/zx/workspace/git/nixpkgs/nixos/tests/fcitx_profile" ;

			fcitx_profile_file = pkgs.writeTextFile { 
				name = "fcitx_profile";
				text = fcitx_profile_contents;
			};
			
			fcitx_config_file = pkgs.writeTextFile { 
				name = "fcitx_config";
				text = fcitx_config_contents;
			};
		in

		rec {
				skipLint = true;
				name = "fcitx";
				machine = 
				{ 
					pkgs, 
					... 
				}: 
					{
						virtualisation.memorySize = 1024;

						imports = [
							./common/user-account.nix
						];

						environment.systemPackages = [
							# related idio-syncracies of xfece
							pkgs.alacritty 
							pkgs.qt5.qttools
						];


						services.xserver = 
						{
							enable = true;

							displayManager = {
								lightdm.enable = true;
								autoLogin = {
									enable = true;
									user = "alice";
								};
							};

							desktopManager.xfce.enable = true;
						};
							
						i18n = {
							inputMethod = {
								enabled = "fcitx";
								fcitx.engines = [
									pkgs.fcitx-engines.m17n
									pkgs.fcitx-engines.table-extra
								];
							};
						};
					}
				;

				testScript =
					let 
						user     = "alice"; #machine.config.users.users.alice { inherit pkgs; } ;
						userHome = "/home/alice";
						xauth    = "/home/alice/.Xauthority";
						fcitx_confdir  = "${userHome}/.fctix/config/";
					in
''
machine.copy_from_host( "${fcitx_profile_file}", "/home/alice/.config/fcitx/profile" )
machine.copy_from_host( "${fcitx_config_file}" , "/home/alice/.config/fcitx/config"  )
print( "1" )

start_all()


machine.wait_for_file( "${xauth}" )
machine.succeed("xauth merge ${xauth}" )

machine.sleep( 20 )

machine.succeed( "su - alice -c 'alacritty&'" )
machine.sleep( 2 )
print( "2" )

machine.send_chars( 'echo ' )
machine.sleep( 1 )
machine.send_key( 'ctrl-alt-shift-u' )
machine.sleep( 1 )
machine.send_chars( 'smil' )
machine.sleep( 1 )
machine.send_key( 'tab' )
machine.sleep( 1 )
machine.send_key( '\n' )
machine.sleep( 1 )
machine.send_key( 'ctrl-spc' )
machine.sleep( 1 )
machine.send_chars( 'a2' )
machine.sleep( 1 )
machine.send_key( 'alt-shift' )
machine.sleep( 1 )
machine.send_chars( 'ka ' )
machine.sleep( 1 )
machine.send_key( 'alt-shift' )
machine.sleep( 1 )
machine.send_key( 'ctrl-spc' )
machine.sleep( 1 )
machine.send_chars( ' > k\n' )
machine.sleep( 1 )
print( "3" )
file_content=machine.succeed( 'cat /home/alice/k' )
print( machine.succeed( 'head /home/alice/.config/fcitx/profile' ) )
print( machine.succeed( 'head /home/alice/.config/fcitx/config' ) )
print( "4" )
print( file_content )
print( "5" )
assert(  file_content== '☺一下क\n' )
''
		;
	}
)

It basically does the following:

  1. Copy config files from hardcoded locations to the vm. The config files are my own and this might sound imperfect but I see this as the quickest way to get to the main test, since otherwise I’ll have to script steps that ultimately get to this config. PS: No way to copy a file directly without first recreating in the store?

  2. start the machine. (Which is redundant since copying starts the machine). I need to copy before starting otherwise I’ll have to restart the fcitx process.

  3. Open alacritty (because it has no menubar and no conflicts with shortuct keys)

  4. Send some keystrokes, invoke fcitx, etc, and get result.

I’m not looking for expertise with fcitx, only someone with some familiarity with the testing framework to maybe act as another pair of eyes.

Thank you.

EDIT: I know that /home/alice etc will not be hardcoded, I did so for easy copying into python REPL.

Also, the file copy process succeeds, I can verify the output of head commands at the end of the python script.

Bump, I’d appreciate an offer of help here …

  1. Instead of
pkgs.writeTextFile { 
	name = "foo";
	text = builtins.readFile "/path/to/foo";
}

you should be able to do just "${/path/to/foo}" and it will be copied to store automatically. But I am not sure why copying directly does not work. Any errors? Do you see the file created in the VM if you try?

But I would just try to create the files declaratively in the configuration, for example using systemd.tmpfiles.rules.


Could you be more specific about what fails?

I succeed in launching alacritty. When I send the keystroke to invoke fcitx, seemingly nothing happens because my keystrokes return as is. When I say a2, I expect to see 一下 but I see a2. Lines in my code above are not numbered but quick glance might show you a2. What I do there after opening alacritty is sending the following keystrokes (with a few changes for brevity:

echo <FCITX INVOKE SHORTCUT>a2<FCITX_INPUT_METHOD_CHANGE_SHORTCUT>ka > `

In python than I verify the contents of this file.

However, content comes out to “a2ka”.

I don’t mind spending my own time for things which seem to have a logical path to solution (for example finding which key exactly sends ctrl+space, debugging through nix code, finding that it sends qemu monitor commands and then finding monitor key sequences). However, my intuition with using puppeteer et al in the past is that there are corners that soembody can quickly guide you through.

Yeah, you are right:

It just runs sendkey monitor command, which seems to be documented here:

https://en.wikibooks.org/wiki/QEMU/Monitor#sendkey_keys

So your code looks fine as far as I can tell.


Did you mean that when you manually paste the machine. commands into the test driver REPL, the input method works correctly?

yeah, it works perfectly in interactive console. The fcitx input box (presumably) opens because I am able to verify (in that last assert) that characters are indeed hanji and Devanagari.

Hi @jtojnar

I’ve managed to deterministically make the test pass.

However, to prepare if for commit, I followed your advice here-

When I do so with a absolute path:

            machine.copy_from_host( "/code/zx/workspace/git/nixpkgs/nixos/tests/fcitx_profile", "/home/alice/.config/fcitx/profile" )

None of the other invocations of copy_from_host in the repo copy a non-store file from host.

  File "/nix/store/bdfalnsmc6f6i8rm97j0rfk1wwgl6501-nixos-test-driver/bin/.nixos-test-driver-wrapped", line 897, in run_tests
    exec(tests, globals())
  File "<string>", line 1, in <module>
  File "<string>", line 2, in <module>
  File "/nix/store/bdfalnsmc6f6i8rm97j0rfk1wwgl6501-nixos-test-driver/bin/.nixos-test-driver-wrapped", line 607, in copy_from_host
    shutil.copy(host_src, host_intermediate)
  File "/nix/store/wbilyi119q8my0kz2pl4chgs35xhlnhm-python3-3.8.5/lib/python3.8/shutil.py", line 415, in copy
    copyfile(src, dst, follow_symlinks=follow_symlinks)
  File "/nix/store/wbilyi119q8my0kz2pl4chgs35xhlnhm-python3-3.8.5/lib/python3.8/shutil.py", line 261, in copyfile
    with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
FileNotFoundError: [Errno 2] No such file or directory: '/code/zx/workspace/git/nixpkgs/nixos/tests/fcitx_profile'
cleaning up
killing machine (pid 6)

I get a similar error when file is addressed relative to the git repo

FileNotFoundError: [Errno 2] No such file or directory: 'nixos/tests/fcitx_profile'

And again, same if I address it relative to the test file.

None of the other invocations of copy_from_host copy from a non-store path. I will thus leave this as is (after replacing the absolute path with relative path)

      fcitx_config_contents  = builtins.readFile ./fcitx_config  ;
      fcitx_profile_contents = builtins.readFile ./fcitx_profile ;

https://github.com/NixOS/nixpkgs/pull/96371