fixes installers
This commit is contained in:
7
lib/clients/rclone/.heroscript
Normal file
7
lib/clients/rclone/.heroscript
Normal file
@@ -0,0 +1,7 @@
|
||||
!!hero_code.generate_client
|
||||
name: "rclone"
|
||||
classname: "RCloneClient"
|
||||
hasconfig: true
|
||||
singleton: true
|
||||
default: true
|
||||
title: ""
|
||||
83
lib/clients/rclone/rclone.v
Normal file
83
lib/clients/rclone/rclone.v
Normal file
@@ -0,0 +1,83 @@
|
||||
module rclone
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
// // RCloneClient represents a configured rclone instance
|
||||
// pub struct RCloneClient {
|
||||
// pub mut:
|
||||
// name string // name of the remote
|
||||
// }
|
||||
|
||||
// new creates a new RCloneClient instance
|
||||
pub fn new(name string) !RCloneClient {
|
||||
return RCloneClient{
|
||||
name: name
|
||||
}
|
||||
}
|
||||
|
||||
// mount mounts a remote at the specified path
|
||||
pub fn (mut r RCloneClient) mount(remote_path string, local_path string) ! {
|
||||
if !os.exists(local_path) {
|
||||
os.mkdir_all(local_path) or { return error('Failed to create mount directory: ${err}') }
|
||||
}
|
||||
|
||||
cmd := 'rclone mount ${r.name}:${remote_path} ${local_path} --daemon'
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Failed to mount remote: ${res.output}')
|
||||
}
|
||||
}
|
||||
|
||||
// unmount unmounts a mounted remote
|
||||
pub fn (mut r RCloneClient) unmount(local_path string) ! {
|
||||
if os.user_os() == 'macos' {
|
||||
os.execute_opt('umount ${local_path}') or { return error('Failed to unmount: ${err}') }
|
||||
} else {
|
||||
os.execute_opt('fusermount -u ${local_path}') or {
|
||||
return error('Failed to unmount: ${err}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// upload uploads a file or directory to the remote
|
||||
pub fn (mut r RCloneClient) upload(local_path string, remote_path string) ! {
|
||||
if !os.exists(local_path) {
|
||||
return error('Local path does not exist: ${local_path}')
|
||||
}
|
||||
|
||||
cmd := 'rclone copy ${local_path} ${r.name}:${remote_path}'
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Failed to upload: ${res.output}')
|
||||
}
|
||||
}
|
||||
|
||||
// download downloads a file or directory from the remote
|
||||
pub fn (mut r RCloneClient) download(remote_path string, local_path string) ! {
|
||||
if !os.exists(local_path) {
|
||||
os.mkdir_all(local_path) or { return error('Failed to create local directory: ${err}') }
|
||||
}
|
||||
|
||||
cmd := 'rclone copy ${r.name}:${remote_path} ${local_path}'
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Failed to download: ${res.output}')
|
||||
}
|
||||
}
|
||||
|
||||
// list lists contents of a remote path
|
||||
pub fn (mut r RCloneClient) list(remote_path string) !string {
|
||||
cmd := 'rclone ls ${r.name}:${remote_path}'
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Failed to list remote contents: ${res.output}')
|
||||
}
|
||||
return res.output
|
||||
}
|
||||
|
||||
// check_installed checks if rclone is installed
|
||||
pub fn check_installed() bool {
|
||||
res := os.execute('which rclone')
|
||||
return res.exit_code == 0
|
||||
}
|
||||
104
lib/clients/rclone/rclone_factory_.v
Normal file
104
lib/clients/rclone/rclone_factory_.v
Normal file
@@ -0,0 +1,104 @@
|
||||
module rclone
|
||||
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
__global (
|
||||
rclone_global map[string]&RCloneClient
|
||||
rclone_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn args_get(args_ ArgsGet) ArgsGet {
|
||||
mut model := args_
|
||||
if model.name == '' {
|
||||
model.name = rclone_default
|
||||
}
|
||||
if model.name == '' {
|
||||
model.name = 'default'
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
pub fn get(args_ ArgsGet) !&RCloneClient {
|
||||
mut model := args_get(args_)
|
||||
if model.name !in rclone_global {
|
||||
if model.name == 'default' {
|
||||
if !config_exists(model) {
|
||||
if default {
|
||||
config_save(model)!
|
||||
}
|
||||
}
|
||||
config_load(model)!
|
||||
}
|
||||
}
|
||||
return rclone_global[model.name] or {
|
||||
println(rclone_global)
|
||||
panic('could not get config for rclone with name:${model.name}')
|
||||
}
|
||||
}
|
||||
|
||||
fn config_exists(args_ ArgsGet) bool {
|
||||
mut model := args_get(args_)
|
||||
mut context := base.context() or { panic('bug') }
|
||||
return context.hero_config_exists('rclone', model.name)
|
||||
}
|
||||
|
||||
fn config_load(args_ ArgsGet) ! {
|
||||
mut model := args_get(args_)
|
||||
mut context := base.context()!
|
||||
mut heroscript := context.hero_config_get('rclone', model.name)!
|
||||
play(heroscript: heroscript)!
|
||||
}
|
||||
|
||||
fn config_save(args_ ArgsGet) ! {
|
||||
mut model := args_get(args_)
|
||||
mut context := base.context()!
|
||||
context.hero_config_set('rclone', model.name, heroscript_default()!)!
|
||||
}
|
||||
|
||||
fn set(o RCloneClient) ! {
|
||||
mut o2 := obj_init(o)!
|
||||
rclone_global[o.name] = &o2
|
||||
rclone_default = o.name
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct PlayArgs {
|
||||
pub mut:
|
||||
heroscript string // if filled in then plbook will be made out of it
|
||||
plbook ?playbook.PlayBook
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn play(args_ PlayArgs) ! {
|
||||
mut model := args_
|
||||
|
||||
if model.heroscript == '' {
|
||||
model.heroscript = heroscript_default()!
|
||||
}
|
||||
mut plbook := model.plbook or { playbook.new(text: model.heroscript)! }
|
||||
|
||||
mut install_actions := plbook.find(filter: 'rclone.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for install_action in install_actions {
|
||||
mut p := install_action.params
|
||||
mycfg := cfg_play(p)!
|
||||
console.print_debug('install action rclone.configure\n${mycfg}')
|
||||
set(mycfg)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// switch instance to be used for rclone
|
||||
pub fn switch(name string) {
|
||||
rclone_default = name
|
||||
}
|
||||
62
lib/clients/rclone/rclone_model.v
Normal file
62
lib/clients/rclone/rclone_model.v
Normal file
@@ -0,0 +1,62 @@
|
||||
module rclone
|
||||
|
||||
import freeflowuniverse.herolib.data.paramsparser
|
||||
import os
|
||||
|
||||
pub const version = '0.0.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
pub fn heroscript_default() !string {
|
||||
name := os.getenv_opt('RCLONE_NAME') or { 'default' }
|
||||
remote_type := os.getenv_opt('RCLONE_TYPE') or { 's3' }
|
||||
provider := os.getenv_opt('RCLONE_PROVIDER') or { 'aws' }
|
||||
access_key := os.getenv_opt('RCLONE_ACCESS_KEY') or { '' }
|
||||
secret_key := os.getenv_opt('RCLONE_SECRET_KEY') or { '' }
|
||||
region := os.getenv_opt('RCLONE_REGION') or { 'us-east-1' }
|
||||
endpoint := os.getenv_opt('RCLONE_ENDPOINT') or { '' }
|
||||
|
||||
heroscript := "
|
||||
!!rclone.configure
|
||||
name: '${name}'
|
||||
type: '${remote_type}'
|
||||
provider: '${provider}'
|
||||
access_key: '${access_key}'
|
||||
secret_key: '${secret_key}'
|
||||
region: '${region}'
|
||||
endpoint: '${endpoint}'
|
||||
"
|
||||
|
||||
return heroscript
|
||||
}
|
||||
|
||||
@[heap]
|
||||
pub struct RCloneClient {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
type_ string = 's3' // remote type (s3, sftp, etc)
|
||||
provider string = 'aws' // provider for s3 (aws, minio, etc)
|
||||
access_key string // access key for authentication
|
||||
secret_key string // secret key for authentication
|
||||
region string = 'us-east-1' // region for s3
|
||||
endpoint string // custom endpoint URL if needed
|
||||
}
|
||||
|
||||
fn cfg_play(p paramsparser.Params) ! {
|
||||
mut mycfg := RCloneClient{
|
||||
name: p.get_default('name', 'default')!
|
||||
type_: p.get_default('type', 's3')!
|
||||
provider: p.get_default('provider', 'aws')!
|
||||
access_key: p.get('access_key')!
|
||||
secret_key: p.get('secret_key')!
|
||||
region: p.get_default('region', 'us-east-1')!
|
||||
endpoint: p.get_default('endpoint', '')!
|
||||
}
|
||||
set(mycfg)!
|
||||
}
|
||||
|
||||
fn obj_init(obj_ RCloneClient) !RCloneClient {
|
||||
// never call get here, only thing we can do here is work on object itself
|
||||
mut obj := obj_
|
||||
return obj
|
||||
}
|
||||
39
lib/clients/rclone/rclone_test.v
Normal file
39
lib/clients/rclone/rclone_test.v
Normal file
@@ -0,0 +1,39 @@
|
||||
module rclone
|
||||
|
||||
fn test_rclone_new() {
|
||||
rclone := new('test_remote') or { panic(err) }
|
||||
assert rclone.name == 'test_remote'
|
||||
}
|
||||
|
||||
fn test_check_installed() {
|
||||
installed := check_installed()
|
||||
// This test will pass or fail depending on whether rclone is installed
|
||||
// on the system. It's mainly for documentation purposes.
|
||||
println('RCloneClient installed: ${installed}')
|
||||
}
|
||||
|
||||
// Note: The following tests are commented out as they require an actual rclone
|
||||
// configuration and remote to work with. They serve as examples of how to use
|
||||
// the RCloneClient module.
|
||||
|
||||
/*
|
||||
fn test_rclone_operations() ! {
|
||||
mut rclone := new('my_remote')!
|
||||
|
||||
// Test upload
|
||||
rclone.upload('./testdata', 'backup/testdata')!
|
||||
|
||||
// Test download
|
||||
rclone.download('backup/testdata', './testdata_download')!
|
||||
|
||||
// Test mount
|
||||
rclone.mount('backup', './mounted_backup')!
|
||||
|
||||
// Test list
|
||||
content := rclone.list('backup')!
|
||||
println(content)
|
||||
|
||||
// Test unmount
|
||||
rclone.unmount('./mounted_backup')!
|
||||
}
|
||||
*/
|
||||
68
lib/clients/rclone/readme.v.md
Normal file
68
lib/clients/rclone/readme.v.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# RCloneClient Module
|
||||
|
||||
This module provides a V language interface to RCloneClient, a command line program to manage files on cloud storage.
|
||||
|
||||
## Features
|
||||
|
||||
- Mount/unmount remote storage
|
||||
- Upload files and directories
|
||||
- Download files and directories
|
||||
- List remote contents
|
||||
- Configuration management through heroscript format
|
||||
|
||||
## Prerequisites
|
||||
|
||||
RCloneClient must be installed on your system. Visit https://rclone.org/install/ for installation instructions.
|
||||
|
||||
## Usage
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.osal.rclone
|
||||
|
||||
fn main() {
|
||||
// Create a new RCloneClient instance
|
||||
mut rc := rclone.new('my_remote') or { panic(err) }
|
||||
|
||||
// Upload a directory
|
||||
rc.upload('./local_dir', 'backup/remote_dir') or { panic(err) }
|
||||
|
||||
// Download a directory
|
||||
rc.download('backup/remote_dir', './downloaded_dir') or { panic(err) }
|
||||
|
||||
// Mount a remote
|
||||
rc.mount('backup', './mounted_backup') or { panic(err) }
|
||||
|
||||
// List contents
|
||||
content := rc.list('backup') or { panic(err) }
|
||||
println(content)
|
||||
|
||||
// Unmount when done
|
||||
rc.unmount('./mounted_backup') or { panic(err) }
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration is managed through heroscript format in `~/hero/config`. Example configuration:
|
||||
|
||||
```heroscript
|
||||
!!config.s3server_define
|
||||
name:'my_remote'
|
||||
description:'My Remote Storage'
|
||||
keyid:'your_key_id'
|
||||
keyname:'your_key_name'
|
||||
appkey:'your_app_key'
|
||||
url:'your_url'
|
||||
```
|
||||
|
||||
The configuration will be automatically loaded and applied when creating a new RCloneClient instance.
|
||||
|
||||
## Testing
|
||||
|
||||
To run the tests:
|
||||
|
||||
```bash
|
||||
vtest ~/code/github/freeflowuniverse/herolib/lib/osal/rclone/rclone_test.v
|
||||
```
|
||||
|
||||
Note: Some tests are commented out as they require an actual rclone configuration and remote to work with. They serve as examples of how to use the RCloneClient module.
|
||||
@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.lang.python
|
||||
// import os
|
||||
|
||||
|
||||
pub fn installll(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
|
||||
|
||||
pub fn installlll(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
version := '2.0.6'
|
||||
|
||||
@@ -9,7 +9,6 @@ import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
pub fn installll(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
|
||||
|
||||
@@ -124,12 +124,12 @@ pub fn (mut self Prometheus) running() !bool {
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgss {
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self Prometheus) install(model InstallArgss) ! {
|
||||
pub fn (mut self Prometheus) install(model InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if model.reset || (!installed()!) {
|
||||
install()!
|
||||
|
||||
@@ -9,28 +9,28 @@ import freeflowuniverse.herolib.sysadmin.startupmanager
|
||||
import os
|
||||
import time
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
// homedir string
|
||||
// configpath string
|
||||
// username string = "admin"
|
||||
// password string @[secret]
|
||||
// secret string @[secret]
|
||||
// title string = 'My Hero DAG'
|
||||
reset bool
|
||||
start bool = true
|
||||
stop bool
|
||||
restart bool
|
||||
uninstall bool
|
||||
// host string = 'localhost' // server host (default is localhost)
|
||||
// port int = 8888
|
||||
}
|
||||
// @[params]
|
||||
// pub struct InstallArgs {
|
||||
// pub mut:
|
||||
// // homedir string
|
||||
// // configpath string
|
||||
// // username string = "admin"
|
||||
// // password string @[secret]
|
||||
// // secret string @[secret]
|
||||
// // title string = 'My Hero DAG'
|
||||
// reset bool
|
||||
// start bool = true
|
||||
// stop bool
|
||||
// restart bool
|
||||
// uninstall bool
|
||||
// // host string = 'localhost' // server host (default is localhost)
|
||||
// // port int = 8888
|
||||
// }
|
||||
|
||||
pub fn install(args_ InstallArgs) ! {
|
||||
install_prometheus(args_)!
|
||||
install_alertmanager(args_)!
|
||||
install_node_exporter(args_)!
|
||||
install_blackbox_exporter(args_)!
|
||||
install_prom2json(args_)!
|
||||
}
|
||||
// pub fn install(args_ InstallArgs) ! {
|
||||
// install_prometheus(args_)!
|
||||
// install_alertmanager(args_)!
|
||||
// install_node_exporter(args_)!
|
||||
// install_blackbox_exporter(args_)!
|
||||
// install_prom2json(args_)!
|
||||
// }
|
||||
|
||||
@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
|
||||
|
||||
pub fn installll(args_ InstallArgs) ! {
|
||||
mut args := args_
|
||||
version := '0.16.2'
|
||||
|
||||
@@ -10,7 +10,6 @@ import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
import os
|
||||
|
||||
|
||||
// install lighttpd will return true if it was already installed
|
||||
pub fn installll(args InstallArgs) ! {
|
||||
// make sure we install base on the node
|
||||
|
||||
95
lib/osal/hostsfile/README.md
Normal file
95
lib/osal/hostsfile/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Hosts File Manager
|
||||
|
||||
This module provides functionality to manage the system's hosts file (`/etc/hosts`) in a safe and structured way. It supports both Linux and macOS systems, automatically handling sudo permissions when required.
|
||||
|
||||
## Features
|
||||
|
||||
- Read and parse the system's hosts file
|
||||
- Add new host entries with IP and domain
|
||||
- Remove host entries by domain
|
||||
- Manage sections with comments
|
||||
- Remove or clear entire sections
|
||||
- Check for existing domains
|
||||
- Automatic sudo handling for macOS and Linux when needed
|
||||
|
||||
## Usage
|
||||
|
||||
Create a file `example.vsh`:
|
||||
|
||||
```v
|
||||
#!/usr/bin/env -S v -n -w -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import freeflowuniverse.herolib.osal.hostsfile
|
||||
import os
|
||||
|
||||
// Create a new instance by reading the hosts file
|
||||
mut hosts := hostsfile.new() or {
|
||||
eprintln('Failed to read hosts file: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Add a new host entry to a section
|
||||
hosts.add_host('127.0.0.1', 'mysite.local', 'Development') or {
|
||||
eprintln('Failed to add host: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Remove a host entry
|
||||
hosts.remove_host('mysite.local') or {
|
||||
eprintln('Failed to remove host: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Check if a domain exists
|
||||
if hosts.exists('example.com') {
|
||||
println('Domain exists')
|
||||
}
|
||||
|
||||
// Clear all entries in a section
|
||||
hosts.clear_section('Development') or {
|
||||
eprintln('Failed to clear section: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Remove an entire section
|
||||
hosts.remove_section('Development') or {
|
||||
eprintln('Failed to remove section: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Save changes back to the hosts file
|
||||
// This will automatically use sudo when needed
|
||||
hosts.save() or {
|
||||
eprintln('Failed to save hosts file: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
The hosts file is organized into sections marked by comments. For example:
|
||||
|
||||
```
|
||||
# Development
|
||||
127.0.0.1 localhost
|
||||
127.0.0.1 mysite.local
|
||||
|
||||
# Production
|
||||
192.168.1.100 prod.example.com
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All functions that can fail return a Result type and should be handled appropriately:
|
||||
|
||||
```v
|
||||
hosts.add_host('127.0.0.1', 'mysite.local', 'Development') or {
|
||||
eprintln('Failed to add host: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Support
|
||||
|
||||
- Linux: Direct write with fallback to sudo if needed
|
||||
- macOS: Always uses sudo due to system restrictions
|
||||
@@ -3,7 +3,7 @@ module hostsfile
|
||||
import os
|
||||
import freeflowuniverse.herolib.osal
|
||||
|
||||
// TODO: will be broken now
|
||||
const hosts_file_path = '/etc/hosts'
|
||||
|
||||
@[heap]
|
||||
pub struct HostsFile {
|
||||
@@ -23,119 +23,182 @@ pub mut:
|
||||
domain string
|
||||
}
|
||||
|
||||
// pub fn new() HostsFile {
|
||||
// mut obj := HostsFile{}
|
||||
// new creates a new HostsFile instance by reading the system's hosts file
|
||||
pub fn new() !HostsFile {
|
||||
mut obj := HostsFile{}
|
||||
mut content := os.read_file(hosts_file_path) or {
|
||||
return error('Failed to read hosts file: ${err}')
|
||||
}
|
||||
mut current_section := Section{
|
||||
name: ''
|
||||
}
|
||||
|
||||
// mut content := os.read_file('/etc/hosts') or { panic(err) }
|
||||
// mut section := ''
|
||||
for line in content.split_into_lines() {
|
||||
trimmed := line.trim_space()
|
||||
if trimmed == '' {
|
||||
continue
|
||||
}
|
||||
|
||||
// for mut line in content.split('\n') {
|
||||
// line = line.trim_space()
|
||||
// if line.starts_with('#') {
|
||||
// section = line.trim('#').trim_space()
|
||||
// continue
|
||||
// }
|
||||
if trimmed.starts_with('#') {
|
||||
// If we have hosts in the current section, add it to sections
|
||||
if current_section.hosts.len > 0 {
|
||||
obj.sections << current_section
|
||||
}
|
||||
// Start a new section
|
||||
current_section = Section{
|
||||
name: trimmed[1..].trim_space()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// mut splitted := line.fields()
|
||||
// if splitted.len > 1 {
|
||||
// if section !in obj.hosts {
|
||||
// obj.hosts[section] = []map[string]string{}
|
||||
// }
|
||||
// obj.hosts[section] << {
|
||||
// splitted[0]: splitted[1]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return obj
|
||||
// }
|
||||
// Parse host entries
|
||||
parts := trimmed.fields()
|
||||
if parts.len >= 2 {
|
||||
current_section.hosts << Host{
|
||||
ip: parts[0]
|
||||
domain: parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn (mut hostsfile HostsFile) save(sudo bool) &HostsFile {
|
||||
// mut str := ''
|
||||
// for section, items in hostsfile.hosts {
|
||||
// if section != '' {
|
||||
// str = str + '# ${section}\n\n'
|
||||
// }
|
||||
// Add the last section if it has hosts
|
||||
if current_section.hosts.len > 0 {
|
||||
obj.sections << current_section
|
||||
}
|
||||
|
||||
// for item in items {
|
||||
// for ip, domain in item {
|
||||
// str = str + '${ip}\t${domain}\n'
|
||||
// }
|
||||
// }
|
||||
// str = str + '\n\n'
|
||||
// }
|
||||
// if sudo {
|
||||
// osal.execute_interactive('sudo -- sh -c -e "echo \'${str}\' > /etc/hosts"') or {
|
||||
// panic(err)
|
||||
// }
|
||||
// } else {
|
||||
// os.write_file('/etc/hosts', str) or { panic(err) }
|
||||
// }
|
||||
// return hostsfile
|
||||
// }
|
||||
return obj
|
||||
}
|
||||
|
||||
// pub fn (mut hostsfile HostsFile) reset(sections []string) &HostsFile {
|
||||
// for section in sections {
|
||||
// if section in hostsfile.hosts {
|
||||
// hostsfile.hosts[section] = []map[string]string{}
|
||||
// }
|
||||
// }
|
||||
// return hostsfile
|
||||
// }
|
||||
// add_host adds a new host entry to the specified section
|
||||
pub fn (mut h HostsFile) add_host(ip string, domain string, section string) ! {
|
||||
// Validate inputs
|
||||
if ip == '' {
|
||||
return error('IP address cannot be empty')
|
||||
}
|
||||
if domain == '' {
|
||||
return error('Domain cannot be empty')
|
||||
}
|
||||
|
||||
// pub struct HostItemArg{
|
||||
// pub mut:
|
||||
// ip string
|
||||
// domain string
|
||||
// section string = "main"
|
||||
// }
|
||||
// Check if domain already exists
|
||||
if h.exists(domain) {
|
||||
return error('Domain ${domain} already exists in hosts file')
|
||||
}
|
||||
|
||||
// pub fn (mut hostsfile HostsFile) add(args HostItemArg) &HostsFile {
|
||||
// if args.section !in hostsfile.hosts {
|
||||
// hostsfile.hosts[args.section] = []map[string]string{}
|
||||
// }
|
||||
// hostsfile.hosts[args.section] << {
|
||||
// ip: domain
|
||||
// }
|
||||
// return hostsfile
|
||||
// }
|
||||
// Find or create section
|
||||
mut found_section := false
|
||||
for mut s in h.sections {
|
||||
if s.name == section {
|
||||
s.hosts << Host{
|
||||
ip: ip
|
||||
domain: domain
|
||||
}
|
||||
found_section = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn (mut hostsfile HostsFile) delete(domain string) &HostsFile {
|
||||
// mut indexes := map[string][]int{}
|
||||
if !found_section {
|
||||
h.sections << Section{
|
||||
name: section
|
||||
hosts: [Host{
|
||||
ip: ip
|
||||
domain: domain
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for section, items in hostsfile.hosts {
|
||||
// indexes[section] = []int{}
|
||||
// for i, item in items {
|
||||
// for _, dom in item {
|
||||
// if dom == domain {
|
||||
// indexes[section] << i
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// remove_host removes all entries for the specified domain
|
||||
pub fn (mut h HostsFile) remove_host(domain string) ! {
|
||||
mut found := false
|
||||
for mut section in h.sections {
|
||||
// Filter out hosts with matching domain
|
||||
old_len := section.hosts.len
|
||||
section.hosts = section.hosts.filter(it.domain != domain)
|
||||
if section.hosts.len < old_len {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
// for section, items in indexes {
|
||||
// for i in items {
|
||||
// hostsfile.hosts[section].delete(i)
|
||||
// }
|
||||
// }
|
||||
if !found {
|
||||
return error('Domain ${domain} not found in hosts file')
|
||||
}
|
||||
}
|
||||
|
||||
// return hostsfile
|
||||
// }
|
||||
// exists checks if a domain exists in any section
|
||||
pub fn (h &HostsFile) exists(domain string) bool {
|
||||
for section in h.sections {
|
||||
for host in section.hosts {
|
||||
if host.domain == domain {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// pub fn (mut hostsfile HostsFile) delete_section(section string) &HostsFile {
|
||||
// hostsfile.hosts.delete(section)
|
||||
// return hostsfile
|
||||
// }
|
||||
// save writes the hosts file back to disk
|
||||
pub fn (h &HostsFile) save() ! {
|
||||
mut content := ''
|
||||
|
||||
// pub fn (mut hostsfile HostsFile) exists(domain string) bool {
|
||||
// for _, items in hostsfile.hosts {
|
||||
// for item in items {
|
||||
// for _, dom in item {
|
||||
// if dom == domain {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
for section in h.sections {
|
||||
if section.name != '' {
|
||||
content += '# ${section.name}\n'
|
||||
}
|
||||
|
||||
for host in section.hosts {
|
||||
content += '${host.ip}\t${host.domain}\n'
|
||||
}
|
||||
content += '\n'
|
||||
}
|
||||
|
||||
// Check if we're on macOS
|
||||
is_macos := os.user_os() == 'macos'
|
||||
|
||||
if is_macos {
|
||||
// On macOS, we need to use sudo
|
||||
osal.execute_interactive('sudo -- sh -c -e "echo \'${content}\' > ${hosts_file_path}"') or {
|
||||
return error('Failed to write hosts file with sudo: ${err}')
|
||||
}
|
||||
} else {
|
||||
// On Linux, try direct write first, fallback to sudo if needed
|
||||
os.write_file(hosts_file_path, content) or {
|
||||
// If direct write fails, try with sudo
|
||||
osal.execute_interactive('sudo -- sh -c -e "echo \'${content}\' > ${hosts_file_path}"') or {
|
||||
return error('Failed to write hosts file: ${err}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove_section removes an entire section and its hosts
|
||||
pub fn (mut h HostsFile) remove_section(section_name string) ! {
|
||||
mut found := false
|
||||
for i, section in h.sections {
|
||||
if section.name == section_name {
|
||||
h.sections.delete(i)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return error('Section ${section_name} not found')
|
||||
}
|
||||
}
|
||||
|
||||
// clear_section removes all hosts from a section but keeps the section
|
||||
pub fn (mut h HostsFile) clear_section(section_name string) ! {
|
||||
mut found := false
|
||||
for mut section in h.sections {
|
||||
if section.name == section_name {
|
||||
section.hosts.clear()
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return error('Section ${section_name} not found')
|
||||
}
|
||||
}
|
||||
|
||||
110
lib/osal/hostsfile/hostsfile_test.v
Normal file
110
lib/osal/hostsfile/hostsfile_test.v
Normal file
@@ -0,0 +1,110 @@
|
||||
module hostsfile
|
||||
|
||||
fn test_hostsfile_basic() {
|
||||
// Create new hosts file instance
|
||||
mut hosts := new() or {
|
||||
assert false, 'Failed to create hosts file: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Test adding a host
|
||||
hosts.add_host('127.0.0.1', 'test.local', 'Test') or {
|
||||
assert false, 'Failed to add host: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Verify host exists
|
||||
assert hosts.exists('test.local'), 'Added host test.local not found'
|
||||
|
||||
// Test adding duplicate host (should fail)
|
||||
hosts.add_host('127.0.0.1', 'test.local', 'Test') or {
|
||||
assert err.str() == 'Domain test.local already exists in hosts file'
|
||||
goto next_test
|
||||
}
|
||||
assert false, 'Adding duplicate host should fail'
|
||||
next_test:
|
||||
// Test removing host
|
||||
hosts.remove_host('test.local') or {
|
||||
assert false, 'Failed to remove host: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Verify host was removed
|
||||
assert !hosts.exists('test.local'), 'Host test.local still exists after removal'
|
||||
|
||||
// Test removing non-existent host (should fail)
|
||||
hosts.remove_host('nonexistent.local') or {
|
||||
assert err.str() == 'Domain nonexistent.local not found in hosts file'
|
||||
return
|
||||
}
|
||||
assert false, 'Removing non-existent host should fail'
|
||||
}
|
||||
|
||||
fn test_hostsfile_sections() {
|
||||
mut hosts := new() or {
|
||||
assert false, 'Failed to create hosts file: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Add hosts to different sections
|
||||
hosts.add_host('127.0.0.1', 'dev.local', 'Development') or {
|
||||
assert false, 'Failed to add dev host: ${err}'
|
||||
return
|
||||
}
|
||||
hosts.add_host('127.0.0.1', 'prod.local', 'Production') or {
|
||||
assert false, 'Failed to add prod host: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Verify both hosts exist
|
||||
assert hosts.exists('dev.local'), 'dev.local not found'
|
||||
assert hosts.exists('prod.local'), 'prod.local not found'
|
||||
|
||||
// Test clearing a section
|
||||
hosts.clear_section('Development') or {
|
||||
assert false, 'Failed to clear Development section: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Verify Development host removed but Production remains
|
||||
assert !hosts.exists('dev.local'), 'dev.local still exists after clearing section'
|
||||
assert hosts.exists('prod.local'), 'prod.local was incorrectly removed'
|
||||
|
||||
// Test removing a section
|
||||
hosts.remove_section('Production') or {
|
||||
assert false, 'Failed to remove Production section: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Verify all hosts removed
|
||||
assert !hosts.exists('dev.local'), 'dev.local still exists'
|
||||
assert !hosts.exists('prod.local'), 'prod.local still exists'
|
||||
|
||||
// Test removing non-existent section (should fail)
|
||||
hosts.remove_section('NonExistent') or {
|
||||
assert err.str() == 'Section NonExistent not found'
|
||||
return
|
||||
}
|
||||
assert false, 'Removing non-existent section should fail'
|
||||
}
|
||||
|
||||
fn test_hostsfile_validation() {
|
||||
mut hosts := new() or {
|
||||
assert false, 'Failed to create hosts file: ${err}'
|
||||
return
|
||||
}
|
||||
|
||||
// Test empty IP
|
||||
hosts.add_host('', 'test.local', 'Test') or {
|
||||
assert err.str() == 'IP address cannot be empty'
|
||||
goto next_test1
|
||||
}
|
||||
assert false, 'Empty IP should fail'
|
||||
next_test1:
|
||||
// Test empty domain
|
||||
hosts.add_host('127.0.0.1', '', 'Test') or {
|
||||
assert err.str() == 'Domain cannot be empty'
|
||||
return
|
||||
}
|
||||
assert false, 'Empty domain should fail'
|
||||
}
|
||||
@@ -1,13 +1,61 @@
|
||||
# screen
|
||||
# Screen
|
||||
|
||||
The Screen module provides a V interface to manage GNU Screen sessions.
|
||||
|
||||
## Example Script
|
||||
|
||||
Create a file `screen_example.vsh`:
|
||||
|
||||
```v
|
||||
#!/usr/bin/env -S v run
|
||||
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
|
||||
// Create a new screen session with hardcoded parameters
|
||||
mut s := screen.Screen{
|
||||
name: 'test_session'
|
||||
cmd: '/bin/bash' // Default shell
|
||||
}
|
||||
|
||||
// Check if screen is running
|
||||
is_running := s.is_running() or {
|
||||
println('Error checking screen status: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
// Get session status
|
||||
status := s.status() or {
|
||||
println('Error getting status: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
// Send a command to the screen session
|
||||
s.cmd_send('ls -la') or {
|
||||
println('Error sending command: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
// Attach to the session
|
||||
s.attach() or {
|
||||
println('Error attaching: ${err}')
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Screen Commands
|
||||
|
||||
```bash
|
||||
#to see sessions which have been created
|
||||
|
||||
screen -ls
|
||||
|
||||
There is a screen on:
|
||||
3230.test (Detached)
|
||||
3230.test (Detached)
|
||||
|
||||
#now to attach to this screen
|
||||
screen -r test
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
vtest ~/code/github/freeflowuniverse/herolib/lib/osal/screen/screen_test.v
|
||||
|
||||
@@ -1,17 +1,148 @@
|
||||
module screen
|
||||
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.osal
|
||||
import os
|
||||
import time
|
||||
|
||||
const test_screen_name = 'test_screen_session'
|
||||
const test_cmd = 'echo "test command"'
|
||||
|
||||
// Initialize test environment
|
||||
pub fn testsuite_begin() ! {
|
||||
// Check if screen is installed
|
||||
res := os.execute('which screen')
|
||||
if res.exit_code != 0 {
|
||||
return error('screen is not installed. Please install screen first.')
|
||||
}
|
||||
|
||||
// Ensure screen directory exists with proper permissions
|
||||
home := os.home_dir()
|
||||
screen_dir := '${home}/.screen'
|
||||
if !os.exists(screen_dir) {
|
||||
// Create directory with proper permissions using mkdir -m
|
||||
res2 := os.execute('mkdir -m 700 ${screen_dir}')
|
||||
if res2.exit_code != 0 {
|
||||
return error('Failed to create screen directory: ${res2.output}')
|
||||
}
|
||||
}
|
||||
|
||||
mut screen_factory := new(reset: true)!
|
||||
cleanup_test_screens()!
|
||||
}
|
||||
|
||||
pub fn test_screen_status() ! {
|
||||
mut screen_factory := new()!
|
||||
mut screen := screen_factory.add(name: 'testservice', cmd: 'redis-server --port 1234')!
|
||||
// Cleanup after all tests
|
||||
pub fn testsuite_end() ! {
|
||||
cleanup_test_screens()!
|
||||
}
|
||||
|
||||
fn cleanup_test_screens() ! {
|
||||
mut screen_factory := new(reset: false)!
|
||||
screen_factory.scan()!
|
||||
|
||||
// Clean up main test screen
|
||||
if screen_factory.exists(test_screen_name) {
|
||||
screen_factory.kill(test_screen_name)!
|
||||
time.sleep(200 * time.millisecond) // Give time for cleanup
|
||||
}
|
||||
|
||||
// Clean up multiple test screens
|
||||
if screen_factory.exists('${test_screen_name}_1') {
|
||||
screen_factory.kill('${test_screen_name}_1')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
if screen_factory.exists('${test_screen_name}_2') {
|
||||
screen_factory.kill('${test_screen_name}_2')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
|
||||
// Final scan to ensure cleanup
|
||||
screen_factory.scan()!
|
||||
}
|
||||
|
||||
// Helper function to create and verify screen
|
||||
fn create_and_verify_screen(mut screen_factory ScreensFactory, name string, cmd string) !&Screen {
|
||||
mut screen := screen_factory.add(
|
||||
name: name
|
||||
cmd: cmd
|
||||
)!
|
||||
|
||||
// Give screen time to initialize
|
||||
time.sleep(500 * time.millisecond)
|
||||
|
||||
// Verify screen exists and is running
|
||||
screen_factory.scan()!
|
||||
if !screen_factory.exists(name) {
|
||||
return error('Screen ${name} was not found after creation')
|
||||
}
|
||||
|
||||
mut result := screen_factory.get(name)!
|
||||
return &result
|
||||
}
|
||||
|
||||
// Test screen creation and basic status
|
||||
pub fn test_screen_creation() ! {
|
||||
mut screen_factory := new(reset: false)!
|
||||
mut screen := create_and_verify_screen(mut &screen_factory, test_screen_name, '/bin/bash')!
|
||||
|
||||
assert screen.name == test_screen_name
|
||||
status := screen.status()!
|
||||
assert status == .active
|
||||
}
|
||||
|
||||
// Test command sending functionality
|
||||
pub fn test_screen_cmd_send() ! {
|
||||
mut screen_factory := new(reset: false)!
|
||||
mut screen := create_and_verify_screen(mut &screen_factory, test_screen_name, '/bin/bash')!
|
||||
|
||||
// Send a test command
|
||||
screen.cmd_send(test_cmd)!
|
||||
|
||||
// Give some time for command execution
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
// Verify screen status after command
|
||||
status := screen.status()!
|
||||
assert status == .active
|
||||
}
|
||||
|
||||
// Test error cases
|
||||
pub fn test_screen_errors() ! {
|
||||
mut screen_factory := new(reset: false)!
|
||||
|
||||
// Test invalid screen name
|
||||
if _ := screen_factory.get('nonexistent_screen') {
|
||||
assert false, 'Should not find nonexistent screen'
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
|
||||
// Test screen status after creation but before start
|
||||
mut screen := screen_factory.add(
|
||||
name: test_screen_name
|
||||
cmd: '/bin/bash'
|
||||
start: false
|
||||
)!
|
||||
status := screen.status()!
|
||||
assert status == .inactive, 'Screen should be inactive before start'
|
||||
}
|
||||
|
||||
// Test multiple screens
|
||||
pub fn test_multiple_screens() ! {
|
||||
mut screen_factory := new(reset: false)!
|
||||
|
||||
screen1_name := '${test_screen_name}_1'
|
||||
screen2_name := '${test_screen_name}_2'
|
||||
|
||||
mut screen1 := create_and_verify_screen(mut &screen_factory, screen1_name, '/bin/bash')!
|
||||
mut screen2 := create_and_verify_screen(mut &screen_factory, screen2_name, '/bin/bash')!
|
||||
|
||||
assert screen1.status()! == .active
|
||||
assert screen2.status()! == .active
|
||||
|
||||
screen_factory.kill(screen1_name)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
assert screen1.status()! == .inactive
|
||||
assert screen2.status()! == .active
|
||||
|
||||
screen_factory.kill(screen2_name)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
|
||||
@@ -14,18 +14,3 @@ sm.start(
|
||||
```
|
||||
|
||||
|
||||
## some basic commands for screen
|
||||
|
||||
```bash
|
||||
#list the screens
|
||||
screen -ls
|
||||
#attach to the screens
|
||||
screen -r myscreen
|
||||
```
|
||||
|
||||
to exit a screen to
|
||||
|
||||
```
|
||||
ctrl a d
|
||||
```
|
||||
|
||||
|
||||
@@ -3,13 +3,31 @@ module startupmanager
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.osal.screen
|
||||
import freeflowuniverse.herolib.osal.systemd
|
||||
import os
|
||||
import time
|
||||
|
||||
const process_name = 'testprocess'
|
||||
|
||||
// Initialize test environment
|
||||
pub fn testsuite_begin() ! {
|
||||
// Initialize screen factory
|
||||
mut screen_factory := screen.new(reset: true)!
|
||||
|
||||
// Ensure screen directory exists with proper permissions
|
||||
home := os.home_dir()
|
||||
screen_dir := '${home}/.screen'
|
||||
if !os.exists(screen_dir) {
|
||||
res := os.execute('mkdir -m 700 ${screen_dir}')
|
||||
if res.exit_code != 0 {
|
||||
return error('Failed to create screen directory: ${res.output}')
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any existing process
|
||||
mut sm := get()!
|
||||
if sm.exists(process_name)! {
|
||||
sm.stop(process_name)!
|
||||
time.sleep(200 * time.millisecond) // Give time for cleanup
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,22 +35,115 @@ pub fn testsuite_end() ! {
|
||||
mut sm := get()!
|
||||
if sm.exists(process_name)! {
|
||||
sm.stop(process_name)!
|
||||
time.sleep(200 * time.millisecond) // Give time for cleanup
|
||||
}
|
||||
|
||||
// Clean up screen sessions
|
||||
mut screen_factory := screen.new(reset: false)!
|
||||
screen_factory.scan()!
|
||||
if screen_factory.exists(process_name) {
|
||||
screen_factory.kill(process_name)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// remove from the startup manager
|
||||
// Test startup manager status functionality
|
||||
pub fn test_status() ! {
|
||||
mut sm := get()!
|
||||
mut screen_factory := screen.new(reset: false)!
|
||||
|
||||
if sm.exists(process_name)! {
|
||||
sm.stop(process_name)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
sm.start(process_name)!
|
||||
time.sleep(500 * time.millisecond) // Give time for startup
|
||||
|
||||
status := sm.status(process_name)!
|
||||
assert status == .inactive
|
||||
} else {
|
||||
sm.new(name: process_name, cmd: 'sleep 100')!
|
||||
// Create new process with screen session
|
||||
sm.new(
|
||||
name: process_name
|
||||
cmd: 'sleep 100'
|
||||
description: 'Test process for startup manager'
|
||||
)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
sm.start(process_name)!
|
||||
time.sleep(500 * time.millisecond) // Give time for startup
|
||||
|
||||
status := sm.status(process_name)!
|
||||
assert status == .active
|
||||
|
||||
// Verify screen session
|
||||
screen_factory.scan()!
|
||||
assert screen_factory.exists(process_name), 'Screen session not found'
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
sm.stop(process_name)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
}
|
||||
|
||||
// Test process creation with description
|
||||
pub fn test_process_with_description() ! {
|
||||
mut sm := get()!
|
||||
mut screen_factory := screen.new(reset: false)!
|
||||
|
||||
description := 'Test process with custom description'
|
||||
|
||||
// Create new process
|
||||
sm.new(
|
||||
name: '${process_name}_desc'
|
||||
cmd: 'sleep 50'
|
||||
description: description
|
||||
)!
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
// Start and verify
|
||||
sm.start('${process_name}_desc')!
|
||||
time.sleep(500 * time.millisecond)
|
||||
|
||||
// Verify screen session
|
||||
screen_factory.scan()!
|
||||
assert screen_factory.exists('${process_name}_desc'), 'Screen session not found'
|
||||
|
||||
// Verify screen is running
|
||||
mut screen := screen_factory.get('${process_name}_desc')!
|
||||
assert screen.is_running()!, 'Screen should be running'
|
||||
|
||||
// Cleanup
|
||||
sm.stop('${process_name}_desc')!
|
||||
time.sleep(200 * time.millisecond)
|
||||
|
||||
// Verify screen is not running after cleanup
|
||||
assert !screen.is_running()!, 'Screen should not be running after cleanup'
|
||||
}
|
||||
|
||||
// Test error handling
|
||||
pub fn test_error_handling() ! {
|
||||
mut sm := get()!
|
||||
mut screen_factory := screen.new(reset: false)!
|
||||
|
||||
// Test non-existent process
|
||||
if _ := sm.status('nonexistent_process') {
|
||||
assert false, 'Should not get status of non-existent process'
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
|
||||
// Test invalid screen session
|
||||
if _ := screen_factory.get('nonexistent_screen') {
|
||||
assert false, 'Should not get non-existent screen'
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
|
||||
// Test stopping non-existent process
|
||||
if _ := sm.stop('nonexistent_process') {
|
||||
assert false, 'Should not stop non-existent process'
|
||||
} else {
|
||||
assert true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user