Can I run multiple integration tests with one single config file in Flutter? - flutter

I am trying to write Flutter integration tests and to run them all with one config file instead of making config file for every single test. Is there any way to do that?
For now I have login.dart and login_test.dart and so on, for every single test. I know its convention that every config and test file must have the same name, but that's not what I need, more configurable things are welcomed. Thanks in advance.
This is my config file (login.dart)
import 'package:flutter_driver/driver_extension.dart';
import 'package:seve/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
And test (login_test.dart) looks something like this
import ...
FlutterDriver driver;
void main() {
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
test('T001loginAsDriverAndVerifyThatDriverIsLogedInTest', () async {
some_code...
});
});
Now I want to make new test file (e.g login_warning.dart) and be able to start both tests by calling single config file (login.dart). Is that even possible?

Yes, running multiple "test" files with the same "config" is possible.
In the flutter jargon, your config file is your target and your test file is your driver. Your target is always login.dart but you have the two drivers login_test.dart and login_warning.dart.
With the flutter drive command, you can specify the target as well as the driver.
So in order to run both drivers, simply execute the following commands
flutter drive --target=test_driver/login.dart --driver=test_driver/login_test.dart
flutter drive --target=test_driver/login.dart --driver=test_driver/login_warning.dart
This executes first the login_test.dart and then the login_warning.dart driver.

You can always have one main test file that you initiate, like say
flutter drive --target=test_driver/app_test.dart
Then in that call your test groups as functions, like so -
void main() {
test1();
}
void test1() {
group('test 1', () {});}
So with one command you get to execute all the cases mentioned in the main()

Like vzurd's answer my favourit and cleanest is to create a single test file and call all main methods from within:
import './first_test.dart' as first;
import './second_test.dart' as second;
void main() {
first.main();
second.main();
}
Then just run driver on the single test file:
flutter drive --driver=test/integration/integration_test_driver.dart --target=test/integration/run_all_test.dart

to expand on to #sceee 's answer:
you can put the multiple commands into a shell script named integration_tests.sh for example and run them with a single command that way.
#!/bin/sh
flutter drive --target=test_driver/app.dart --driver=test_driver/app_test.dart
flutter drive --target=test_driver/app.dart --driver=test_driver/start_screen_test.dar
make executable:
$chmod a+rx integration_tests.sh
run it:
$./integration_tests.sh

We can use shell command to automate this process.
The following solution will work even with any new test files without manually adding its name to any of the files.
Create a shell script with name integrationTestRunner.sh inside the root directory. You can use the command
touch integrationTestRunner.sh
Inside integrationTestRunner.sh file, paste the following code.
#!/bin/bash
# Declare an array to store the file names and paths
declare -a targets
# Find all .dart files in the current directory and subdirectories
while IFS= read -r -d $'\0' file; do
targets+=("$file")
done < <(find integration_test -name "*.dart" -type f -print0)
# Loop through the array and run the flutter drive command for each target
for target in "${targets[#]}"
do
flutter drive \
--driver=test_driver/integation_test_driver.dart \
--target=$target
done
Run the integrationTestRunner.sh file with any methods:
Pressing the ▶️ button in that file (if you are in VS Code)
Running the script from command line ./integrationTestRunner.sh

Related

Add bash script as an entrypoint to Python package with Poetry

Is it possible to add bash script as an entrypoint (console script) to Python package via poetry? It looks like it only accepts python files (see code here).
I want entry.sh to be an entry script
#!/usr/bin/env bash
set -e
echo "Running entrypoint"
via setup.py
entry_points={
"console_scripts": [
"entry=entry.sh",
],
},
On the other hand setuptools seems to be supporting shell scripts (see code here).
Is it possible to include shell script into a package and add it to the entrypoints after installing when working with Poetry?
UPD. setuptools does not support that as well (it generates code below)
def importlib_load_entry_point(spec, group, name):
dist_name, _, _ = spec.partition('==')
matches = (
entry_point
for entry_point in distribution(dist_name).entry_points
if entry_point.group == group and entry_point.name == name
)
return next(matches).load()
globals().setdefault('load_entry_point', importlib_load_entry_point)
Is it design decision? It looks to me that packaging should provide such a feature to deliver complex applications as a single bundle.
So I ended up using this workaround: have my script in place and add it to the bundle via package_data and call it from within Python code which I made as an entrypoint.
import subprocess
def _run(bash_script):
return subprocess.call(bash_script, shell=True)
def entrypoint():
return _run("./scripts/my_entrypoint.sh")
def another_entrypoint_if_needed():
return _run("./scripts/some_other_script.sh")
and pyproject.toml
[tool.poetry.scripts]
entrypoint = 'bash_runner:entrypoint'
another = 'bash_runner:another_entrypoint_if_needed'
Same works for console_scripts in setup.py file.

Get the current project directory from flutter integration test

Is there a way to get the current project directory in flutter integration test? This is a basic test setup as an example.
import 'dart:io';
import 'package:flutter_driver/driver_extension.dart';
import 'package:ivori/main.dart' as app;
import 'package:glob/glob.dart';
void main() {
// This line enables the extension.
enableFlutterDriverExtension();
print("Where the working directory? ${Directory.current}");
print("What's under the current directory? ${Glob("*").listSync()}");
// Call the `main()` function of the app, or call `runApp` with
// any widget you are interested in testing.
app.main();
}
Both of the middle two added lines Directory.current and Glob("*").listSync() are attempts to check/show the current working directory. The output from the above is:
$ flutter drive --target=test_driver/app.dart
Changing current working directory to: /Users/yuchen/Documents/MyDemoApp
Using device iPhone SE (2nd generation).
Starting application: test_driver/app.dart
...
...
...
flutter: Where the working directory? Directory: '/'
flutter: What's under the current directory? [Directory: './home', Directory: './usr', File: './.DS_Store', Directory: './bin', Directory: './sbin', File: './.file',
Directory: './etc', Directory: './var', Directory: './Library', Directory: './System', Link: './.VolumeIcon.icns', Directory: './.fseventsd', Directory: './private',
Directory: './.vol', Directory: './Users', Directory: './Applications', Directory: './opt', Directory: './dev', Directory: './Volumes', Directory: './tmp', Directory:
'./cores']
Not hard to see, the working directory is actually set to the root folder of the computer. Is there a way to get to the current project directory or src directory somehow?
The motivation behind this is to have some test data that will be used during the test but not in the app bundle. There are some discussions (fixes and work rounds) about unit tests in this long thread https://github.com/flutter/flutter/issues/12999. However, it doesn't seem to have a solution for integration tests.
In my case, I used ext_storage to access the device's storage. The path_provider plugin seems to only able to access the app's directory.
Here's a sample of fetching the storage path for Downloads.
/// Get storage path for Downloads
/// https://pub.dev/documentation/ext_storage/latest/
Future<String> _getPath() {
return ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
}
Flutter test command accepts a parameter --dart-define which allows you to set variables
Running your test like this:
flutter test integration_test/test.dart --dart-define=projectRoot=$(pwd)
from the root of your project will allow your test code to access the projectRoot variable and get your files
const projectRoot = String.fromEnvironment('projectRoot');
// now you can access your files like you would expect
final file = File('$projectRoot/test_resources/test.json');
Note: Flutter sets the execution directory to the root of the file system only for integration tests, you don't need this workaround for regular unit (widget) tests

How to import other source code files in dm script

Is there a way to use multiple code files in dm-script to structure the code? Something like:
import "path/to/utility_functions.s";
utility_functions.do_something_general();
Note that I do not want to have the code as a menu item if possible. The code contains only functions that I use in the main script.
I tried the following:
File 1: test.s
void test(){
result("test\n");
}
File 2: require-test.s
AddScriptFileToPackage("path/to/test.s", "test", 3, "test-function", "", "", 1);
ExecuteScriptString("test()"); // works immediately but feels wrong
test(); // works after restart
Now I have the following problems:
I have to restart DigitalMicrograph after executing this script, otherwise test() does not work (ExecuteScriptString("test()"); works but it feels wrong to use strings for invoking code, if possible I'd like to avoid that)
When I restart DigitalMicrograph another time AddScriptFileToPackage() sais 'The script cannot be added because the package exists and is read-only. [...]'. Is there a way around it or do I have to use try blocks?
I feel like I am not doing this wrong at some place.
DM script does not support on-demand-loading of packages, but there are two different ways to achieve what you want:
Using library packages
When you "install" a script, you can choose to either install it as menu-command or as a library. This is true for both installing scripts via the menu command (which get stored in the global preferences file) or via the scripting-command (which can be used to
create .gtk files which one can then add/remove from the plugins
folder as needed).
The "menu" option will install a script such that it is invoked once via the menu-item but does not stay in memory otherwise.
The "library" option will execute a script once on startup and keep the script itself in scope. So you can define methods (or classes) in a library file and have it generally available. And you can put some executable code in a library if you need some startup-action.
Using script libraries as .gtk plugins is possibly the recommended way to achieve what you want. They are, however, always loaded.
Piece of advise: If you make libraries ensure you use very unique class and method names to avoid any conflict. I would recommend pre-fixing all class/method names with some library-name, i.e. use MyLib_MyClass instead of MyClass and the like.
Clarification: Scripts added as library packages are permanently added to the software, i.e. those packages get created once and are then placed in the plugins-folder. They will always load on startup of DM and be available. The Library package method is not suitable for temporarily 'loading' external scripts. Such 'on demand import' is not supported by DM-scripting.
The script commands to create packages are utility commands to help one create packages in an easy and manageable way. Typically, one would create a "Create package XY" script with several such commands adding all scripts from a location into a package. The script would be called once to create the package-file (It is already in the plugins folder afterwards.)
Only when the included scripts change and the package therefore requires to be updated, is the create-package script called again. Note, that in this case it is first required to remove the package-file from the plugins folder and start DigitalMicrograph without loading it, so that a new package is created. Otherwise the script would append to the package, which would not be possible if methods of the same name already exist in the package.
The F1 help documentation has an example script:
A typical examples, using GMS 3.4.0:
Script stored at: C:\Tmp\testLib.s
void TestCall()
{
Result("\nTest")
}
Script stored at: C:\Tmp\menuAction.s
Result("\nPerforming an action here.")
One-time run script to install a package:
// General package parameters
// *********************************************
string pkNa = "myPkg" // Filename of plugin
number pkLe = 3 // level 3 (.gtk) only needed for load order
string pkLo = "user_plugin" // plugin location
string scriptRoot = "C:\\Temp\\"
// List of Scripts to be installed as menu items
// *********************************************
// Each entry needs a (unique) command-name, a menu-name and an optional sub-menu name.
// The "isLibary" flag is set to 0
// It is possible to add the same script multiple times. The script will be executed when the menu item
// is chosen. Methods and Classes of the script are not available otherwise
// A separator can be added by installing and empty script with a (unique) command name starting with "-"
AddScriptFileToPackage( scriptRoot + "menuAction.s", pkNa, pkLe, pkLo, "Call 1", "MyMenu", "MySubMenu", 0 )
AddScriptFileToPackage( scriptRoot + "menuAction.s", pkNa, pkLe, pkLo, "Call 2", "MyMenu", "", 0 )
AddScriptToPackage( "", pkNa, pkLe, pkLo, "-sep1", "MyMenu", "", 0 )
AddScriptFileToPackage( scriptRoot + "menuAction.s", pkNa, pkLe, pkLo, "Call 3", "MyMenu", "", 0 )
// List of Scripts to be installed as library
// *********************************************
// Each entry needs a (unique) command-name. Menu-name and sub-menu name are "".
// The "isLibary" flag is set to 1
// The script will be executed once on startup (if there is executable code). It is also executed once
// here during the install.
// Methods and Classes of the script are permanently available and need unique names.
// Adding a script to the package as libary can be used to create on-load-version info output.
AddScriptFileToPackage( scriptRoot + "testLib.s", pkNa, pkLe, pkLo, "library-1", "", "", 1 )
AddScriptToPackage( "Result(\"Script packages myPkg loaded.\\n\")", pkNa, pkLe, pkLo, "myPkg-versionInfo", "", "", 1 )
After running the install-script there will be:
A menu like this:
Output in the results window like this:
A package file in the folder C:\Users\USERNAME\AppData\Local\Gatan\Plugins\myPkg.gtk
The script command TestCall() generally available in all scripts.
The package will load each time when DM starts as long as the .gtk file remains in the plugins folder.
Calling script code from within scripts
The scripting language supports two commands to call a script from within a script:
Number ExecuteScriptString( String text )
Number ExecuteScriptFile( String file_path )
Using the command to execute scripts form disc can do what you want, but maintaining a useful 'library' that way could be tedious. It also does not allow you to install classes.
Example of calling a script from within a script:
// Direct example
void Demo()
{
ClearResults()
Result( "I am a test call.\n")
number n = 5
Result( "I am working on the number: " + n )
}
Demo()
//Having the script as a string
number otherNumber = 11 // To show how you can modify a script call as an example
string scriptStr
scriptStr += "void Demo()\n{" + "\n"
scriptStr += "ClearResults()" + "\n"
scriptStr += "Result( \"I am a test call.\\n\")" + "\n"
scriptStr += "number n = " + otherNumber + "\n"
scriptStr += "Result( \"I am working on the number: \" + n )"+ "\n"
scriptStr += "}\n"
scriptStr += "Demo()\n"
If ( TwoButtonDialog("Script-call","Show it", "Run it") )
{
ClearResults()
Result( scriptStr )
}
else
ExecuteScriptString( scriptStr )
The following explicit example of build script usage may be closer to what you are looking for. It shows that in the course of a single DM session, one can edit the module source files and repeatedly rebuild the package without having to relaunch DM, contrary to the clarification about package creation provided in the answer from BmyGuest. This example also makes use of the very convenient GetCurrentScriptSourceFilePath function which greatly simplifies file path references when one can locate the build script and module source files in the same folder (this is the approach I take with my own development projects).
Here is the arrangement of my files for this example:
The two source modules are very simple function and class libraries.
Here is Module1:
void Module1SayHello()
{
OKDialog("Hello from module 1");
}
And here is Module2:
class Module2TestClass
{
void Module2SayHello(Object self)
{
OKDialog("Hello from module 2");
}
}
Here is the build script:
void main()
{
// Establish the source code directory relative to the current build script location
String buildScriptSourceFilePath;
GetCurrentScriptSourceFilePath(buildScriptSourceFilePath);
String sourceFileDir = buildScriptSourceFilePath.PathExtractDirectory(0);
// Add the modules
AddScriptFileToPackage(sourceFileDir.PathConcatenate("Module1.s"), "MultiModuleTest", 3, "Module1", "", "", 1);
AddScriptFileToPackage(sourceFileDir.PathConcatenate("Module2.s"), "MultiModuleTest", 3, "Module2", "", "", 1);
}
main();
Contrary to the above-mentioned clarification, this build script can be run multiple times during a DM session and the content of the package file gets replaced each time. So now one has a very nice development environment where one can open the source file for a module, edit it as desired, save it, and then rebuild the package file. One can use the following test script to see that the behavior changes as one edits, saves, and rebuilds the implementation of any function or method in the module source files:
void main()
{
Module1SayHello();
Alloc(Module2TestClass).Module2SayHello();
}
main();
Because of the way the DM script interpreter parses, tokenizes, and executes code, all functions and methods invoked anywhere in a script must have been previously defined before a script is executed. This is why the above test script, or any other script that uses the added modules, cannot simply be appended to the end of the build script (except if embedded in a string passed to the ExecuteScriptString function, as pointed out in the posed question). The concept of imported code modules (e.g. as in Python) is therefore not really possible in DM scripting (as pointed out in a comment to the answer by BmyGuest). In this sense, DM scripting shows its roots in 1990’s coding concepts, which commonly involved separate compilation, linking, and execution phases.
Nevertheless, the build script approach described here allows one to take advantage of the features of a true integrated development environment (IDE). For example, one can add the module source files (and build script) to a project in Visual Studio and get all the benefits of a modern multi-file code editor and revision control (e.g. via Git). This is what I do with the Enabler framework.
The one caveat is that once the DM session is closed, the plug-in (package) file does become finalized in some way so that it can no longer be replaced by the build script in a future DM session. In this case, one does have to remove the package file from the plug-ins folder before resuming another development session in DM (as covered in the clarification from BmyGuest).
For everybody else who needs this, I am using AddScriptFileToPackage() now, inspired by both, #BmyGuest and #MikeKundmann.
The following main.s is always open in my GMS. The real code I'm working on is in program.s. To test your code execute the main.s. This file can be executed multiple times in one session!
For opening GMS I use the (Windows) batch file below. This deleteds registered plugins automatically which makes the main.s usable again. For debugging I created a python script that combines all the files listed in the main.s. This way GMS jumps to the errors. This python program can be downloaded from my github page.
/**
* File: main.s
*/
String __file__;
GetCurrentScriptSourceFilePath(__file__);
String __base__ = __file__.PathExtractDirectory(0);
/**
* Load and add the file `filename`, the name will be the `filename` without
* the extension.
*
* This is dynamic only for the current session. If GMS is restarted, using
* this will create errors except if the plugins folder does not contain the
* required files (delete `%LOCALAPPDATA%\Gatan\Plugins\` before starting).
*
* #param filename The filename (or path) relative to the path of this file
* #param name The internal name to register the script with
*/
void require(String filename, String name){
// AddScriptFileToPackage(
// <file_path>,
// <packageName: filename of .gtk file in plugins>,
// <packageLevel: load order [0..3]>,
// <command_name: id/name of the libary/command>,
// <menu_name: name of the menu, ignored if isLibrary=1>
// <sub_menu_name: name of the submenu, ignored if isLibrary=1>,
// <isLibrary: wheter to add as library (1) or as menu item (0)>
// )
AddScriptFileToPackage(__base__.PathConcatenate(filename), "__require_main_" + name, 3, name, "", "", 1);
}
/**
* Require the file `filename` with the basename of the `filename` as the name.
*
* #see require(String filename, String name);
*
* #param filename The filename (or path) relative to the path of this file
*/
void require(String filename){
require(filename, PathExtractBaseName(filename, 0));
}
void main(){
// add libaries
require("string-lib.s");
// add main file
require("program.s");
}
main();
The (Windows) batch file to start GMS. This deletes the plugins folder automatically. Then the main.s does not cause any problems.
#echo off
rem
rem File: start-gatan.bat
rem ---------------------
echo Deleting GMS cached libaries...
SET plugins_path=%LOCALAPPDATA%\Gatan\Plugins\
SET gms_path=%PROGRAMFILES%\Gatan\DigitalMicrograph.exe
if exist %plugins_path% (
echo Deleting all .gtk files in %plugins_path%...
del %plugins_path%__require_main_*.gtk /F /Q
del %plugins_path%__require_main_*.gt1 /F /Q
del %plugins_path%__require_main_*.gt2 /F /Q
del %plugins_path%__require_main_*.gt3 /F /Q
if exist "%gms_path%" (
echo Starting GMS
start "" "%gms_path%"
) else (
echo GMS path %gms_path% does not exist.
pause
)
) else (
echo Plugins path %plugins_path% does not exist.
pause
)

How to get build and version number of Flutter Web app

The keyword here is Web. I can get the build and version number of a Flutter app by using the package_info plugin, but if it is running on the web I can't. How do I get the package info for a Flutter Web app?
I'll include an answer below as a temporary fix, but I am looking a solution that gets the version info from pubspec.yaml.
As a temporary workaround you can create a separate file with the version info in it:
web_version_info.dart
class WebVersionInfo {
static const String name = '1.0.0';
static const int build = 1;
}
You can use that for all platforms or in your code you can use kIsWeb to just use it for the web:
Future<String> _getAppVersion() async {
if (kIsWeb) {
return WebVersionInfo.name;
} else {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
}
}
Of course, this is not a great solution because now you need to remember to update the version and build information in both pubspec.yaml and in WebVersionInfo every time you update the app.
If you use beta channel you can use package_info_plus plugin that appears to be a drop-in-replacement for package_info. So all you need to change is pubspec.yaml and your import. (I only use version so there could be differences that I haven't noticed)
Change pubspec and your import
pubspec.yaml: package_info_plus: '>=0.6.3 <2.0.0'
import: import 'package:package_info_plus/package_info_plus.dart'
Reference:
Github issue 46609
For those using Linux and in order to improve Suragch's answer, I suggest automating the build process using bash scripts. For that, we need two scripts: one to increase the version build number and another to call the flutter build command itself, forwarding the parameters. That way, if you prefer to just increment the version build number manually, you can just call the update script and then 'flutter build' later, but if you want to do everything in one step, you can call the builder script.
You will only need to edit the '.app_version' file as the version changes.
The '.build_seq', '.version_number' files are always rewritten, and the '.app_version' file is created only if it is not found.
The scripts:
updversion.sh:
#!/bin/bash
if [ -f ".app_version" ]; then
VER=`cat .app_version`
else
VER="1.0.0"
echo $VER > .app_version
fi
if [ -f ".build_seq" ]; then
BLD=`cat .build_seq`
else
BLD='0'
fi
((BLD++))
echo $BLD > .build_seq
echo "Ver: $VER ($BLD)" > .current_version
echo "
// Auto-generated by updversion.sh. Do not edit.
class WebVersionInfo {
static const String name = '$VER';
static const int build = $BLD;
}
" > lib/version_info.dart
exit 0
buildweb.sh:
#!/bin/bash
./updversion.sh
flutter build web $*
exit $?

run part of code as root

I have a package which runs uses Gtk and written in vala.A dialog box or a gui opens after selecting a file.I want this dialog box or gui to run as root so as to open and read the files which don't open with normal users.I have this code
static void open_file(string filename) {
selected_file = filename;
stdout.printf(selected_file);
new ProgressWindow(selected_file, {});
}
I want to run ProgressWindow to run as root.Is it possible?
No. To run as root, it must be in a separate process and you must run that process using pkexec via PolicyKit. Here's a tutorial on PolicyKit in Vala.