├── Evaluation
└── README.md
├── Pilot Study
├── Bugs-visual-cue.png
├── GitHub_Bug_Report_all.xlsx
├── Pilot-study.png
└── README.md
├── README.md
├── VisionDroid Code
├── AndroidManifest.xml
├── GUIGet
│ ├── build.gradle
│ ├── build
│ │ └── intermediates
│ │ │ └── lint-cache
│ │ │ ├── maven.google
│ │ │ ├── com
│ │ │ │ └── android
│ │ │ │ │ └── support
│ │ │ │ │ ├── constraint
│ │ │ │ │ └── group-index.xml
│ │ │ │ │ ├── group-index.xml
│ │ │ │ │ └── test
│ │ │ │ │ ├── espresso
│ │ │ │ │ └── group-index.xml
│ │ │ │ │ └── group-index.xml
│ │ │ └── master-index.xml
│ │ │ └── sdk-registry.xml
│ │ │ └── sdk-registry.xml
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── local.properties
│ └── settings.gradle
├── GptModel.py
├── README.md
├── Scene_1
│ └── annotated_image
│ │ └── example.png
├── SparkApi.py
├── SparkModel.py
├── action.py
├── assets
│ └── workflow.png
├── cmd.py
├── detector.py
├── extract_activities.py
├── graph.py
├── info.py
├── main.py
├── process_image.py
├── prompt.py
└── venv
│ ├── Scripts
│ ├── __pycache__
│ │ ├── pywin32_postinstall.cpython-39.pyc
│ │ ├── pywin32_testall.cpython-39.pyc
│ │ └── readelf.cpython-39.pyc
│ ├── activate
│ ├── activate.bat
│ ├── activate.fish
│ ├── activate.nu
│ ├── activate.ps1
│ ├── activate_this.py
│ ├── deactivate.bat
│ ├── deactivate.nu
│ ├── f2py.exe
│ ├── normalizer.exe
│ ├── openai.exe
│ ├── pip-3.9.exe
│ ├── pip.exe
│ ├── pip3.9.exe
│ ├── pip3.exe
│ ├── pydoc.bat
│ ├── python.exe
│ ├── pythonw.exe
│ ├── pywin32_postinstall.py
│ ├── pywin32_testall.py
│ ├── readelf.py
│ ├── tqdm.exe
│ ├── uiautomator2.exe
│ ├── wheel-3.9.exe
│ ├── wheel.exe
│ ├── wheel3.9.exe
│ └── wheel3.exe
│ └── pyvenv.cfg
└── workflow.png
/Evaluation/README.md:
--------------------------------------------------------------------------------
1 | # Evaluation
2 |
3 |
4 | The apks confirmed or fixed from usefulness evaluation.
5 |
6 | Because the Google Play requires that individuals cannot upload apk without permission. You can download them on Google play through the information in the table.
7 |
8 | **ID** | **App name** | **Category** | **Download** | **Status**
9 | :-: | :-: | :-: | :-: | :-:
10 | 1 | MOMPA | Music | 500M+ | fixed
11 | 2 | Calendar | Tool | 100M+ | fixed
12 | 3 | DigiCal | Tool | 100M+ | fixed
13 | 4 | Expensify | Finance | 100M+ | fixed
14 | 5 | TrackWallet | Finance | 10M+ | confirmed
15 | 6 | Roadie | Travel | 10M+ | confirmed
16 | 7 | ISAY | Commun | 10M+ | fixed
17 | 8 | EasyBudget | Finance | 10M+ | confirmed
18 | 9 | Iplan | Travel | 10M+ | fixed
19 | 10 | MediaFire | Product | 5M+ | confirmed
20 | 11 | SmartGuide | Navig | 500K+ | fixed
21 | 12 | MMDR | Utilities | 500K+ | confirmed
22 | 13 | Sygic | Travel | 500K+ | fixed
23 | 14 | Fair | Health | 500K+ | confirmed
24 | 15 | ClassySha | Tool | 500K+ | fixed
25 | 16 | Linphone | Commun | 50K+ | confirmed
26 | 17 | Rocket | Finance | 50K+ | confirmed
27 | 18 | Monefy | Finance | 50K+ | fixed
28 | 19 | Origin | Finance | 50K+ | confirmed
29 |
30 |
--------------------------------------------------------------------------------
/Pilot Study/Bugs-visual-cue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/Pilot Study/Bugs-visual-cue.png
--------------------------------------------------------------------------------
/Pilot Study/GitHub_Bug_Report_all.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/Pilot Study/GitHub_Bug_Report_all.xlsx
--------------------------------------------------------------------------------
/Pilot Study/Pilot-study.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/Pilot Study/Pilot-study.png
--------------------------------------------------------------------------------
/Pilot Study/README.md:
--------------------------------------------------------------------------------
1 | # Pilot Study
2 |
3 | ## Data
4 | 1. GitHub bug reports in `GitHub_Bug_Report_all.xlsx`
5 | 2. Our experimental dataset is collected from one of the largest open-source Android app hosting websites (GitHub), which has a great number of Android bug reports.
6 | 3. We employ the following criteria for app selection: more than 1K downloads in Google Play (popular), a public issue tracking system (traceable), and more than three years of development history (trustworthy).
7 |
8 | ## Bug categories
9 | 1. Intra-page non-crash functional bug denotes these bugs only related to a single GUI page, mainly involving the issues about information display as shown in Figure.
10 | 2. Inter-page non-crash functional bug denotes these bugs related to multiple GUI pages, i.e., involving a testing sequence to trigger it, with details in Figure.
11 |
12 | 
13 |
14 | ## Bug Examples
15 | 1. Intra-page non-crash functional bug examples as shown in Figure.
16 | 
17 | 2. Inter-page non-crash functional bug examples as shown in Figure.
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VisionDroid
2 |
3 | VisionDroid, a vison-driven approach for detecting non-crash functional bugs with MLLM. It begins by extracting GUI text information and aligning it with GUI screenshots to form a vision prompt, enabling MLLM to understand GUI context. Second, the function-aware explorer utilizes the MLLM for GUI page exploration. It maintains a testing history comprising both the higher-level catalogue of testing progress and a detailed snapshot of each performed action, to enhance the exploration to be deeper and function-oriented. Third, the logic-aware bug detector breaks the entire exploration sequence into logically cohesive parts that mirror the app’s logical operational sequences. Based on them, the MLLM detects the bugs potentially by checking whether the GUI information changes between page transitions align with the processing logic.
4 |
5 | ## structure
6 |
7 | 
8 |
9 | # VisionDroid Source code
10 |
11 | ## How to use
12 | 1. Generate Your API Key: Before we start working with the GPT-4 API, we need to login into OpenAI account and generate our API keys.
13 | `openai.api_key = "XXXXXXX"`
14 | 2. Installing the library: To work with the GPT-4 API, first, we have to install the openai library by running the following command.
15 | 3. Using “ChatCompletion” gpt-4-vision-preview, which is the same model used by GPT-4.
16 |
17 | `from openai import OpenAI`
18 |
19 | `client = OpenAI()`
20 |
21 | `response = client.chat.completions.create(`
22 |
23 | `model="gpt-4-vision-preview",`
24 |
25 | `messages=[`
26 |
27 | `{`
28 |
29 | `"role": "user",`
30 |
31 | `"content": [`
32 |
33 | `{"type": "text", "text": "What’s in this image?"},`
34 |
35 | `{
36 |
37 | ` "type": "image_url",`
38 |
39 | `"image_url": {`
40 |
41 | ` "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",'
42 |
43 | `},`
44 |
45 | `},`
46 |
47 | `],`
48 |
49 | `}`
50 |
51 | `],`
52 |
53 | `max_tokens=300,`
54 |
55 | `)`
56 |
57 |
58 | ### Requirements
59 | * Android emulator
60 | * Ubuntu or Windows
61 | * Appium Desktop Client: [link](https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4)
62 | * Python 3.7
63 | * apkutils==0.10.2
64 | * Appium-Python-Client==1.3.0
65 | * Levenshtein==0.18.1
66 | * lxml==4.8.0
67 | * opencv-python==4.5.5.64
68 | * sentence-transformers==1.0.3
69 | * torch==1.6.0
70 | * torchvision==0.7.0
71 |
72 | Use the gpt-4.
73 |
74 |
75 |
76 |
77 | ## This example introduces how to use ChatGPT
78 |
79 | [a-simple-guide-to-gpt-4-api-with-python](https://platform.openai.com/docs/guides/vision)
80 |
81 |
82 | ## LLM Test
83 |
84 | USE GPT-4 App Testing
85 |
86 | 
87 |
88 | ### usage
89 |
90 | Find app, change `cmd.py` `PACKAGE = "xxx"` as package name
91 |
92 | Package name in `adb shell dumpsys activity activities | grep mControlTarget=Window`
93 |
94 | Opensource app, find `AndroidManifest.xml` change `/main/app/src/main/AndroidManifest.xml`
95 |
96 |
97 | ### Http
98 |
99 | `GptModel.py`
100 |
101 | ```
102 | import os
103 | os.environ['HTTP_PROXY'] = 'http://127.0.0.1:10809'
104 | os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:10809'
105 | ```
106 |
107 |
108 | ### How to set up a proxy for OpenAI's API in Python?
109 |
110 | https://github.com/Onelinerhub/onelinerhub/blob/main//python-openai/how-to-set-up-a-proxy-for-openai-s-api-in-python.md
111 |
--------------------------------------------------------------------------------
/VisionDroid Code/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:4.2.0'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | maven { url 'https://jitpack.io' }
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build/intermediates/lint-cache/maven.google/com/android/support/constraint/group-index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build/intermediates/lint-cache/maven.google/com/android/support/group-index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build/intermediates/lint-cache/maven.google/com/android/support/test/espresso/group-index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build/intermediates/lint-cache/maven.google/com/android/support/test/group-index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build/intermediates/lint-cache/maven.google/master-index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/build/intermediates/lint-cache/sdk-registry.xml/sdk-registry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/GUIGet/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Tue Nov 08 20:01:02 CST 2022
8 | sdk.dir=C\:\\Users\\IcyFeather\\AppData\\Local\\Android\\Sdk
9 |
--------------------------------------------------------------------------------
/VisionDroid Code/GUIGet/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/VisionDroid Code/GptModel.py:
--------------------------------------------------------------------------------
1 | from openai import OpenAI
2 |
3 | import os
4 | os.environ['HTTP_PROXY'] = 'http://127.0.0.1:10809'
5 | os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:10809'
6 |
7 |
8 | class GptModel:
9 | client = OpenAI(
10 | api_key="xxxxxx",
11 | )
12 |
13 | def chat(self, question: str):
14 | chat_completion = self.client.chat.completions.create(
15 | messages=[
16 | {
17 | "role": "user",
18 | "content": question,
19 | }
20 | ],
21 | model="gpt-xxx",
22 | )
23 |
24 | return chat_completion.choices[0].message.content
25 |
--------------------------------------------------------------------------------
/VisionDroid Code/README.md:
--------------------------------------------------------------------------------
1 |
2 | # VisionDroid Source code
3 |
4 | ## How to use
5 | 1. Generate Your API Key: Before we start working with the GPT-4 API, we need to login into OpenAI account and generate our API keys.
6 | `openai.api_key = "XXXXXXX"`
7 | 2. Installing the library: To work with the GPT-4 API, first, we have to install the openai library by running the following command.
8 | 3. Using “ChatCompletion” gpt-4-vision-preview, which is the same model used by GPT-4.
9 |
10 | `from openai import OpenAI`
11 |
12 | `client = OpenAI()`
13 |
14 | `response = client.chat.completions.create(`
15 |
16 | `model="gpt-4-vision-preview",`
17 |
18 | `messages=[`
19 |
20 | `{`
21 |
22 | `"role": "user",`
23 |
24 | `"content": [`
25 |
26 | `{"type": "text", "text": "What’s in this image?"},`
27 |
28 | `{
29 |
30 | ` "type": "image_url",`
31 |
32 | `"image_url": {`
33 |
34 | ` "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",'
35 |
36 | `},`
37 |
38 | `},`
39 |
40 | `],`
41 |
42 | `}`
43 |
44 | `],`
45 |
46 | `max_tokens=300,`
47 |
48 | `)`
49 |
50 |
51 | ### Requirements
52 | * Android emulator
53 | * Ubuntu or Windows
54 | * Appium Desktop Client: [link](https://github.com/appium/appium-desktop/releases/tag/v1.22.3-4)
55 | * Python 3.7
56 | * apkutils==0.10.2
57 | * Appium-Python-Client==1.3.0
58 | * Levenshtein==0.18.1
59 | * lxml==4.8.0
60 | * opencv-python==4.5.5.64
61 | * sentence-transformers==1.0.3
62 | * torch==1.6.0
63 | * torchvision==0.7.0
64 |
65 | Use the gpt-4.
66 |
67 |
68 | ## structure
69 |
70 | 
71 |
72 | ## This example introduces how to use ChatGPT
73 |
74 | [a-simple-guide-to-gpt-4-api-with-python](https://platform.openai.com/docs/guides/vision)
75 |
76 |
77 | ## LLM Test
78 |
79 | USE GPT-4 App Testing
80 |
81 | 
82 |
83 | ### usage
84 |
85 | Find app, change `cmd.py` `PACKAGE = "xxx"` as package name
86 |
87 | Package name in `adb shell dumpsys activity activities | grep mControlTarget=Window`
88 |
89 | Opensource app, find `AndroidManifest.xml` change `/main/app/src/main/AndroidManifest.xml`
90 |
91 |
92 | ### Http
93 |
94 | `GptModel.py`
95 |
96 | ```
97 | import os
98 | os.environ['HTTP_PROXY'] = 'http://127.0.0.1:10809'
99 | os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:10809'
100 | ```
101 |
102 |
103 | ### How to set up a proxy for OpenAI's API in Python?
104 |
105 | https://github.com/Onelinerhub/onelinerhub/blob/main//python-openai/how-to-set-up-a-proxy-for-openai-s-api-in-python.md
106 |
--------------------------------------------------------------------------------
/VisionDroid Code/Scene_1/annotated_image/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/Scene_1/annotated_image/example.png
--------------------------------------------------------------------------------
/VisionDroid Code/SparkApi.py:
--------------------------------------------------------------------------------
1 | import _thread as thread
2 | import base64
3 | import datetime
4 | import hashlib
5 | import hmac
6 | import json
7 | from urllib.parse import urlparse
8 | import ssl
9 | from datetime import datetime
10 | from time import mktime
11 | from urllib.parse import urlencode
12 | from wsgiref.handlers import format_date_time
13 |
14 | import websocket
15 |
16 | answer = ""
17 |
18 |
19 | class Ws_Param(object):
20 | def __init__(self, APPID, APIKey, APISecret, Spark_url):
21 | self.APPID = APPID
22 | self.APIKey = APIKey
23 | self.APISecret = APISecret
24 | self.host = urlparse(Spark_url).netloc
25 | self.path = urlparse(Spark_url).path
26 | self.Spark_url = Spark_url
27 |
28 | def create_url(self):
29 | now = datetime.now()
30 | date = format_date_time(mktime(now.timetuple()))
31 |
32 | signature_origin = "host: " + self.host + "\n"
33 | signature_origin += "date: " + date + "\n"
34 | signature_origin += "GET " + self.path + " HTTP/1.1"
35 |
36 | signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
37 | digestmod=hashlib.sha256).digest()
38 |
39 | signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')
40 |
41 | authorization_origin = f'api_key="{self.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha_base64}"'
42 |
43 | authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
44 |
45 | v = {
46 | "authorization": authorization,
47 | "date": date,
48 | "host": self.host
49 | }
50 | url = self.Spark_url + '?' + urlencode(v)
51 | return url
52 |
53 |
54 | def on_error(ws, error):
55 | print("### error:", error)
56 |
57 |
58 | def on_close(ws, one, two):
59 | print(" ")
60 |
61 |
62 | def on_open(ws):
63 | thread.start_new_thread(run, (ws,))
64 |
65 |
66 | def run(ws, *args):
67 | data = json.dumps(gen_params(appid=ws.appid, domain=ws.domain, question=ws.question))
68 | ws.send(data)
69 |
70 |
71 | def on_message(ws, message):
72 | # print(message)
73 | data = json.loads(message)
74 | code = data['header']['code']
75 | if code != 0:
76 | print(f'error: {code}, {data}')
77 | ws.close()
78 | else:
79 | choices = data["payload"]["choices"]
80 | status = choices["status"]
81 | content = choices["text"][0]["content"]
82 | print(content, end="")
83 | global answer
84 | answer += content
85 | # print(1)
86 | if status == 2:
87 | ws.close()
88 |
89 |
90 | def gen_params(appid, domain, question):
91 | """
92 | appid
93 | """
94 | data = {
95 | "header": {
96 | "app_id": appid,
97 | "uid": "1234"
98 | },
99 | "parameter": {
100 | "chat": {
101 | "domain": domain,
102 | "temperature": 0.5,
103 | "max_tokens": 2048
104 | }
105 | },
106 | "payload": {
107 | "message": {
108 | "text": question
109 | }
110 | }
111 | }
112 | return data
113 |
114 |
115 | def main(appid, api_key, api_secret, Spark_url, domain, question):
116 | wsParam = Ws_Param(appid, api_key, api_secret, Spark_url)
117 | websocket.enableTrace(False)
118 | wsUrl = wsParam.create_url()
119 | ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open)
120 | ws.appid = appid
121 | ws.question = question
122 | ws.domain = domain
123 | ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
124 |
--------------------------------------------------------------------------------
/VisionDroid Code/SparkModel.py:
--------------------------------------------------------------------------------
1 | import SparkApi
2 |
3 | appid = "XXXX"
4 | api_secret = "XXXXX"
5 | api_key = "XXXXX"
6 |
7 | domain = "XXX"
8 | Spark_url = "XXXXX"
9 |
10 |
11 | class Spark:
12 | def __init__(self):
13 | self.text = []
14 |
15 | def _add_to_text(self, role, content):
16 | json_con = {"role": role, "content": content}
17 | self.text.append(json_con)
18 | return self.text
19 |
20 | @staticmethod
21 | def _get_length(text):
22 | length = 0
23 | for content in text:
24 | temp = content["content"]
25 | temp_len = len(temp)
26 | length += temp_len
27 | return length
28 |
29 | @staticmethod
30 | def _check_len(text):
31 | while Spark._get_length(text) > 8000:
32 | del text[0]
33 | return text
34 |
35 | def chat(self, in_text):
36 | question = Spark._check_len(self._add_to_text("user", in_text))
37 | SparkApi.answer = ""
38 | SparkApi.main(appid, api_key, api_secret, Spark_url, domain, question)
39 | self._add_to_text("assistant", SparkApi.answer)
40 | return SparkApi.answer
41 |
42 |
43 | if __name__ == '__main__':
44 | s = Spark()
45 | while True:
46 | in_text = input("\n" + "XXX:")
47 | print("XXX:", end="")
48 | out = s.chat(in_text)
49 |
--------------------------------------------------------------------------------
/VisionDroid Code/action.py:
--------------------------------------------------------------------------------
1 | from info import Widget
2 | import cmd
3 |
4 |
5 | def click(widget: Widget):
6 | cmd.click_node(widget.node)
7 |
8 |
9 | def back():
10 | cmd.execute_adb_cmd("input keyevent 4")
11 |
12 |
13 |
--------------------------------------------------------------------------------
/VisionDroid Code/assets/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/assets/workflow.png
--------------------------------------------------------------------------------
/VisionDroid Code/cmd.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import re
4 | import subprocess
5 |
6 | import cv2
7 | import uiautomator2 as u2
8 | import xmltodict
9 |
10 | PACKAGE = "xxxx"
11 |
12 |
13 | def layout_to_json():
14 | d = u2.connect()
15 | page_source = d.dump_hierarchy(compressed=True, pretty=True)
16 | data_dict = xmltodict.parse(page_source)
17 | return data_dict
18 |
19 |
20 | def _get_desc(node):
21 | text = node['@text']
22 | content = node['@content-desc']
23 | resource_id = node['@resource-id']
24 | if text != "":
25 | desc = text
26 | elif content != "":
27 | desc = content
28 | elif resource_id != "":
29 | desc = resource_id.split('/')[-1]
30 | desc = desc.replace('_', ' ')
31 | else:
32 | desc = ""
33 | return desc
34 |
35 |
36 | def _node_check(node, is_farther_clickable):
37 | # package
38 | if node['@package'] != PACKAGE:
39 | return False, False
40 |
41 | # click
42 | is_clickable = node['@clickable'] == "true" or is_farther_clickable
43 | if is_clickable:
44 | node['@clickable'] = "true"
45 | else:
46 | node['@clickable'] = "false"
47 |
48 | # fill
49 | is_editable = ('@class' in node) and (node['@class'] == 'android.widget.EditText' or
50 | node['@class'] == 'android.widget.AutoCompleteTextView')
51 | if is_editable:
52 | node['@editable'] = "true"
53 | else:
54 | node['@editable'] = "false"
55 |
56 | # desc
57 | node["@desc"] = _get_desc(node)
58 | is_described = node["@desc"] != ""
59 | if not is_described:
60 | # desc
61 | return is_clickable, False
62 |
63 | return is_clickable, is_described
64 |
65 |
66 | def get_nodes(hierarchy):
67 | info_nodes = []
68 |
69 | def dfs(node, is_farther_clickable=False):
70 | if 'node' not in node:
71 | return
72 | node = node['node']
73 | if type(node) is dict:
74 | is_clickable, is_described = _node_check(node, is_farther_clickable)
75 | if is_described:
76 | info_nodes.append(node)
77 | dfs(node, is_farther_clickable or is_clickable)
78 | else:
79 | for idx in range(len(node)):
80 | is_clickable, is_described = _node_check(node[idx], is_farther_clickable)
81 | if is_described:
82 | info_nodes.append(node[idx])
83 | dfs(node[idx], is_farther_clickable or is_clickable)
84 |
85 | dfs(hierarchy['hierarchy'])
86 | return info_nodes
87 |
88 |
89 | def get_key_nodes():
90 | hierarchy = layout_to_json()
91 | # with open("dump.test.json", "r") as f:
92 | # hierarchy = json.load(f)
93 | return get_nodes(hierarchy)
94 |
95 |
96 | def get_bounds(bounds):
97 | a, b, c, d = [int(i) for i in re.findall(r"(\d+)", bounds)]
98 | return a, b, c, d
99 |
100 |
101 | def click_node(target):
102 | pos_x_start, pos_y_start, pos_x_end, pos_y_end = get_bounds(target['@bounds'])
103 | cmd = "adb shell input tap {} {}".format(str((pos_x_start + pos_x_end) // 2), str((pos_y_start + pos_y_end) // 2))
104 | os.system(cmd)
105 |
106 |
107 | def execute_adb_cmd(cmd):
108 | return subprocess.getoutput("D:\\Android\\android-sdk\\platform-tools\\adb.exe shell " + cmd)
109 |
110 |
111 | if __name__ == '__main__':
112 | k = get_key_nodes()
113 | pass
114 |
--------------------------------------------------------------------------------
/VisionDroid Code/detector.py:
--------------------------------------------------------------------------------
1 | import requests, time
2 | import base64, os
3 | import fire
4 |
5 | def getPrompt():
6 | prompt = ("The common categories of inter-page no-crash bugs include: data operation failure, media control not responding, numerical calculation errors, setting and configuration failures, navigation and linking errors. We provide the example of inter-page bug descriptions: (1) The personal income addition function has failed to add, and the a bug path is as follows: \n"
7 | + "1) Please be a tester to test the [XXX] App. It has the following activities, including [XX, XXX]. The operable components on the image have been marked with red boxes. \n"
8 | + "2) The image shows a test sequence arranged in order from left to right and from top to bottom. Each screenshot is labeled with a different colored bounding box indicating the action of the operation (red as click, blue as input,...). Below the screenshot is the corresponding function name. I have numbered each operable component in order from top to bottom and left to right. The number corresponding to the first component of each row is marked in the upper left corner of the row, and the other numbers in the row can be deduced in sequence.\n"
9 | + "Output Example:\n"
10 | + "1) Does the current page meet expectations? No. If not, the component number that does not meet expectations: (xx). \nIs there a bug on the page? Yes If yes, the component number with the bug: xx.\n"
11 | + "2) Component number requiring next step operation: xx; Text of the component in the screenshot: xx; Operation status: Click; Current page: xxx; Operation expectation: After clicking xx, it is expected to enter the xx page.\n"
12 | + "3) Component number: xx; Text of the component in the screenshot: xx; Operation status: Input; Input content: xx; Current page: xxx; Operation expectation: After clicking xx, it is expected to enter the xx page.\n"
13 | + "Please analyze each step in the test sequence in the image based on the description and examples of the bugs, predict whether the page that transits after each step meets your expectation, use this to determine whether there are any bugs, and point out the bug page."
14 | )
15 |
16 | return prompt
17 |
18 |
19 | def get_response_from_lm(images):
20 | api_key = "xxxxx"
21 |
22 | def encode_image(image_path):
23 | with open(image_path, "rb") as image_file:
24 | return base64.b64encode(image_file.read()).decode('utf-8')
25 |
26 | prompt = getPrompt()
27 | msg = [{
28 | "type": "text",
29 | "text": prompt
30 | }]
31 | for image in images:
32 | image_path = image
33 | base64_image = encode_image(image_path)
34 | msg_cur = {
35 | "type": "image_url",
36 | "image_url": {
37 | "url": f"data:image/jpeg;base64,{base64_image}"
38 | }
39 | }
40 | msg.append(msg_cur)
41 |
42 | headers = {
43 | "Content-Type": "application/json",
44 | "Authorization": f"Bearer {api_key}"
45 | }
46 |
47 | payload = {
48 | "model": "gpt-4-xxxx",
49 | "messages": [
50 | {
51 | "role": "user",
52 | "content": msg
53 | }
54 | ],
55 | "max_tokens": 3000
56 | }
57 | response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
58 | outputs = response.json()['choices'][0]['message']['content'].strip()
59 | # print(response.json())
60 | if 'choices' not in response.json():
61 | print('Sleep!')
62 | time.sleep(15 * 60)
63 | # print('Model Output: ', outputs)
64 |
65 | return outputs
66 |
67 | def postprocess(content):
68 | import re
69 | c_list = content.split('\n\n')
70 | res_dict = dict()
71 | for item in c_list:
72 | item = item.strip()
73 | if item.startswith('1)'):
74 | if 'yes' in item.lower():
75 | res_dict['bug'] = -1
76 | else:
77 | numbers = re.findall(r'\d+', item)
78 | bug_num = numbers[-1]
79 | res_dict['bug'] = bug_num
80 | elif item.startswith('2)'):
81 | numbers = re.findall(r'\d+', item)
82 | action_num = numbers[-1]
83 | res_dict['action'] = action_num
84 | elif item.startswith('3)'):
85 | numbers = re.findall(r'\d+', item)
86 | action_num = numbers[-1]
87 | res_dict['next_page'] = action_num
88 |
89 | return res_dict
90 |
91 | def filter(actions, end_num, start_num=1):
92 | new_actions = []
93 | for item in actions:
94 | if item[1] >= start_num and item[1] <= end_num:
95 | new_actions.append(item)
96 |
97 | return new_actions
98 |
99 | def community(nodes, edges):
100 | import networkx as nx
101 | import community
102 | G = nx.Graph()
103 | sub_dict = dict()
104 |
105 | G.add_nodes_from([1, 2, 3, 4, 5])
106 | G.add_edges_from([(1, 2), (1, 3), (2, 3), (2, 4), (3, 4), (4, 5)])
107 | partition = community.best_partition(G)
108 | for k in partition.keys():
109 | v = partition[k]
110 | if v not in sub_dict.keys():
111 | sub_dict[v] = [k]
112 | else:
113 | sub_dict[v].append(k)
114 |
115 | return sub_dict
116 |
117 | # dir = 'Scene_1/annotated_image'
118 | # for image in os.listdir(dir):
119 |
120 | def run():
121 |
122 | dir_path = 'Scene_1/annotated_image'
123 |
124 | memory = []
125 | memory_action = []
126 | images = []
127 | num = 1
128 | for image in os.listdir(dir_path):
129 | image = os.path.join(dir_path, image)
130 | print('Image: ', image)
131 | memory.append(num)
132 | images.append(image)
133 | num += 1
134 |
135 | file_path = image
136 |
137 | output = ''
138 | times = 0
139 | while '\n' not in output and times < 5:
140 | output = get_response_from_lm([file_path])
141 | times += 1
142 | # print(output)
143 |
144 | res_dict = postprocess(output)
145 | if 'next_page' in res_dict.keys():
146 | next = int(res_dict['next_page'])
147 | memory_action.append((num, next))
148 |
149 | print(res_dict)
150 | print('Memory: ', memory)
151 | memory_action = filter(memory_action, num-1)
152 | print('Action: ', memory_action)
153 | sub_dict = community(memory, memory_action)
154 | for k in sub_dict.keys():
155 | sub_list = sub_dict[k]
156 | sub_img = []
157 | for num in sub_list:
158 | img_path = images[num-1]
159 | sub_img.append(img_path)
160 |
161 | output = ''
162 | times = 0
163 | while '\n' not in output and times < 5:
164 | output = get_response_from_lm(sub_img)
165 | times += 1
166 | # print(output)
167 |
168 | res_dict = postprocess(output)
169 | if 'next_page' in res_dict.keys():
170 | next = int(res_dict['next_page'])
171 | memory_action.append((num, next))
172 |
173 | print(res_dict)
174 |
175 |
176 | if __name__ == '__main__':
177 | fire.Fire(run)
178 |
--------------------------------------------------------------------------------
/VisionDroid Code/extract_activities.py:
--------------------------------------------------------------------------------
1 | """
2 | filepath: app code app/src/main/AndroidManifest.xml
3 | function: xml activities info
4 | """
5 | import xml.etree.ElementTree as ET
6 |
7 |
8 | def get_all_activities(file_path):
9 | tree = ET.parse(file_path)
10 | root = tree.getroot()
11 | app = root.find('application')
12 | activities = []
13 |
14 | for e in app.iter('activity'):
15 | if e.find('intent-filter') is not None:
16 | activities.append(e.attrib['{http://schemas.android.com/apk/res/android}name'].split('.')[-1].replace('Activity', ''))
17 | return activities
18 |
19 |
20 | if __name__ == '__main__':
21 | activities = get_all_activities("./AndroidManifest.xml")
22 | pass
23 |
--------------------------------------------------------------------------------
/VisionDroid Code/graph.py:
--------------------------------------------------------------------------------
1 | import os
2 | import cv2
3 | from cmd import execute_adb_cmd, get_bounds
4 | import uiautomator2 as u2
5 |
6 | screenshot_dir = 'screenshot'
7 | page_hierarchy_dir = 'page'
8 | os.makedirs(screenshot_dir, exist_ok=True)
9 |
10 |
11 | def clear_directories():
12 | directories = ['screenshot', 'page']
13 | files = ['path.txt']
14 |
15 | for directory in directories:
16 | for root, dirs, files in os.walk(directory):
17 | for file in files:
18 | file_path = os.path.join(root, file)
19 | os.remove(file_path)
20 |
21 | for file in files:
22 | if os.path.exists(file):
23 | os.remove(file)
24 |
25 | os.makedirs(screenshot_dir, exist_ok=True)
26 | os.makedirs(page_hierarchy_dir, exist_ok=True)
27 |
28 |
29 | def save_path(page_info, action_cnt, selected_widget=None):
30 | path_file = None
31 | try:
32 | path_file = open("path.txt", "a+", errors='ignore')
33 | path_file.write(f" {page_info.activity_name}({action_cnt}) ")
34 | if selected_widget is None:
35 | path_file.write(f" -> BACK\n")
36 | else:
37 | path_file.write(f" -> {selected_widget.widget_id}\n")
38 | finally:
39 | if path_file is not None:
40 | path_file.close()
41 |
42 |
43 | def save_screenshot(action_cnt, widget):
44 | path = screenshot_dir + "/" + action_cnt + ".jpg"
45 | execute_adb_cmd("/system/bin/screencap -p /sdcard/screenshot.png")
46 | cmd = f"adb pull /sdcard/screenshot.png {path}"
47 | os.system(cmd)
48 |
49 | # widget
50 | if widget is not None:
51 | image = cv2.imread(f"{path}")
52 | pos_x_start, pos_y_start, pos_x_end, pos_y_end = get_bounds(widget.node['@bounds'])
53 | cv2.rectangle(image, (pos_x_start, pos_y_start), (pos_x_end, pos_y_end), (0, 0, 255), 4)
54 | cv2.imwrite(f"{path}", image)
55 |
56 |
57 | def save_page_hierarchy(action_cnt):
58 | path = page_hierarchy_dir + "/" + action_cnt + ".xml"
59 | d = u2.connect()
60 | with open(path, 'w', encoding='utf-8') as hierarchy_file:
61 | page_source = d.dump_hierarchy(compressed=True, pretty=True)
62 | hierarchy_file.write(page_source)
63 |
--------------------------------------------------------------------------------
/VisionDroid Code/info.py:
--------------------------------------------------------------------------------
1 | from dataclasses import field, dataclass
2 | from typing import Dict, Set
3 |
4 | import cmd
5 | import re
6 |
7 | import extract_activities
8 |
9 | XML_PATH = "./AndroidManifest.xml"
10 | THRESHOLD = 0.8
11 |
12 |
13 | @dataclass
14 | class Global:
15 | app_name: str = field(
16 | default=None, metadata={"desc": "app name"}
17 | )
18 | activities: Set[str] = field(
19 | default_factory=set, metadata={"desc": "activities"}
20 | )
21 | priority: Dict = field(
22 | default_factory=dict, metadata={"desc": "priority"}
23 | )
24 |
25 |
26 | def get_global_info(opensource=False):
27 | global_info = Global()
28 | global_info.app_name = cmd.PACKAGE
29 | # Activity
30 | ret = cmd.execute_adb_cmd("dumpsys activity activities | grep mControlTarget=Window")
31 | results = [i for i in re.findall(r"/(.+?)}", ret)]
32 | for act in results:
33 | global_info.activities.add(act)
34 | # clear duplicated elements
35 | global_info.activities = list(set(global_info.activities))
36 | if opensource:
37 | # Activity
38 | global_info.activities = extract_activities.get_all_activities(XML_PATH)
39 | # priority
40 | for act in global_info.activities:
41 | global_info.priority[act] = "1"
42 | return global_info
43 |
44 |
45 | @dataclass
46 | class Page:
47 | activity_name: str = field(
48 | default=None,
49 | metadata={"desc": "activity"}
50 | )
51 | layouts: dict = field(
52 | default_factory=dict,
53 | metadata={"desc": "widget"}
54 | )
55 | visit_times: int = field(
56 | default=1,
57 | metadata={"desc": "visit"}
58 | )
59 | is_first_page: bool = field(
60 | default=False,
61 | metadata={"desc": "first page"}
62 | )
63 |
64 | def visit(self):
65 | self.visit_times += 1
66 |
67 | @staticmethod
68 | def sim_ratio(page1, page2):
69 | if page1.activity_name != page2.activity_name:
70 | return 0.0
71 | else:
72 | sim = 0.0
73 | times = 0
74 | for key in page1.layouts:
75 | times += 1
76 | widget1 = page1.layouts[key]
77 | if key not in page2.layouts:
78 | continue
79 | widget2 = page2.layouts[key]
80 | sim += Widget.sim_ratio(widget1, widget2)
81 | for key in page2.layouts:
82 | times += 1
83 | widget2 = page2.layouts[key]
84 | if key not in page1.layouts:
85 | continue
86 | widget1 = page1.layouts[key]
87 | sim += Widget.sim_ratio(widget1, widget2)
88 | if times == 0:
89 | return 0
90 | return sim / times
91 |
92 |
93 | all_pages = []
94 |
95 |
96 | def page_db_fetch(page_info):
97 | for page in all_pages:
98 | if Page.sim_ratio(page_info, page) > THRESHOLD:
99 | page.visit()
100 | page.layouts = page_info.layouts
101 | return page
102 | all_pages.append(page_info)
103 | return page_info
104 |
105 |
106 | def get_page_info(global_info, prev_page_info):
107 | if global_info is None:
108 | return None
109 | page_info = Page()
110 | ret = cmd.execute_adb_cmd("dumpsys activity activities | grep mControlTarget=Window")
111 | results = [i for i in re.findall(r"/(.+?)}", ret)]
112 | if len(results) == 0:
113 | page_info.activity_name = prev_page_info.activity_name
114 | else:
115 | page_info.activity_name = results[0]
116 | if prev_page_info is None:
117 | page_info.is_first_page = True
118 | info_nodes = cmd.get_key_nodes()
119 | all_desc = set()
120 | for i in info_nodes:
121 | if i["@desc"] in all_desc:
122 | continue
123 | all_desc.add(i["@desc"])
124 |
125 | widget_info = Widget()
126 | widget_info.widget_id = i["@desc"]
127 | widget_info.widget_type = i["@class"].split()[-1]
128 | if i["@clickable"] == "true":
129 | widget_info.widget_act = "click"
130 | elif i["@editable"] == "true":
131 | widget_info.widget_act = "edit"
132 | widget_info.node = i
133 | page_info.layouts[widget_info.widget_id] = widget_info
134 | return page_info
135 |
136 |
137 | @dataclass
138 | class Widget:
139 | widget_type: str = field(
140 | default=None,
141 | metadata={"desc": "parent"}
142 | )
143 | widget_id: str = field(
144 | default=None,
145 | metadata={"desc": "ID"}
146 | )
147 | widget_act: str = field(
148 | default=None,
149 | metadata={"desc": "action"}
150 | )
151 | node: Dict = field(
152 | default=None,
153 | metadata={"desc": "root"}
154 | )
155 |
156 | @staticmethod
157 | def sim_ratio(widget1, widget2):
158 | if widget1.widget_id != widget2.widget_id or widget1.widget_type != widget2.widget_type or widget1.widget_act != widget2.widget_act:
159 | return 0.0
160 | else:
161 | return 1.0
162 |
163 |
164 | if __name__ == '__main__':
165 | test = get_global_info()
166 | print(test)
--------------------------------------------------------------------------------
/VisionDroid Code/main.py:
--------------------------------------------------------------------------------
1 | from SparkModel import Spark
2 | from GptModel import GptModel
3 | from action import click, back
4 | from prompt import generate_prompt, generate_global_prompt
5 | import info
6 | import time
7 | from graph import save_screenshot, save_path, save_page_hierarchy, clear_directories
8 |
9 | # Use Spark
10 | # s = Spark()
11 |
12 | s = GptModel()
13 |
14 | if __name__ == '__main__':
15 | global_info = info.get_global_info(opensource=False)
16 | prompt_global = generate_global_prompt(global_info)
17 | status = s.chat(prompt_global)
18 | page_info = None
19 | action_cnt = 0
20 | clear_directories()
21 | while True:
22 | page_info = info.get_page_info(global_info, page_info)
23 | save_page_hierarchy(str(action_cnt + 1))
24 | prompt, opt_key_mapping = generate_prompt(info.page_db_fetch(page_info))
25 | print(prompt)
26 | output = s.chat(prompt)
27 | print("============")
28 | print(output)
29 | print("============")
30 | # Action: 1
31 | output = output.lower().replace("action:", "").strip()
32 | choice = int(output.split(".")[0]) - 1
33 |
34 | print(choice)
35 | print("============")
36 | if choice == len(opt_key_mapping):
37 | save_screenshot(str(action_cnt + 1), None)
38 | save_path(page_info, str(action_cnt))
39 | back()
40 | else:
41 | save_screenshot(str(action_cnt + 1), page_info.layouts[opt_key_mapping[choice]])
42 | save_path(page_info, str(action_cnt), page_info.layouts[opt_key_mapping[choice]])
43 | click(page_info.layouts[opt_key_mapping[choice]])
44 | time.sleep(2.0)
45 | action_cnt += 1
46 |
47 |
--------------------------------------------------------------------------------
/VisionDroid Code/process_image.py:
--------------------------------------------------------------------------------
1 | from xml.etree import ElementTree as ET
2 | import numpy as np
3 | from matplotlib import pyplot as plt
4 | import cv2
5 | import re
6 |
7 | # Function to recursively extract bounds of enabled elements
8 | def extract_enabled_bounds(node, f_click=False):
9 | bounds = []
10 | # Check if the node is enabled
11 | if node.attrib.get('clickable') == 'true':
12 | # Extract the 'bounds' attribute
13 | if len(node) == 1:
14 | bound = node[0].attrib.get('bounds')
15 | if bound:
16 | bounds.append(bound)
17 | else:
18 | bound = node.attrib.get('bounds')
19 | if bound:
20 | bounds.append(bound)
21 |
22 |
23 | # Recursively check for any child nodes
24 | for child in node:
25 | bounds.extend(extract_enabled_bounds(child, f_click))
26 |
27 | return bounds
28 |
29 | def sortBounds(bounds):
30 | sorted_data = sorted(bounds, key=lambda x: (x[1], x[0]))
31 |
32 | return sorted_data
33 |
34 | # Function to draw rectangles on the image based on the bounds
35 | def draw_bounds(image, bounds_list):
36 | num = 0
37 | last_y = -1
38 | for coords in bounds_list:
39 | num += 1
40 | # Extracting the coordinates from the bounds string
41 | # coords = list(map(int, re.findall(r'\d+', bound_str)))
42 | # Since coords are in the form [x1,y1,x2,y2], we unpack them
43 | x1, y1, x2, y2 = coords
44 | # Draw a rectangle on the image
45 | cv2.rectangle(image, (x1, y1), (x2, y2), (0, 0, 255), 2) # Blue color
46 |
47 | if y1 != last_y:
48 | font = cv2.FONT_HERSHEY_SIMPLEX
49 | font_scale = 1
50 | font_color = (255, 0, 0) # Green color
51 | cv2.putText(image, str(num), (x1, y1 + 30), font, font_scale, font_color, 2)
52 | last_y = y1
53 |
54 | return image
55 |
56 | def calculate_area(box):
57 | # bound
58 | return (box[2] - box[0]) * (box[3] - box[1])
59 |
60 | def is_fully_overlapped(box1, box2):
61 | # box1 and box2
62 | return ((box1[0] <= box2[0] and box1[1] <= box2[1] and box1[2] >= box2[2] and box1[3] >= box2[3]) or
63 | (box2[0] <= box1[0] and box2[1] <= box1[1] and box2[2] >= box1[2] and box2[3] >= box1[3]))
64 |
65 | def calculate_overlap_area(box1, box2):
66 | # overlap
67 | overlap_width = max(0, min(box1[2], box2[2]) - max(box1[0], box2[0]))
68 | overlap_height = max(0, min(box1[3], box2[3]) - max(box1[1], box2[1]))
69 | return overlap_width * overlap_height
70 |
71 | def is_large_overlap(box1, box2, threshold=0.3):
72 | # overlap
73 | overlap_area = calculate_overlap_area(box1, box2)
74 | area1 = calculate_area(box1)
75 | area2 = calculate_area(box2)
76 |
77 | # area
78 | return overlap_area > threshold * area1 or overlap_area > threshold * area2
79 |
80 | def remove_larger_overlap(boxes):
81 | i = 0
82 | while i < len(boxes):
83 | removed = False
84 | for j in range(i + 1, len(boxes)):
85 | if is_fully_overlapped(boxes[i], boxes[j]):
86 | # if is_overlap(boxes[i], boxes[j]):
87 | # overlap
88 | if calculate_area(boxes[i]) > calculate_area(boxes[j]):
89 | del boxes[i]
90 | else:
91 | del boxes[j]
92 | removed = True
93 | break # delete
94 | if not removed:
95 | i += 1 # move
96 | return boxes
97 |
98 | def shift_box(box, dx, dy):
99 | # bound
100 | return [box[0] + dx, box[1] + dy, box[2] + dx, box[3] + dy]
101 |
102 | def resolve_overlap(boxes):
103 | for i in range(len(boxes)):
104 | for j in range(len(boxes)):
105 | if i != j and is_large_overlap(boxes[i], boxes[j]) and is_fully_overlapped(boxes[i], boxes[j]) == False:
106 | # print('OOOKKKK')
107 | # print('box i', boxes[i])
108 | # print('box j', boxes[j])
109 | # bound
110 | # right
111 | if boxes[i][0] < boxes[j][0] or boxes[i][1] < boxes[j][1]:
112 | # boxes[j]
113 | shift_x = max(0, boxes[i][2] - boxes[j][0])
114 | shift_y = max(0, boxes[i][3] - boxes[j][1])
115 | # boxes[j] = shift_box(boxes[j], shift_x, shift_y)
116 | boxes[j] = shift_box(boxes[j], 0, shift_y)
117 | else:
118 | # boxes[i]
119 | shift_x = max(0, boxes[j][2] - boxes[i][0])
120 | shift_y = max(0, boxes[j][3] - boxes[i][1])
121 | # boxes[i] = shift_box(boxes[i], shift_x, shift_y)
122 | boxes[i] = shift_box(boxes[i], 0, shift_y)
123 | # print('box i', boxes[i])
124 | # print('box j', boxes[j])
125 | return boxes
126 |
127 | for i in range(6):
128 | # Parse the XML content
129 | print('Num: ', i + 1)
130 | tree = ET.parse(f"Scene_1/hierarchy_files/{i+1}.xml")
131 | root = tree.getroot()
132 |
133 | # Extracting bounds from the root node
134 | enabled_bounds = extract_enabled_bounds(root)
135 | bounds = []
136 | for bound_str in enabled_bounds:
137 | coords = list(map(int, re.findall(r'\d+', bound_str)))
138 | # Since coords are in the form [x1,y1,x2,y2], we unpack them
139 | # x1, y1, x2, y2 = coords
140 | bounds.append(coords)
141 | print(len(bounds))
142 | # print(bounds) # Displaying the first 10 for brevity
143 |
144 | # enabled_bounds = remove_larger_overlap(bounds)
145 | enabled_bounds = bounds
146 | enabled_bounds = sortBounds(enabled_bounds)
147 | print(enabled_bounds)
148 |
149 | t = len(enabled_bounds)
150 | for m in range(t):
151 | # print('m: ', m)
152 | if enabled_bounds[0][0] == 0 and enabled_bounds[0][1] == 0:
153 | enabled_bounds = enabled_bounds[1:]
154 | else:
155 | break
156 | print(enabled_bounds)
157 |
158 | enabled_bounds = resolve_overlap(enabled_bounds)
159 |
160 | # print(enabled_bounds)
161 |
162 | # Load the image
163 | image_path = f'Scene_1/screenshots/{i+1}.jpg'
164 | image = cv2.imread(image_path)
165 |
166 | # Drawing the bounds on the image
167 | image_with_bounds = draw_bounds(image, enabled_bounds)
168 |
169 | # Save the image with drawn bounds
170 | output_path = f'Scene_1/annotated_image/{i+1}.jpg'
171 | cv2.imwrite(output_path, image_with_bounds)
172 |
173 | print('ok!')
174 |
--------------------------------------------------------------------------------
/VisionDroid Code/prompt.py:
--------------------------------------------------------------------------------
1 | from info import Global, Page
2 |
3 | few_shot = """
4 | Now that you are an automated testing program for Android software,\
5 | what you have to do is test the functionality of the software as completely as possible and check for any problems.\
6 | I will tell you the information of the current program interface.\
7 | by asking questions, and you will tell me the next step of the test by answering.\
8 | When you encounter components with similar names,\
9 | you can look at them as the same category and test one or more of them.\
10 | When you encounter many operation options, you tend to click on them from smallest to largest,\
11 | and tend to click on the component with "menu button" in its name.\
12 | Now that you are an automated testing program for Android software,\
13 | what you have to do is test the functionality of the software as completely as possible and check for any problems.\
14 | I will tell you the information of the current program interface by asking questions,\
15 | and you will tell me the next step of the test by answering.
16 | """
17 |
18 |
19 | def generate_global_prompt(global_info: Global):
20 | prompt = few_shot
21 | prompt += "We want to test the {} App, which has {} main function pages, namely: ".format(global_info.app_name,
22 | len(global_info.activities))
23 |
24 | # not open source, so there is no activity priority sequence
25 |
26 | # for activity in global_info.activities:
27 | # prompt += '"{}", '.format(activity)
28 | # prompt = prompt[:-2]
29 | # prompt += '.\nThe recommended test sequence is: '
30 |
31 | for priority in global_info.priority:
32 | prompt += '"{}", '.format(priority)
33 |
34 | prompt += "If you're clear, please answer \"OK\"."
35 | return prompt
36 |
37 |
38 | def generate_prompt(page_info: Page):
39 | prompt = ""
40 | prompt += 'The function UI page we are currently testing is "{}".\n'.format(page_info.activity_name)
41 | prompt += 'The number of exploration recorded on the current page is {}.\n'.format(page_info.visit_times)
42 |
43 | prompt += "Do not select the same choice as before. Now we can do these:\n"
44 |
45 | opt_key_mapping = []
46 | opt_id = 1
47 | for widget_key in page_info.layouts.keys():
48 | if page_info.layouts[widget_key].widget_act != 'click':
49 | continue
50 | prompt += '{}. click "{}"\n'.format(str(opt_id), widget_key)
51 | opt_key_mapping.append(widget_key)
52 | opt_id += 1
53 | if not page_info.is_first_page:
54 | prompt += "{}. return to previous page\n".format(str(opt_id))
55 | prompt += "Give a number of a choice above only, such as \"1. action\".\n"
56 | return prompt, opt_key_mapping
57 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/__pycache__/pywin32_postinstall.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/__pycache__/pywin32_postinstall.cpython-39.pyc
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/__pycache__/pywin32_testall.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/__pycache__/pywin32_testall.cpython-39.pyc
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/__pycache__/readelf.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/__pycache__/readelf.cpython-39.pyc
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/activate:
--------------------------------------------------------------------------------
1 | # This file must be used with "source bin/activate" *from bash*
2 | # you cannot run it directly
3 |
4 |
5 | if [ "${BASH_SOURCE-}" = "$0" ]; then
6 | echo "You must source this script: \$ source $0" >&2
7 | exit 33
8 | fi
9 |
10 | deactivate () {
11 | unset -f pydoc >/dev/null 2>&1 || true
12 |
13 | # reset old environment variables
14 | # ! [ -z ${VAR+_} ] returns true if VAR is declared at all
15 | if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then
16 | PATH="$_OLD_VIRTUAL_PATH"
17 | export PATH
18 | unset _OLD_VIRTUAL_PATH
19 | fi
20 | if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
21 | PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
22 | export PYTHONHOME
23 | unset _OLD_VIRTUAL_PYTHONHOME
24 | fi
25 |
26 | # The hash command must be called to get it to forget past
27 | # commands. Without forgetting past commands the $PATH changes
28 | # we made may not be respected
29 | hash -r 2>/dev/null
30 |
31 | if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then
32 | PS1="$_OLD_VIRTUAL_PS1"
33 | export PS1
34 | unset _OLD_VIRTUAL_PS1
35 | fi
36 |
37 | unset VIRTUAL_ENV
38 | if [ ! "${1-}" = "nondestructive" ] ; then
39 | # Self destruct!
40 | unset -f deactivate
41 | fi
42 | }
43 |
44 | # unset irrelevant variables
45 | deactivate nondestructive
46 |
47 | VIRTUAL_ENV='D:\Projects\hintdroid-v1\venv'
48 | if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then
49 | VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV")
50 | fi
51 | export VIRTUAL_ENV
52 |
53 | _OLD_VIRTUAL_PATH="$PATH"
54 | PATH="$VIRTUAL_ENV/Scripts:$PATH"
55 | export PATH
56 |
57 | # unset PYTHONHOME if set
58 | if ! [ -z "${PYTHONHOME+_}" ] ; then
59 | _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
60 | unset PYTHONHOME
61 | fi
62 |
63 | if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
64 | _OLD_VIRTUAL_PS1="${PS1-}"
65 | if [ "x" != x ] ; then
66 | PS1="() ${PS1-}"
67 | else
68 | PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}"
69 | fi
70 | export PS1
71 | fi
72 |
73 | # Make sure to unalias pydoc if it's already there
74 | alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true
75 |
76 | pydoc () {
77 | python -m pydoc "$@"
78 | }
79 |
80 | # The hash command must be called to get it to forget past
81 | # commands. Without forgetting past commands the $PATH changes
82 | # we made may not be respected
83 | hash -r 2>/dev/null
84 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/activate.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set "VIRTUAL_ENV=D:\Projects\hintdroid-v1\venv"
4 |
5 | if defined _OLD_VIRTUAL_PROMPT (
6 | set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
7 | ) else (
8 | if not defined PROMPT (
9 | set "PROMPT=$P$G"
10 | )
11 | if not defined VIRTUAL_ENV_DISABLE_PROMPT (
12 | set "_OLD_VIRTUAL_PROMPT=%PROMPT%"
13 | )
14 | )
15 | if not defined VIRTUAL_ENV_DISABLE_PROMPT (
16 | if "" NEQ "" (
17 | set "PROMPT=() %PROMPT%"
18 | ) else (
19 | for %%d in ("%VIRTUAL_ENV%") do set "PROMPT=(%%~nxd) %PROMPT%"
20 | )
21 | )
22 |
23 | REM Don't use () to avoid problems with them in %PATH%
24 | if defined _OLD_VIRTUAL_PYTHONHOME goto ENDIFVHOME
25 | set "_OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%"
26 | :ENDIFVHOME
27 |
28 | set PYTHONHOME=
29 |
30 | REM if defined _OLD_VIRTUAL_PATH (
31 | if not defined _OLD_VIRTUAL_PATH goto ENDIFVPATH1
32 | set "PATH=%_OLD_VIRTUAL_PATH%"
33 | :ENDIFVPATH1
34 | REM ) else (
35 | if defined _OLD_VIRTUAL_PATH goto ENDIFVPATH2
36 | set "_OLD_VIRTUAL_PATH=%PATH%"
37 | :ENDIFVPATH2
38 |
39 | set "PATH=%VIRTUAL_ENV%\Scripts;%PATH%"
40 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/activate.fish:
--------------------------------------------------------------------------------
1 | # This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*.
2 | # Do not run it directly.
3 |
4 | function _bashify_path -d "Converts a fish path to something bash can recognize"
5 | set fishy_path $argv
6 | set bashy_path $fishy_path[1]
7 | for path_part in $fishy_path[2..-1]
8 | set bashy_path "$bashy_path:$path_part"
9 | end
10 | echo $bashy_path
11 | end
12 |
13 | function _fishify_path -d "Converts a bash path to something fish can recognize"
14 | echo $argv | tr ':' '\n'
15 | end
16 |
17 | function deactivate -d 'Exit virtualenv mode and return to the normal environment.'
18 | # reset old environment variables
19 | if test -n "$_OLD_VIRTUAL_PATH"
20 | # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
21 | if test (echo $FISH_VERSION | head -c 1) -lt 3
22 | set -gx PATH (_fishify_path "$_OLD_VIRTUAL_PATH")
23 | else
24 | set -gx PATH $_OLD_VIRTUAL_PATH
25 | end
26 | set -e _OLD_VIRTUAL_PATH
27 | end
28 |
29 | if test -n "$_OLD_VIRTUAL_PYTHONHOME"
30 | set -gx PYTHONHOME "$_OLD_VIRTUAL_PYTHONHOME"
31 | set -e _OLD_VIRTUAL_PYTHONHOME
32 | end
33 |
34 | if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
35 | and functions -q _old_fish_prompt
36 | # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`.
37 | set -l fish_function_path
38 |
39 | # Erase virtualenv's `fish_prompt` and restore the original.
40 | functions -e fish_prompt
41 | functions -c _old_fish_prompt fish_prompt
42 | functions -e _old_fish_prompt
43 | set -e _OLD_FISH_PROMPT_OVERRIDE
44 | end
45 |
46 | set -e VIRTUAL_ENV
47 |
48 | if test "$argv[1]" != 'nondestructive'
49 | # Self-destruct!
50 | functions -e pydoc
51 | functions -e deactivate
52 | functions -e _bashify_path
53 | functions -e _fishify_path
54 | end
55 | end
56 |
57 | # Unset irrelevant variables.
58 | deactivate nondestructive
59 |
60 | set -gx VIRTUAL_ENV 'D:\Projects\hintdroid-v1\venv'
61 |
62 | # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
63 | if test (echo $FISH_VERSION | head -c 1) -lt 3
64 | set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH)
65 | else
66 | set -gx _OLD_VIRTUAL_PATH $PATH
67 | end
68 | set -gx PATH "$VIRTUAL_ENV"'/Scripts' $PATH
69 |
70 | # Unset `$PYTHONHOME` if set.
71 | if set -q PYTHONHOME
72 | set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
73 | set -e PYTHONHOME
74 | end
75 |
76 | function pydoc
77 | python -m pydoc $argv
78 | end
79 |
80 | if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
81 | # Copy the current `fish_prompt` function as `_old_fish_prompt`.
82 | functions -c fish_prompt _old_fish_prompt
83 |
84 | function fish_prompt
85 | # Run the user's prompt first; it might depend on (pipe)status.
86 | set -l prompt (_old_fish_prompt)
87 |
88 | # Prompt override provided?
89 | # If not, just prepend the environment name.
90 | if test -n ''
91 | printf '(%s) ' ''
92 | else
93 | printf '(%s) ' (basename "$VIRTUAL_ENV")
94 | end
95 |
96 | string join -- \n $prompt # handle multi-line prompts
97 | end
98 |
99 | set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
100 | end
101 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/activate.nu:
--------------------------------------------------------------------------------
1 | # Setting all environment variables for the venv
2 | let path-name = (if ((sys).host.name == "Windows") { "Path" } { "PATH" })
3 | let virtual-env = "D:\Projects\hintdroid-v1\venv"
4 | let bin = "Scripts"
5 | let path-sep = ";"
6 |
7 | let old-path = ($nu.path | str collect ($path-sep))
8 |
9 | let venv-path = ([$virtual-env $bin] | path join)
10 | let new-path = ($nu.path | prepend $venv-path | str collect ($path-sep))
11 |
12 | # environment variables that will be batched loaded to the virtual env
13 | let new-env = ([
14 | [name, value];
15 | [$path-name $new-path]
16 | [_OLD_VIRTUAL_PATH $old-path]
17 | [VIRTUAL_ENV $virtual-env]
18 | ])
19 |
20 | load-env $new-env
21 |
22 | # Creating the new prompt for the session
23 | let virtual_prompt = (if ("" != "") {
24 | "() "
25 | } {
26 | (build-string '(' ($virtual-env | path basename) ') ')
27 | }
28 | )
29 |
30 | # If there is no default prompt, then only the env is printed in the prompt
31 | let new_prompt = (if ( config | select prompt | empty? ) {
32 | ($"build-string '($virtual_prompt)'")
33 | } {
34 | ($"build-string '($virtual_prompt)' (config get prompt | str find-replace "build-string" "")")
35 | })
36 | let-env PROMPT_COMMAND = $new_prompt
37 |
38 | # We are using alias as the function definitions because only aliases can be
39 | # removed from the scope
40 | alias pydoc = python -m pydoc
41 | alias deactivate = source "D:\Projects\hintdroid-v1\venv\Scripts\deactivate.nu"
42 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/activate.ps1:
--------------------------------------------------------------------------------
1 | $script:THIS_PATH = $myinvocation.mycommand.path
2 | $script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent
3 |
4 | function global:deactivate([switch] $NonDestructive) {
5 | if (Test-Path variable:_OLD_VIRTUAL_PATH) {
6 | $env:PATH = $variable:_OLD_VIRTUAL_PATH
7 | Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global
8 | }
9 |
10 | if (Test-Path function:_old_virtual_prompt) {
11 | $function:prompt = $function:_old_virtual_prompt
12 | Remove-Item function:\_old_virtual_prompt
13 | }
14 |
15 | if ($env:VIRTUAL_ENV) {
16 | Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue
17 | }
18 |
19 | if (!$NonDestructive) {
20 | # Self destruct!
21 | Remove-Item function:deactivate
22 | Remove-Item function:pydoc
23 | }
24 | }
25 |
26 | function global:pydoc {
27 | python -m pydoc $args
28 | }
29 |
30 | # unset irrelevant variables
31 | deactivate -nondestructive
32 |
33 | $VIRTUAL_ENV = $BASE_DIR
34 | $env:VIRTUAL_ENV = $VIRTUAL_ENV
35 |
36 | New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH
37 |
38 | $env:PATH = "$env:VIRTUAL_ENV/Scripts;" + $env:PATH
39 | if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
40 | function global:_old_virtual_prompt {
41 | ""
42 | }
43 | $function:_old_virtual_prompt = $function:prompt
44 |
45 | if ("" -ne "") {
46 | function global:prompt {
47 | # Add the custom prefix to the existing prompt
48 | $previous_prompt_value = & $function:_old_virtual_prompt
49 | ("() " + $previous_prompt_value)
50 | }
51 | }
52 | else {
53 | function global:prompt {
54 | # Add a prefix to the current prompt, but don't discard it.
55 | $previous_prompt_value = & $function:_old_virtual_prompt
56 | $new_prompt_value = "($( Split-Path $env:VIRTUAL_ENV -Leaf )) "
57 | ($new_prompt_value + $previous_prompt_value)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/activate_this.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Activate virtualenv for current interpreter:
3 |
4 | Use exec(open(this_file).read(), {'__file__': this_file}).
5 |
6 | This can be used when you must use an existing Python interpreter, not the virtualenv bin/python.
7 | """
8 | import os
9 | import site
10 | import sys
11 |
12 | try:
13 | abs_file = os.path.abspath(__file__)
14 | except NameError:
15 | raise AssertionError("You must use exec(open(this_file).read(), {'__file__': this_file}))")
16 |
17 | bin_dir = os.path.dirname(abs_file)
18 | base = bin_dir[: -len("Scripts") - 1] # strip away the bin part from the __file__, plus the path separator
19 |
20 | # prepend bin to PATH (this file is inside the bin directory)
21 | os.environ["PATH"] = os.pathsep.join([bin_dir] + os.environ.get("PATH", "").split(os.pathsep))
22 | os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory
23 |
24 | # add the virtual environments libraries to the host python import mechanism
25 | prev_length = len(sys.path)
26 | for lib in "..\Lib\site-packages".split(os.pathsep):
27 | path = os.path.realpath(os.path.join(bin_dir, lib))
28 | site.addsitedir(path.decode("utf-8") if "" else path)
29 | sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]
30 |
31 | sys.real_prefix = sys.prefix
32 | sys.prefix = base
33 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/deactivate.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set VIRTUAL_ENV=
4 |
5 | REM Don't use () to avoid problems with them in %PATH%
6 | if not defined _OLD_VIRTUAL_PROMPT goto ENDIFVPROMPT
7 | set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
8 | set _OLD_VIRTUAL_PROMPT=
9 | :ENDIFVPROMPT
10 |
11 | if not defined _OLD_VIRTUAL_PYTHONHOME goto ENDIFVHOME
12 | set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
13 | set _OLD_VIRTUAL_PYTHONHOME=
14 | :ENDIFVHOME
15 |
16 | if not defined _OLD_VIRTUAL_PATH goto ENDIFVPATH
17 | set "PATH=%_OLD_VIRTUAL_PATH%"
18 | set _OLD_VIRTUAL_PATH=
19 | :ENDIFVPATH
20 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/deactivate.nu:
--------------------------------------------------------------------------------
1 | # Setting the old path
2 | let path-name = (if ((sys).host.name == "Windows") { "Path" } { "PATH" })
3 | let-env $path-name = $nu.env._OLD_VIRTUAL_PATH
4 |
5 | # Unleting the environment variables that were created when activating the env
6 | unlet-env VIRTUAL_ENV
7 | unlet-env _OLD_VIRTUAL_PATH
8 | unlet-env PROMPT_COMMAND
9 |
10 | unalias pydoc
11 | unalias deactivate
12 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/f2py.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/f2py.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/normalizer.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/normalizer.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/openai.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/openai.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pip-3.9.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/pip-3.9.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pip.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/pip.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pip3.9.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/pip3.9.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pip3.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/pip3.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pydoc.bat:
--------------------------------------------------------------------------------
1 | python.exe -m pydoc %*
2 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/python.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/python.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pythonw.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/pythonw.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pywin32_postinstall.py:
--------------------------------------------------------------------------------
1 | # postinstall script for pywin32
2 | #
3 | # copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory,
4 | # and creates a pth file
5 | import os
6 | import sys
7 | import glob
8 | import shutil
9 | import sysconfig
10 |
11 | try:
12 | import winreg as winreg
13 | except:
14 | import winreg
15 |
16 | # Send output somewhere so it can be found if necessary...
17 | import tempfile
18 |
19 | tee_f = open(os.path.join(tempfile.gettempdir(), "pywin32_postinstall.log"), "w")
20 |
21 |
22 | class Tee:
23 | def __init__(self, file):
24 | self.f = file
25 |
26 | def write(self, what):
27 | if self.f is not None:
28 | try:
29 | self.f.write(what.replace("\n", "\r\n"))
30 | except IOError:
31 | pass
32 | tee_f.write(what)
33 |
34 | def flush(self):
35 | if self.f is not None:
36 | try:
37 | self.f.flush()
38 | except IOError:
39 | pass
40 | tee_f.flush()
41 |
42 |
43 | # For some unknown reason, when running under bdist_wininst we will start up
44 | # with sys.stdout as None but stderr is hooked up. This work-around allows
45 | # bdist_wininst to see the output we write and display it at the end of
46 | # the install.
47 | if sys.stdout is None:
48 | sys.stdout = sys.stderr
49 |
50 | sys.stderr = Tee(sys.stderr)
51 | sys.stdout = Tee(sys.stdout)
52 |
53 | com_modules = [
54 | # module_name, class_names
55 | ("win32com.servers.interp", "Interpreter"),
56 | ("win32com.servers.dictionary", "DictionaryPolicy"),
57 | ("win32com.axscript.client.pyscript", "PyScript"),
58 | ]
59 |
60 | # Is this a 'silent' install - ie, avoid all dialogs.
61 | # Different than 'verbose'
62 | silent = 0
63 |
64 | # Verbosity of output messages.
65 | verbose = 1
66 |
67 | root_key_name = "Software\\Python\\PythonCore\\" + sys.winver
68 |
69 | try:
70 | # When this script is run from inside the bdist_wininst installer,
71 | # file_created() and directory_created() are additional builtin
72 | # functions which write lines to Python23\pywin32-install.log. This is
73 | # a list of actions for the uninstaller, the format is inspired by what
74 | # the Wise installer also creates.
75 | file_created
76 | is_bdist_wininst = True
77 | except NameError:
78 | is_bdist_wininst = False # we know what it is not - but not what it is :)
79 |
80 | def file_created(file):
81 | pass
82 |
83 | def directory_created(directory):
84 | pass
85 |
86 | def get_root_hkey():
87 | try:
88 | winreg.OpenKey(
89 | winreg.HKEY_LOCAL_MACHINE, root_key_name, 0, winreg.KEY_CREATE_SUB_KEY
90 | )
91 | return winreg.HKEY_LOCAL_MACHINE
92 | except OSError:
93 | # Either not exist, or no permissions to create subkey means
94 | # must be HKCU
95 | return winreg.HKEY_CURRENT_USER
96 |
97 |
98 | try:
99 | create_shortcut
100 | except NameError:
101 | # Create a function with the same signature as create_shortcut provided
102 | # by bdist_wininst
103 | def create_shortcut(
104 | path, description, filename, arguments="", workdir="", iconpath="", iconindex=0
105 | ):
106 | import pythoncom
107 | from win32com.shell import shell
108 |
109 | ilink = pythoncom.CoCreateInstance(
110 | shell.CLSID_ShellLink,
111 | None,
112 | pythoncom.CLSCTX_INPROC_SERVER,
113 | shell.IID_IShellLink,
114 | )
115 | ilink.SetPath(path)
116 | ilink.SetDescription(description)
117 | if arguments:
118 | ilink.SetArguments(arguments)
119 | if workdir:
120 | ilink.SetWorkingDirectory(workdir)
121 | if iconpath or iconindex:
122 | ilink.SetIconLocation(iconpath, iconindex)
123 | # now save it.
124 | ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile)
125 | ipf.Save(filename, 0)
126 |
127 | # Support the same list of "path names" as bdist_wininst.
128 | def get_special_folder_path(path_name):
129 | from win32com.shell import shell, shellcon
130 |
131 | for maybe in """
132 | CSIDL_COMMON_STARTMENU CSIDL_STARTMENU CSIDL_COMMON_APPDATA
133 | CSIDL_LOCAL_APPDATA CSIDL_APPDATA CSIDL_COMMON_DESKTOPDIRECTORY
134 | CSIDL_DESKTOPDIRECTORY CSIDL_COMMON_STARTUP CSIDL_STARTUP
135 | CSIDL_COMMON_PROGRAMS CSIDL_PROGRAMS CSIDL_PROGRAM_FILES_COMMON
136 | CSIDL_PROGRAM_FILES CSIDL_FONTS""".split():
137 | if maybe == path_name:
138 | csidl = getattr(shellcon, maybe)
139 | return shell.SHGetSpecialFolderPath(0, csidl, False)
140 | raise ValueError("%s is an unknown path ID" % (path_name,))
141 |
142 |
143 | def CopyTo(desc, src, dest):
144 | import win32api, win32con
145 |
146 | while 1:
147 | try:
148 | win32api.CopyFile(src, dest, 0)
149 | return
150 | except win32api.error as details:
151 | if details.winerror == 5: # access denied - user not admin.
152 | raise
153 | if silent:
154 | # Running silent mode - just re-raise the error.
155 | raise
156 | full_desc = (
157 | "Error %s\n\n"
158 | "If you have any Python applications running, "
159 | "please close them now\nand select 'Retry'\n\n%s"
160 | % (desc, details.strerror)
161 | )
162 | rc = win32api.MessageBox(
163 | 0, full_desc, "Installation Error", win32con.MB_ABORTRETRYIGNORE
164 | )
165 | if rc == win32con.IDABORT:
166 | raise
167 | elif rc == win32con.IDIGNORE:
168 | return
169 | # else retry - around we go again.
170 |
171 |
172 | # We need to import win32api to determine the Windows system directory,
173 | # so we can copy our system files there - but importing win32api will
174 | # load the pywintypes.dll already in the system directory preventing us
175 | # from updating them!
176 | # So, we pull the same trick pywintypes.py does, but it loads from
177 | # our pywintypes_system32 directory.
178 | def LoadSystemModule(lib_dir, modname):
179 | # See if this is a debug build.
180 | import importlib.util, importlib.machinery
181 |
182 | suffix = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else ""
183 | filename = "%s%d%d%s.dll" % (
184 | modname,
185 | sys.version_info[0],
186 | sys.version_info[1],
187 | suffix,
188 | )
189 | filename = os.path.join(lib_dir, "pywin32_system32", filename)
190 | loader = importlib.machinery.ExtensionFileLoader(modname, filename)
191 | spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename)
192 | mod = importlib.util.module_from_spec(spec)
193 | spec.loader.exec_module(mod)
194 |
195 |
196 | def SetPyKeyVal(key_name, value_name, value):
197 | root_hkey = get_root_hkey()
198 | root_key = winreg.OpenKey(root_hkey, root_key_name)
199 | try:
200 | my_key = winreg.CreateKey(root_key, key_name)
201 | try:
202 | winreg.SetValueEx(my_key, value_name, 0, winreg.REG_SZ, value)
203 | if verbose:
204 | print("-> %s\\%s[%s]=%r" % (root_key_name, key_name, value_name, value))
205 | finally:
206 | my_key.Close()
207 | finally:
208 | root_key.Close()
209 |
210 |
211 | def UnsetPyKeyVal(key_name, value_name, delete_key=False):
212 | root_hkey = get_root_hkey()
213 | root_key = winreg.OpenKey(root_hkey, root_key_name)
214 | try:
215 | my_key = winreg.OpenKey(root_key, key_name, 0, winreg.KEY_SET_VALUE)
216 | try:
217 | winreg.DeleteValue(my_key, value_name)
218 | if verbose:
219 | print("-> DELETE %s\\%s[%s]" % (root_key_name, key_name, value_name))
220 | finally:
221 | my_key.Close()
222 | if delete_key:
223 | winreg.DeleteKey(root_key, key_name)
224 | if verbose:
225 | print("-> DELETE %s\\%s" % (root_key_name, key_name))
226 | except OSError as why:
227 | winerror = getattr(why, "winerror", why.errno)
228 | if winerror != 2: # file not found
229 | raise
230 | finally:
231 | root_key.Close()
232 |
233 |
234 | def RegisterCOMObjects(register=True):
235 | import win32com.server.register
236 |
237 | if register:
238 | func = win32com.server.register.RegisterClasses
239 | else:
240 | func = win32com.server.register.UnregisterClasses
241 | flags = {}
242 | if not verbose:
243 | flags["quiet"] = 1
244 | for module, klass_name in com_modules:
245 | __import__(module)
246 | mod = sys.modules[module]
247 | flags["finalize_register"] = getattr(mod, "DllRegisterServer", None)
248 | flags["finalize_unregister"] = getattr(mod, "DllUnregisterServer", None)
249 | klass = getattr(mod, klass_name)
250 | func(klass, **flags)
251 |
252 |
253 | def RegisterHelpFile(register=True, lib_dir=None):
254 | if lib_dir is None:
255 | lib_dir = sysconfig.get_paths()["platlib"]
256 | if register:
257 | # Register the .chm help file.
258 | chm_file = os.path.join(lib_dir, "PyWin32.chm")
259 | if os.path.isfile(chm_file):
260 | # This isn't recursive, so if 'Help' doesn't exist, we croak
261 | SetPyKeyVal("Help", None, None)
262 | SetPyKeyVal("Help\\Pythonwin Reference", None, chm_file)
263 | return chm_file
264 | else:
265 | print("NOTE: PyWin32.chm can not be located, so has not " "been registered")
266 | else:
267 | UnsetPyKeyVal("Help\\Pythonwin Reference", None, delete_key=True)
268 | return None
269 |
270 |
271 | def RegisterPythonwin(register=True, lib_dir=None):
272 | """Add (or remove) Pythonwin to context menu for python scripts.
273 | ??? Should probably also add Edit command for pys files also.
274 | Also need to remove these keys on uninstall, but there's no function
275 | like file_created to add registry entries to uninstall log ???
276 | """
277 | import os
278 |
279 | if lib_dir is None:
280 | lib_dir = sysconfig.get_paths()["platlib"]
281 | classes_root = get_root_hkey()
282 | ## Installer executable doesn't seem to pass anything to postinstall script indicating if it's a debug build,
283 | pythonwin_exe = os.path.join(lib_dir, "Pythonwin", "Pythonwin.exe")
284 | pythonwin_edit_command = pythonwin_exe + ' -edit "%1"'
285 |
286 | keys_vals = [
287 | (
288 | "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Pythonwin.exe",
289 | "",
290 | pythonwin_exe,
291 | ),
292 | (
293 | "Software\\Classes\\Python.File\\shell\\Edit with Pythonwin",
294 | "command",
295 | pythonwin_edit_command,
296 | ),
297 | (
298 | "Software\\Classes\\Python.NoConFile\\shell\\Edit with Pythonwin",
299 | "command",
300 | pythonwin_edit_command,
301 | ),
302 | ]
303 |
304 | try:
305 | if register:
306 | for key, sub_key, val in keys_vals:
307 | ## Since winreg only uses the character Api functions, this can fail if Python
308 | ## is installed to a path containing non-ascii characters
309 | hkey = winreg.CreateKey(classes_root, key)
310 | if sub_key:
311 | hkey = winreg.CreateKey(hkey, sub_key)
312 | winreg.SetValueEx(hkey, None, 0, winreg.REG_SZ, val)
313 | hkey.Close()
314 | else:
315 | for key, sub_key, val in keys_vals:
316 | try:
317 | if sub_key:
318 | hkey = winreg.OpenKey(classes_root, key)
319 | winreg.DeleteKey(hkey, sub_key)
320 | hkey.Close()
321 | winreg.DeleteKey(classes_root, key)
322 | except OSError as why:
323 | winerror = getattr(why, "winerror", why.errno)
324 | if winerror != 2: # file not found
325 | raise
326 | finally:
327 | # tell windows about the change
328 | from win32com.shell import shell, shellcon
329 |
330 | shell.SHChangeNotify(
331 | shellcon.SHCNE_ASSOCCHANGED, shellcon.SHCNF_IDLIST, None, None
332 | )
333 |
334 |
335 | def get_shortcuts_folder():
336 | if get_root_hkey() == winreg.HKEY_LOCAL_MACHINE:
337 | try:
338 | fldr = get_special_folder_path("CSIDL_COMMON_PROGRAMS")
339 | except OSError:
340 | # No CSIDL_COMMON_PROGRAMS on this platform
341 | fldr = get_special_folder_path("CSIDL_PROGRAMS")
342 | else:
343 | # non-admin install - always goes in this user's start menu.
344 | fldr = get_special_folder_path("CSIDL_PROGRAMS")
345 |
346 | try:
347 | install_group = winreg.QueryValue(
348 | get_root_hkey(), root_key_name + "\\InstallPath\\InstallGroup"
349 | )
350 | except OSError:
351 | vi = sys.version_info
352 | install_group = "Python %d.%d" % (vi[0], vi[1])
353 | return os.path.join(fldr, install_group)
354 |
355 |
356 | # Get the system directory, which may be the Wow64 directory if we are a 32bit
357 | # python on a 64bit OS.
358 | def get_system_dir():
359 | import win32api # we assume this exists.
360 |
361 | try:
362 | import pythoncom
363 | import win32process
364 | from win32com.shell import shell, shellcon
365 |
366 | try:
367 | if win32process.IsWow64Process():
368 | return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEMX86)
369 | return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEM)
370 | except (pythoncom.com_error, win32process.error):
371 | return win32api.GetSystemDirectory()
372 | except ImportError:
373 | return win32api.GetSystemDirectory()
374 |
375 |
376 | def fixup_dbi():
377 | # We used to have a dbi.pyd with our .pyd files, but now have a .py file.
378 | # If the user didn't uninstall, they will find the .pyd which will cause
379 | # problems - so handle that.
380 | import win32api, win32con
381 |
382 | pyd_name = os.path.join(os.path.dirname(win32api.__file__), "dbi.pyd")
383 | pyd_d_name = os.path.join(os.path.dirname(win32api.__file__), "dbi_d.pyd")
384 | py_name = os.path.join(os.path.dirname(win32con.__file__), "dbi.py")
385 | for this_pyd in (pyd_name, pyd_d_name):
386 | this_dest = this_pyd + ".old"
387 | if os.path.isfile(this_pyd) and os.path.isfile(py_name):
388 | try:
389 | if os.path.isfile(this_dest):
390 | print(
391 | "Old dbi '%s' already exists - deleting '%s'"
392 | % (this_dest, this_pyd)
393 | )
394 | os.remove(this_pyd)
395 | else:
396 | os.rename(this_pyd, this_dest)
397 | print("renamed '%s'->'%s.old'" % (this_pyd, this_pyd))
398 | file_created(this_pyd + ".old")
399 | except os.error as exc:
400 | print("FAILED to rename '%s': %s" % (this_pyd, exc))
401 |
402 |
403 | def install(lib_dir):
404 | import traceback
405 |
406 | # The .pth file is now installed as a regular file.
407 | # Create the .pth file in the site-packages dir, and use only relative paths
408 | # We used to write a .pth directly to sys.prefix - clobber it.
409 | if os.path.isfile(os.path.join(sys.prefix, "pywin32.pth")):
410 | os.unlink(os.path.join(sys.prefix, "pywin32.pth"))
411 | # The .pth may be new and therefore not loaded in this session.
412 | # Setup the paths just in case.
413 | for name in "win32 win32\\lib Pythonwin".split():
414 | sys.path.append(os.path.join(lib_dir, name))
415 | # It is possible people with old versions installed with still have
416 | # pywintypes and pythoncom registered. We no longer need this, and stale
417 | # entries hurt us.
418 | for name in "pythoncom pywintypes".split():
419 | keyname = "Software\\Python\\PythonCore\\" + sys.winver + "\\Modules\\" + name
420 | for root in winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER:
421 | try:
422 | winreg.DeleteKey(root, keyname + "\\Debug")
423 | except WindowsError:
424 | pass
425 | try:
426 | winreg.DeleteKey(root, keyname)
427 | except WindowsError:
428 | pass
429 | LoadSystemModule(lib_dir, "pywintypes")
430 | LoadSystemModule(lib_dir, "pythoncom")
431 | import win32api
432 |
433 | # and now we can get the system directory:
434 | files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*"))
435 | if not files:
436 | raise RuntimeError("No system files to copy!!")
437 | # Try the system32 directory first - if that fails due to "access denied",
438 | # it implies a non-admin user, and we use sys.prefix
439 | for dest_dir in [get_system_dir(), sys.prefix]:
440 | # and copy some files over there
441 | worked = 0
442 | try:
443 | for fname in files:
444 | base = os.path.basename(fname)
445 | dst = os.path.join(dest_dir, base)
446 | CopyTo("installing %s" % base, fname, dst)
447 | if verbose:
448 | print("Copied %s to %s" % (base, dst))
449 | # Register the files with the uninstaller
450 | file_created(dst)
451 | worked = 1
452 | # Nuke any other versions that may exist - having
453 | # duplicates causes major headaches.
454 | bad_dest_dirs = [
455 | os.path.join(sys.prefix, "Library\\bin"),
456 | os.path.join(sys.prefix, "Lib\\site-packages\\win32"),
457 | ]
458 | if dest_dir != sys.prefix:
459 | bad_dest_dirs.append(sys.prefix)
460 | for bad_dest_dir in bad_dest_dirs:
461 | bad_fname = os.path.join(bad_dest_dir, base)
462 | if os.path.exists(bad_fname):
463 | # let exceptions go here - delete must succeed
464 | os.unlink(bad_fname)
465 | if worked:
466 | break
467 | except win32api.error as details:
468 | if details.winerror == 5:
469 | # access denied - user not admin - try sys.prefix dir,
470 | # but first check that a version doesn't already exist
471 | # in that place - otherwise that one will still get used!
472 | if os.path.exists(dst):
473 | msg = (
474 | "The file '%s' exists, but can not be replaced "
475 | "due to insufficient permissions. You must "
476 | "reinstall this software as an Administrator" % dst
477 | )
478 | print(msg)
479 | raise RuntimeError(msg)
480 | continue
481 | raise
482 | else:
483 | raise RuntimeError(
484 | "You don't have enough permissions to install the system files"
485 | )
486 |
487 | # Pythonwin 'compiles' config files - record them for uninstall.
488 | pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin")
489 | for fname in glob.glob(os.path.join(pywin_dir, "*.cfg")):
490 | file_created(fname[:-1] + "c") # .cfg->.cfc
491 |
492 | # Register our demo COM objects.
493 | try:
494 | try:
495 | RegisterCOMObjects()
496 | except win32api.error as details:
497 | if details.winerror != 5: # ERROR_ACCESS_DENIED
498 | raise
499 | print("You do not have the permissions to install COM objects.")
500 | print("The sample COM objects were not registered.")
501 | except Exception:
502 | print("FAILED to register the Python COM objects")
503 | traceback.print_exc()
504 |
505 | # There may be no main Python key in HKCU if, eg, an admin installed
506 | # python itself.
507 | winreg.CreateKey(get_root_hkey(), root_key_name)
508 |
509 | chm_file = None
510 | try:
511 | chm_file = RegisterHelpFile(True, lib_dir)
512 | except Exception:
513 | print("Failed to register help file")
514 | traceback.print_exc()
515 | else:
516 | if verbose:
517 | print("Registered help file")
518 |
519 | # misc other fixups.
520 | fixup_dbi()
521 |
522 | # Register Pythonwin in context menu
523 | try:
524 | RegisterPythonwin(True, lib_dir)
525 | except Exception:
526 | print("Failed to register pythonwin as editor")
527 | traceback.print_exc()
528 | else:
529 | if verbose:
530 | print("Pythonwin has been registered in context menu")
531 |
532 | # Create the win32com\gen_py directory.
533 | make_dir = os.path.join(lib_dir, "win32com", "gen_py")
534 | if not os.path.isdir(make_dir):
535 | if verbose:
536 | print("Creating directory %s" % (make_dir,))
537 | directory_created(make_dir)
538 | os.mkdir(make_dir)
539 |
540 | try:
541 | # create shortcuts
542 | # CSIDL_COMMON_PROGRAMS only available works on NT/2000/XP, and
543 | # will fail there if the user has no admin rights.
544 | fldr = get_shortcuts_folder()
545 | # If the group doesn't exist, then we don't make shortcuts - its
546 | # possible that this isn't a "normal" install.
547 | if os.path.isdir(fldr):
548 | dst = os.path.join(fldr, "PythonWin.lnk")
549 | create_shortcut(
550 | os.path.join(lib_dir, "Pythonwin\\Pythonwin.exe"),
551 | "The Pythonwin IDE",
552 | dst,
553 | "",
554 | sys.prefix,
555 | )
556 | file_created(dst)
557 | if verbose:
558 | print("Shortcut for Pythonwin created")
559 | # And the docs.
560 | if chm_file:
561 | dst = os.path.join(fldr, "Python for Windows Documentation.lnk")
562 | doc = "Documentation for the PyWin32 extensions"
563 | create_shortcut(chm_file, doc, dst)
564 | file_created(dst)
565 | if verbose:
566 | print("Shortcut to documentation created")
567 | else:
568 | if verbose:
569 | print("Can't install shortcuts - %r is not a folder" % (fldr,))
570 | except Exception as details:
571 | print(details)
572 |
573 | # importing win32com.client ensures the gen_py dir created - not strictly
574 | # necessary to do now, but this makes the installation "complete"
575 | try:
576 | import win32com.client # noqa
577 | except ImportError:
578 | # Don't let this error sound fatal
579 | pass
580 | print("The pywin32 extensions were successfully installed.")
581 |
582 | if is_bdist_wininst:
583 | # Open a web page with info about the .exe installers being deprecated.
584 | import webbrowser
585 |
586 | try:
587 | webbrowser.open("https://mhammond.github.io/pywin32_installers.html")
588 | except webbrowser.Error:
589 | print("Please visit https://mhammond.github.io/pywin32_installers.html")
590 |
591 |
592 | def uninstall(lib_dir):
593 | # First ensure our system modules are loaded from pywin32_system, so
594 | # we can remove the ones we copied...
595 | LoadSystemModule(lib_dir, "pywintypes")
596 | LoadSystemModule(lib_dir, "pythoncom")
597 |
598 | try:
599 | RegisterCOMObjects(False)
600 | except Exception as why:
601 | print("Failed to unregister COM objects: %s" % (why,))
602 |
603 | try:
604 | RegisterHelpFile(False, lib_dir)
605 | except Exception as why:
606 | print("Failed to unregister help file: %s" % (why,))
607 | else:
608 | if verbose:
609 | print("Unregistered help file")
610 |
611 | try:
612 | RegisterPythonwin(False, lib_dir)
613 | except Exception as why:
614 | print("Failed to unregister Pythonwin: %s" % (why,))
615 | else:
616 | if verbose:
617 | print("Unregistered Pythonwin")
618 |
619 | try:
620 | # remove gen_py directory.
621 | gen_dir = os.path.join(lib_dir, "win32com", "gen_py")
622 | if os.path.isdir(gen_dir):
623 | shutil.rmtree(gen_dir)
624 | if verbose:
625 | print("Removed directory %s" % (gen_dir,))
626 |
627 | # Remove pythonwin compiled "config" files.
628 | pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin")
629 | for fname in glob.glob(os.path.join(pywin_dir, "*.cfc")):
630 | os.remove(fname)
631 |
632 | # The dbi.pyd.old files we may have created.
633 | try:
634 | os.remove(os.path.join(lib_dir, "win32", "dbi.pyd.old"))
635 | except os.error:
636 | pass
637 | try:
638 | os.remove(os.path.join(lib_dir, "win32", "dbi_d.pyd.old"))
639 | except os.error:
640 | pass
641 |
642 | except Exception as why:
643 | print("Failed to remove misc files: %s" % (why,))
644 |
645 | try:
646 | fldr = get_shortcuts_folder()
647 | for link in ("PythonWin.lnk", "Python for Windows Documentation.lnk"):
648 | fqlink = os.path.join(fldr, link)
649 | if os.path.isfile(fqlink):
650 | os.remove(fqlink)
651 | if verbose:
652 | print("Removed %s" % (link,))
653 | except Exception as why:
654 | print("Failed to remove shortcuts: %s" % (why,))
655 | # Now remove the system32 files.
656 | files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*"))
657 | # Try the system32 directory first - if that fails due to "access denied",
658 | # it implies a non-admin user, and we use sys.prefix
659 | try:
660 | for dest_dir in [get_system_dir(), sys.prefix]:
661 | # and copy some files over there
662 | worked = 0
663 | for fname in files:
664 | base = os.path.basename(fname)
665 | dst = os.path.join(dest_dir, base)
666 | if os.path.isfile(dst):
667 | try:
668 | os.remove(dst)
669 | worked = 1
670 | if verbose:
671 | print("Removed file %s" % (dst))
672 | except Exception:
673 | print("FAILED to remove %s" % (dst,))
674 | if worked:
675 | break
676 | except Exception as why:
677 | print("FAILED to remove system files: %s" % (why,))
678 |
679 |
680 | # NOTE: If this script is run from inside the bdist_wininst created
681 | # binary installer or uninstaller, the command line args are either
682 | # '-install' or '-remove'.
683 |
684 | # Important: From inside the binary installer this script MUST NOT
685 | # call sys.exit() or raise SystemExit, otherwise not only this script
686 | # but also the installer will terminate! (Is there a way to prevent
687 | # this from the bdist_wininst C code?)
688 |
689 |
690 | def verify_destination(location):
691 | if not os.path.isdir(location):
692 | raise argparse.ArgumentTypeError('Path "{}" does not exist!'.format(location))
693 | return location
694 |
695 |
696 | def main():
697 | import argparse
698 |
699 | parser = argparse.ArgumentParser(
700 | formatter_class=argparse.RawDescriptionHelpFormatter,
701 | description="""A post-install script for the pywin32 extensions.
702 |
703 | * Typical usage:
704 |
705 | > python pywin32_postinstall.py -install
706 |
707 | If you installed pywin32 via a .exe installer, this should be run
708 | automatically after installation, but if it fails you can run it again.
709 |
710 | If you installed pywin32 via PIP, you almost certainly need to run this to
711 | setup the environment correctly.
712 |
713 | Execute with script with a '-install' parameter, to ensure the environment
714 | is setup correctly.
715 | """,
716 | )
717 | parser.add_argument(
718 | "-install",
719 | default=False,
720 | action="store_true",
721 | help="Configure the Python environment correctly for pywin32.",
722 | )
723 | parser.add_argument(
724 | "-remove",
725 | default=False,
726 | action="store_true",
727 | help="Try and remove everything that was installed or copied.",
728 | )
729 | parser.add_argument(
730 | "-wait",
731 | type=int,
732 | help="Wait for the specified process to terminate before starting.",
733 | )
734 | parser.add_argument(
735 | "-silent",
736 | default=False,
737 | action="store_true",
738 | help='Don\'t display the "Abort/Retry/Ignore" dialog for files in use.',
739 | )
740 | parser.add_argument(
741 | "-quiet",
742 | default=False,
743 | action="store_true",
744 | help="Don't display progress messages.",
745 | )
746 | parser.add_argument(
747 | "-destination",
748 | default=sysconfig.get_paths()["platlib"],
749 | type=verify_destination,
750 | help="Location of the PyWin32 installation",
751 | )
752 |
753 | args = parser.parse_args()
754 |
755 | if not args.quiet:
756 | print("Parsed arguments are: {}".format(args))
757 |
758 | if not args.install ^ args.remove:
759 | parser.error("You need to either choose to -install or -remove!")
760 |
761 | if args.wait is not None:
762 | try:
763 | os.waitpid(args.wait, 0)
764 | except os.error:
765 | # child already dead
766 | pass
767 |
768 | silent = args.silent
769 | verbose = not args.quiet
770 |
771 | if args.install:
772 | install(args.destination)
773 |
774 | if args.remove:
775 | if not is_bdist_wininst:
776 | uninstall(args.destination)
777 |
778 |
779 | if __name__ == "__main__":
780 | main()
781 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/pywin32_testall.py:
--------------------------------------------------------------------------------
1 | """A test runner for pywin32"""
2 | import sys
3 | import os
4 | import site
5 | import subprocess
6 |
7 | # locate the dirs based on where this script is - it may be either in the
8 | # source tree, or in an installed Python 'Scripts' tree.
9 | this_dir = os.path.dirname(__file__)
10 | site_packages = [
11 | site.getusersitepackages(),
12 | ] + site.getsitepackages()
13 |
14 | failures = []
15 |
16 | # Run a test using subprocess and wait for the result.
17 | # If we get an returncode != 0, we know that there was an error, but we don't
18 | # abort immediately - we run as many tests as we can.
19 | def run_test(script, cmdline_extras):
20 | dirname, scriptname = os.path.split(script)
21 | # some tests prefer to be run from their directory.
22 | cmd = [sys.executable, "-u", scriptname] + cmdline_extras
23 | result = subprocess.run(cmd, check=False, cwd=dirname)
24 | print("*** Test script '%s' exited with %s" % (script, result.returncode))
25 | sys.stdout.flush()
26 | if result.returncode:
27 | failures.append(script)
28 |
29 |
30 | def find_and_run(possible_locations, extras):
31 | for maybe in possible_locations:
32 | if os.path.isfile(maybe):
33 | run_test(maybe, extras)
34 | break
35 | else:
36 | raise RuntimeError(
37 | "Failed to locate a test script in one of %s" % possible_locations
38 | )
39 |
40 |
41 | def main():
42 | import argparse
43 |
44 | code_directories = [this_dir] + site_packages
45 |
46 | parser = argparse.ArgumentParser(
47 | description="A script to trigger tests in all subprojects of PyWin32."
48 | )
49 | parser.add_argument(
50 | "-no-user-interaction",
51 | default=False,
52 | action="store_true",
53 | help="(This is now the default - use `-user-interaction` to include them)",
54 | )
55 |
56 | parser.add_argument(
57 | "-user-interaction",
58 | action="store_true",
59 | help="Include tests which require user interaction",
60 | )
61 |
62 | parser.add_argument(
63 | "-skip-adodbapi",
64 | default=False,
65 | action="store_true",
66 | help="Skip the adodbapi tests; useful for CI where there's no provider",
67 | )
68 |
69 | args, remains = parser.parse_known_args()
70 |
71 | # win32
72 | maybes = [
73 | os.path.join(directory, "win32", "test", "testall.py")
74 | for directory in code_directories
75 | ]
76 | extras = []
77 | if args.user_interaction:
78 | extras += "-user-interaction"
79 | extras.extend(remains)
80 |
81 | find_and_run(maybes, extras)
82 |
83 | # win32com
84 | maybes = [
85 | os.path.join(directory, "win32com", "test", "testall.py")
86 | for directory in [
87 | os.path.join(this_dir, "com"),
88 | ]
89 | + site_packages
90 | ]
91 | extras = remains + ["1"] # only run "level 1" tests in CI
92 | find_and_run(maybes, extras)
93 |
94 | # adodbapi
95 | if not args.skip_adodbapi:
96 | maybes = [
97 | os.path.join(directory, "adodbapi", "test", "adodbapitest.py")
98 | for directory in code_directories
99 | ]
100 | find_and_run(maybes, remains)
101 | # This script has a hard-coded sql server name in it, (and markh typically
102 | # doesn't have a different server to test on) but there is now supposed to be a server out there on the Internet
103 | # just to run these tests, so try it...
104 | maybes = [
105 | os.path.join(directory, "adodbapi", "test", "test_adodbapi_dbapi20.py")
106 | for directory in code_directories
107 | ]
108 | find_and_run(maybes, remains)
109 |
110 | if failures:
111 | print("The following scripts failed")
112 | for failure in failures:
113 | print(">", failure)
114 | sys.exit(1)
115 | print("All tests passed \o/")
116 |
117 |
118 | if __name__ == "__main__":
119 | main()
120 |
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/tqdm.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/tqdm.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/uiautomator2.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/uiautomator2.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/wheel-3.9.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/wheel-3.9.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/wheel.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/wheel.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/wheel3.9.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/wheel3.9.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/Scripts/wheel3.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/VisionDroid Code/venv/Scripts/wheel3.exe
--------------------------------------------------------------------------------
/VisionDroid Code/venv/pyvenv.cfg:
--------------------------------------------------------------------------------
1 | home = C:\Users\IcyFeather\AppData\Local\Programs\Python\Python39
2 | implementation = CPython
3 | version_info = 3.9.13.final.0
4 | virtualenv = 20.13.0
5 | include-system-site-packages = false
6 | base-prefix = C:\Users\IcyFeather\AppData\Local\Programs\Python\Python39
7 | base-exec-prefix = C:\Users\IcyFeather\AppData\Local\Programs\Python\Python39
8 | base-executable = C:\Users\IcyFeather\AppData\Local\Programs\Python\Python39\python.exe
9 |
--------------------------------------------------------------------------------
/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testtestA6/VisionDroid/3cc7dc097306ce4c8495fdcd09b04e04c3a7c98a/workflow.png
--------------------------------------------------------------------------------